Merge branch 'master' into gridView

pull/4817/head
Liana Husikyan 5 months ago committed by GitHub
commit 4e728c5409
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 32
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 20
      .github/ISSUE_TEMPLATE/feature_request.md
  3. 2
      apps/remix-ide-e2e/src/commands/addFileSnekmate.ts
  4. 121
      apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
  5. 4
      apps/remix-ide-e2e/src/tests/file_explorer_context_menu.test.ts
  6. 3
      apps/remix-ide-e2e/src/tests/file_explorer_dragdrop.test.ts
  7. 49
      apps/remix-ide-e2e/src/tests/homeTab.test.ts
  8. 13
      apps/remix-ide-e2e/src/tests/url.test.ts
  9. 90
      apps/remix-ide-e2e/src/tests/vyper_api.test.ts
  10. 2
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  11. 107
      apps/remix-ide-e2e/src/tests/workspace_git.test.ts
  12. 4
      apps/remix-ide/contracts/ballot.sol
  13. 9
      apps/remix-ide/src/app.js
  14. 3
      apps/remix-ide/src/app/components/preload.tsx
  15. 2
      apps/remix-ide/src/app/components/side-panel.tsx
  16. 106
      apps/remix-ide/src/app/components/status-bar.tsx
  17. 7
      apps/remix-ide/src/app/panels/file-panel.js
  18. 11
      apps/remix-ide/src/app/panels/tab-proxy.js
  19. 1
      apps/remix-ide/src/app/plugins/compile-details.tsx
  20. 11
      apps/remix-ide/src/app/tabs/locales/en/home.json
  21. 2
      apps/remix-ide/src/app/tabs/locales/en/udapp.json
  22. 1
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  23. 4
      apps/remix-ide/src/remixAppManager.js
  24. 19
      apps/remix-ide/src/types/index.d.ts
  25. 32
      apps/vyper/src/app/app.tsx
  26. 23
      apps/vyper/src/app/utils/compiler.tsx
  27. 25
      apps/vyper/src/app/utils/remix-client.tsx
  28. 13
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  29. 5
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  30. 8
      libs/remix-ui/app/src/lib/remix-app/style/remix-app.css
  31. 21
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  32. 27
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  33. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx
  34. 173
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  35. 182
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  36. 156
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  37. 6
      libs/remix-ui/home-tab/src/lib/components/homeTablangOptions.tsx
  38. 35
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css
  39. 31
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  40. 2
      libs/remix-ui/panel/src/index.ts
  41. 4
      libs/remix-ui/panel/src/lib/plugins/panel.css
  42. 20
      libs/remix-ui/panel/src/lib/types/index.ts
  43. 11
      libs/remix-ui/run-tab/src/lib/components/solScanTable.tsx
  44. 8
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  45. 23
      libs/remix-ui/solidity-compile-details/src/lib/components/solidityCompile.tsx
  46. 32
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  47. 23
      libs/remix-ui/statusbar/src/css/statusbar.css
  48. 2
      libs/remix-ui/statusbar/src/index.ts
  49. 44
      libs/remix-ui/statusbar/src/lib/components/aiStatus.tsx
  50. 88
      libs/remix-ui/statusbar/src/lib/components/gitStatus.tsx
  51. 27
      libs/remix-ui/statusbar/src/lib/components/scamAlertStatus.tsx
  52. 48
      libs/remix-ui/statusbar/src/lib/components/scamDetails.tsx
  53. 86
      libs/remix-ui/statusbar/src/lib/remixui-statusbar-panel.tsx
  54. 27
      libs/remix-ui/statusbar/src/lib/types/index.ts
  55. 14
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  56. 2
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  57. 2
      libs/remix-ui/toaster/src/lib/toaster.tsx
  58. 7
      libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.css
  59. 18
      libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx
  60. 8
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css
  61. 2
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx
  62. 2
      libs/remix-ui/workspace/src/index.ts
  63. 1
      package.json
  64. 3
      tsconfig.paths.json
  65. 41
      yarn.lock

@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows, Linux or MacOS]
- Browser [e.g. chrome, firefox]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -60,7 +60,7 @@ function addFileSnekmate(browser: NightwatchBrowser, name: string, content: Nigh
}) })
.setEditorValue(content.content) .setEditorValue(content.content)
.getEditorValue((result) => { .getEditorValue((result) => {
if(result != content.content) { if (result != content.content) {
browser.setEditorValue(content.content) browser.setEditorValue(content.content)
} }
}) })

@ -117,69 +117,68 @@ module.exports = {
'Should add deep tree with buttons #group3': function (browser: NightwatchBrowser) { 'Should add deep tree with buttons #group3': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('div[data-id="remixIdeSidePanel"]') .waitForElementVisible('div[data-id="remixIdeSidePanel"]')
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="filePanelFileExplorerTree"]') .waitForElementVisible('*[data-id="filePanelFileExplorerTree"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]')
.click('[data-id="fileExplorerNewFilecreateNewFolder"]') .click('[data-id="fileExplorerNewFilecreateNewFolder"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep1') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep1')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]')
.click('[data-id="fileExplorerNewFilecreateNewFolder"]') .click('[data-id="fileExplorerNewFilecreateNewFolder"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep2') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep2')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1/deep2"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1/deep2"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]')
.click('[data-id="fileExplorerNewFilecreateNewFolder"]') .click('[data-id="fileExplorerNewFilecreateNewFolder"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep3') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep3')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1/deep2/deep3"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1/deep2/deep3"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFile"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFile"]')
.click('[data-id="fileExplorerNewFilecreateNewFile"]') .click('[data-id="fileExplorerNewFilecreateNewFile"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep4.sol') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep4.sol')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1/deep2/deep3/deep4.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep1/deep2/deep3/deep4.sol"]')
// click on root to focus // click on root to focus
.click('li[data-id="treeViewLitreeViewItemREADME.txt"]') .click('li[data-id="treeViewLitreeViewItemREADME.txt"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]')
.click('[data-id="fileExplorerNewFilecreateNewFolder"]') .click('[data-id="fileExplorerNewFilecreateNewFolder"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep5') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep5')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep5"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep5"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]')
.click('[data-id="fileExplorerNewFilecreateNewFolder"]') .click('[data-id="fileExplorerNewFilecreateNewFolder"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep6') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep6')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep5/deep6"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemdeep5/deep6"]')
// focus on contracts // focus on contracts
.click('li[data-id="treeViewLitreeViewItemcontracts"]') .click('li[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]')
.click('[data-id="fileExplorerNewFilecreateNewFolder"]') .click('[data-id="fileExplorerNewFilecreateNewFolder"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep7') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep7')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/deep7"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/deep7"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFolder"]')
.click('[data-id="fileExplorerNewFilecreateNewFolder"]') .click('[data-id="fileExplorerNewFilecreateNewFolder"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep8') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep8')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/deep7/deep8"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/deep7/deep8"]')
.waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFile"]') .waitForElementVisible('[data-id="fileExplorerNewFilecreateNewFile"]')
.click('[data-id="fileExplorerNewFilecreateNewFile"]') .click('[data-id="fileExplorerNewFilecreateNewFile"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]') .waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep9.sol') .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'deep9.sol')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/deep7/deep8/deep9.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/deep7/deep8/deep9.sol"]')
.end() .end()
} }
} }

@ -90,7 +90,7 @@ module.exports = {
.rightClick('li[data-id="treeViewLitreeViewItemREADME.txt"]') .rightClick('li[data-id="treeViewLitreeViewItemREADME.txt"]')
.waitForElementPresent('[data-id="contextMenuItemcopy') .waitForElementPresent('[data-id="contextMenuItemcopy')
.click('[data-id="contextMenuItemcopy"]') .click('[data-id="contextMenuItemcopy"]')
.rightClick('*[data-id="treeViewLiMenu"]') .rightClick('*[data-id="treeViewUltreeViewMenu"]')
.saveScreenshot('./reports/screenshot/file_explorer_context_menu.png') .saveScreenshot('./reports/screenshot/file_explorer_context_menu.png')
.click('*[data-id="contextMenuItempaste"]') .click('*[data-id="contextMenuItempaste"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_README.txt"]', 7000) .waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_README.txt"]', 7000)
@ -111,7 +111,7 @@ module.exports = {
.rightClick('li[data-id="treeViewLitreeViewItemcontracts"]') .rightClick('li[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementPresent('[data-id="contextMenuItemcopy') .waitForElementPresent('[data-id="contextMenuItemcopy')
.click('[data-id="contextMenuItemcopy"]') .click('[data-id="contextMenuItemcopy"]')
.rightClick('*[data-id="treeViewLiMenu"]') .rightClick('*[data-id="treeViewUltreeViewMenu"]')
.click('*[data-id="contextMenuItempaste"]') .click('*[data-id="contextMenuItempaste"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_contracts"]', 7000) .waitForElementVisible('*[data-id="treeViewLitreeViewItemCopy_contracts"]', 7000)
}, },

@ -6,7 +6,6 @@ const checkBrowserIsChrome = function (browser: NightwatchBrowser) {
return browser.browserName.indexOf('chrome') > -1 return browser.browserName.indexOf('chrome') > -1
} }
module.exports = { module.exports = {
'@disabled': true, '@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
@ -98,6 +97,4 @@ module.exports = {
} }
} }
} }

@ -3,15 +3,52 @@ import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
module.exports = { module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done) init(browser, done)
}, },
'Should start coding': function (browser: NightwatchBrowser) { 'Should start coding #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="homeTabGetStartedremixDefault"]')
.click('*[data-id="homeTabGetStartedremixDefault"]')
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemcontracts/1_Storage.sol"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/2_Owner.sol"')
.waitForElementVisible('*[data-id="treeViewDivDraggableItemREADME.txt"')
.click('*[data-id="treeViewDivtreeViewItemcontracts/1_Storage.sol"]')
.waitForElementPresent({
selector: "//div[contains(@class, 'view-line') and contains(.//span, 'pragma')]",
locateStrategy: 'xpath'
})
.getEditorValue((editorContent) => {
browser.assert.ok(editorContent.indexOf(`pragma solidity`) !== -1, 'unexpected content encountered!')
})
},
'Should start with ERC20 workspace #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-path="home"')
.waitForElementVisible('*[data-id="homeTabGetStartedERC20"]')
.click('*[data-id="homeTabGetStartedERC20"')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]')
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemtests/MyToken_test.sol"]')
.click('*[data-id="treeViewDivtreeViewItemtests/MyToken_test.sol"]')
.waitForElementPresent({
selector: "//div[contains(@class, 'view-line') and contains(.//span, 'pragma')]",
locateStrategy: 'xpath'
})
.getEditorValue((editorContent) => {
browser.assert.ok(editorContent.indexOf(`import "../contracts/MyToken.sol";`) !== -1, 'content encountered!')
})
},
'Should create a new file in the current workspace': '' +function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="homeTabStartCoding"]') .click('*[data-path="home"]')
.click('*[data-id="homeTabStartCoding"]') .waitForElementVisible('*[data-id="homeTabNewFile"]')
.waitForElementVisible('div[data-id="treeViewDivtreeViewItemcontracts/HelloWorld.sol"]') .click('*[data-id="homeTabNewFile"]')
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'HometabNewFile.txt')
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemHometabNewFile.txt"]', 7000)
} }
} }

@ -11,31 +11,31 @@ const sources = [
content: ` content: `
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable { contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor /// @custom:oz-upgrades-unsafe-allow constructor
constructor() { constructor() {
_disableInitializers(); _disableInitializers();
} }
function initialize(address initialOwner) initializer public { function initialize(address initialOwner) initializer public {
__ERC721_init("MyToken", "MTK"); __ERC721_init("MyToken", "MTK");
__Ownable_init(initialOwner); __Ownable_init(initialOwner);
__UUPSUpgradeable_init(); __UUPSUpgradeable_init();
} }
function _authorizeUpgrade(address newImplementation) function _authorizeUpgrade(address newImplementation)
internal internal
onlyOwner onlyOwner
override override
{} {}
} }
` `
} }
} }
@ -117,7 +117,7 @@ module.exports = {
}) })
}, },
'Should load Blockscout verified contracts from URL "address" and "blockscout" params (single source)': function (browser: NightwatchBrowser) { 'Should load Blockscout verified contracts from URL "address" and "blockscout" params (single source)': ''+function (browser: NightwatchBrowser) {
browser browser
.url('http://127.0.0.1:8080/#address=0xdAC17F958D2ee523a2206206994597C13D831ec7&blockscout=eth.blockscout.com') .url('http://127.0.0.1:8080/#address=0xdAC17F958D2ee523a2206206994597C13D831ec7&blockscout=eth.blockscout.com')
.refreshPage() .refreshPage()
@ -175,6 +175,7 @@ module.exports = {
'code has been loaded') 'code has been loaded')
}) })
.url('http://127.0.0.1:8080') // refresh without loading the code sample .url('http://127.0.0.1:8080') // refresh without loading the code sample
.pause(2000)
.currentWorkspaceIs('default_workspace') .currentWorkspaceIs('default_workspace')
.execute(() => { .execute(() => {
return document.querySelector('[data-id="dropdown-item-code-sample"]') === null return document.querySelector('[data-id="dropdown-item-code-sample"]') === null

@ -27,30 +27,30 @@ module.exports = {
.frameParent() .frameParent()
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.waitForElementVisible({ .waitForElementVisible({
selector: "//*[@data-id='workspacesSelect' and contains(.,'snekmate')]", selector: "//*[@data-id='workspacesSelect' and contains(.,'vyper-lang')]",
locateStrategy: 'xpath', locateStrategy: 'xpath',
timeout: 60000 timeout: 60000
}) })
.currentWorkspaceIs('snekmate') .currentWorkspaceIs('vyper-lang')
.waitForElementVisible({ // .waitForElementVisible({
selector: "//*[@data-id='treeViewLitreeViewItemsrc' and contains(.,'src')]", // selector: "//*[@data-id='treeViewLitreeViewItemsrc' and contains(.,'src')]",
locateStrategy: 'xpath', // locateStrategy: 'xpath',
timeout: 60000 // timeout: 60000
}) // })
.openFile('src') .openFile('examples')
.openFile('src/snekmate') .openFile('examples/auctions')
.openFile('src/snekmate/tokens') .openFile('examples/auctions/simple_open_auction.vy')
.openFile('src/snekmate/tokens/ERC721.vy')
}, },
// 'Add vyper file to run tests #group1': function (browser: NightwatchBrowser) {
// browser.addFile('TestBallot.sol', sources[0]['TestBallot.sol']) // '@sources': () => sources,
// }, // 'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) {
'@sources': () => sources, // browser
// .addFileSnekmate('blind_auction.vy', sources[0]['blindAuction'])
'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) { 'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) {
browser browser
.addFileSnekmate('blind_auction.vy', sources[0]['blindAuction']) .click('*[data-id="treeViewDivtreeViewItemexamples/auctions/blind_auction.vy"]')
.click('*[data-id="treeViewLitreeViewItemblind_auction.vy"]') .rightClick('*[data-id="treeViewDivtreeViewItemexamples/auctions/blind_auction.vy"]')
.rightClick('*[data-id="treeViewLitreeViewItemblind_auction.vy"]')
.waitForElementPresent('[data-id="contextMenuItemvyper"]') .waitForElementPresent('[data-id="contextMenuItemvyper"]')
.click('[data-id="contextMenuItemvyper"]') .click('[data-id="contextMenuItemvyper"]')
.clickLaunchIcon('vyper') .clickLaunchIcon('vyper')
@ -149,32 +149,32 @@ module.exports = {
}) })
}, },
'Compile Ownable contract from snekmate #group1': function (browser: NightwatchBrowser) { // 'Compile Ownable contract from snekmate #group1': function (browser: NightwatchBrowser) {
let contractAddress // let contractAddress
browser // browser
.frameParent() // .frameParent()
.clickLaunchIcon('filePanel') // .clickLaunchIcon('filePanel')
.switchWorkspace('snekmate') // .switchWorkspace('snekmate')
.openFile('src') // .openFile('src')
.openFile('src/snekmate') // .openFile('src/snekmate')
.openFile('src/snekmate/auth') // .openFile('src/snekmate/auth')
.openFile('src/snekmate/auth/Ownable.vy') // .openFile('src/snekmate/auth/Ownable.vy')
.rightClick('*[data-id="treeViewLitreeViewItemsrc/snekmate/auth/Ownable.vy"]') // .rightClick('*[data-id="treeViewLitreeViewItemsrc/snekmate/auth/Ownable.vy"]')
.waitForElementVisible('*[data-id="contextMenuItemvyper"]') // .waitForElementVisible('*[data-id="contextMenuItemvyper"]')
.click('*[data-id="contextMenuItemvyper"]') // .click('*[data-id="contextMenuItemvyper"]')
.clickLaunchIcon('vyper') // .clickLaunchIcon('vyper')
// @ts-ignore // // @ts-ignore
.frame(0) // .frame(0)
.click('[data-id="compile"]') // .click('[data-id="compile"]')
.waitForElementVisible({ // .waitForElementVisible({
selector:'[data-id="compilation-details"]', // selector:'[data-id="compilation-details"]',
timeout: 60000 // timeout: 60000
}) // })
.click('[data-id="compilation-details"]') // .click('[data-id="compilation-details"]')
.frameParent() // .frameParent()
.waitForElementVisible('[data-id="copy-abi"]') // .waitForElementVisible('[data-id="copy-abi"]')
.end() // .end()
} // }
} }
const testContract = ` const testContract = `
@ -388,6 +388,6 @@ def auctionEnd():
# Transfer funds to beneficiary # Transfer funds to beneficiary
send(self.beneficiary, self.highestBid) send(self.beneficiary, self.highestBid)
`} ` }
} }
] ]

@ -577,7 +577,7 @@ module.exports = {
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`contract Create2FactoryAssembly {`) !== -1, browser.assert.ok(content.indexOf(`contract Create2FactoryAssembly {`) !== -1,
'current displayed content is not Create2FactoryAssembly') 'current displayed content is not Create2FactoryAssembly')
}) })
}, },
tearDown: sauce tearDown: sauce

@ -159,11 +159,13 @@ module.exports = {
.setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ioedeveloper/test-branch-change') .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ioedeveloper/test-branch-change')
.click('[data-id="fileSystem-modal-footer-ok-react"]') .click('[data-id="fileSystem-modal-footer-ok-react"]')
.waitForElementPresent('.fa-spinner') .waitForElementPresent('.fa-spinner')
.pause(5000) .pause(7000)
.waitForElementNotPresent('.fa-spinner') .waitForElementNotPresent('.fa-spinner')
.waitForElementContainsText('[data-id="workspacesSelect"]', 'test-branch-change') .waitForElementContainsText('[data-id="workspacesSelect"]', 'test-branch-change')
.waitForElementVisible('[data-id="workspaceGitPanel"]') .waitForElementVisible('[data-id="workspaceGitPanel"]')
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]') .click('[data-id="workspaceGitBranchesDropdown"]')
.pause()
.waitForElementVisible('[data-id="custom-dropdown-menu"]') .waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/production') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/production')
@ -311,66 +313,67 @@ module.exports = {
}, },
'When switching branches the submodules should disappear #group4': function (browser: NightwatchBrowser) { 'When switching branches the submodules should disappear #group4': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]') .waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]') .pause()
.waitForElementVisible('[data-id="custom-dropdown-menu"]') .click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/empty') .waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementPresent('[data-id="workspaceGit-origin/empty"]') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/empty')
.click('[data-id="workspaceGit-origin/empty"]') .waitForElementPresent('[data-id="workspaceGit-origin/empty"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemlibdeep"]') .click('[data-id="workspaceGit-origin/empty"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]') .waitForElementNotPresent('[data-id="treeViewDivtreeViewItemlibdeep"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]') .waitForElementNotPresent('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]')
}, },
'When switching to main update the modules #group4': function (browser: NightwatchBrowser) { 'When switching to main update the modules #group4': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]') .waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]') .click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementVisible('[data-id="custom-dropdown-menu"]') .waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/main') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/main')
.waitForElementPresent('[data-id="workspaceGit-origin/main"]') .waitForElementPresent('[data-id="workspaceGit-origin/main"]')
.click('[data-id="workspaceGit-origin/main"]') .click('[data-id="workspaceGit-origin/main"]')
.waitForElementVisible('[data-id="updatesubmodules"]') .waitForElementVisible('[data-id="updatesubmodules"]')
.click('[data-id="updatesubmodules"]') .click('[data-id="updatesubmodules"]')
.waitForElementPresent('.fa-spinner') .waitForElementPresent('.fa-spinner')
.waitForElementVisible({ .waitForElementVisible({
selector: '*[data-id="treeViewLitreeViewItem.git"]', selector: '*[data-id="treeViewLitreeViewItem.git"]',
timeout: 240000 timeout: 240000
}) })
.pause(2000) .pause(2000)
// check recursive submodule // check recursive submodule
.waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]') .click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]') .click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2"]') .click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2/submodule2.ts"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2/submodule2.ts"]')
// check test-branch-submodule-2 submodule // check test-branch-submodule-2 submodule
.waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]') .click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]') .click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2/submodule2.ts"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2/submodule2.ts"]')
// check libdeep submodule // check libdeep submodule
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep"]') .click('[data-id="treeViewDivtreeViewItemlibdeep"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep"]') .click('[data-id="treeViewDivtreeViewItemlibdeep"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep/test-branch-submodule-2"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep/test-branch-submodule-2"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep/test-branch-submodule-2"]') .click('[data-id="treeViewDivtreeViewItemlibdeep/test-branch-submodule-2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep/test-branch-submodule-2/submodule2.ts"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep/test-branch-submodule-2/submodule2.ts"]')
// check libdeep2 submodule // check libdeep2 submodule
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep2"]') .click('[data-id="treeViewDivtreeViewItemlibdeep2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]') .click('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive/test-branch-submodule-2"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive/test-branch-submodule-2"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep2/recursive/test-branch-submodule-2"]') .click('[data-id="treeViewDivtreeViewItemlibdeep2/recursive/test-branch-submodule-2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive/test-branch-submodule-2/submodule2.ts"]') .waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive/test-branch-submodule-2/submodule2.ts"]')
}, },
// GIT SUBMODULES E2E ENDS // GIT SUBMODULES E2E ENDS
// GIT WORKSPACE E2E STARTS // GIT WORKSPACE E2E STARTS
'Should create a git workspace (uniswapV4Template) #group4': function (browser: NightwatchBrowser) { 'Should create a git workspace (uniswapV4Template) #group4': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspacesMenuDropdown"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]') .click('*[data-id="workspacecreate"]')
@ -389,15 +392,13 @@ module.exports = {
browser.assert.ok(content.indexOf(`contract Counter is BaseHook {`) !== -1, browser.assert.ok(content.indexOf(`contract Counter is BaseHook {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
}, },
// GIT WORKSPACE E2E ENDS // GIT WORKSPACE E2E ENDS
tearDown: sauce, tearDown: sauce,
} }
const gitmodules = `[submodule "subdemo3"] const gitmodules = `[submodule "subdemo3"]
path = subdemo3 path = subdemo3
url = https://github.com/bunsenstraat/empty3 url = https://github.com/bunsenstraat/empty3

@ -60,7 +60,7 @@ contract Ballot {
!voters[voter].voted, !voters[voter].voted,
"The voter already voted." "The voter already voted."
); );
require(voters[voter].weight == 0); require(voters[voter].weight == 0, "Voter already has the right to vote.");
voters[voter].weight = 1; voters[voter].weight = 1;
} }
@ -135,4 +135,4 @@ contract Ballot {
{ {
winnerName_ = proposals[winningProposal()].name; winnerName_ = proposals[winningProposal()].name;
} }
} }

@ -9,6 +9,7 @@ import {Web3ProviderModule} from './app/tabs/web3-provider'
import {CompileAndRun} from './app/tabs/compile-and-run' import {CompileAndRun} from './app/tabs/compile-and-run'
import {PluginStateLogger} from './app/tabs/state-logger' import {PluginStateLogger} from './app/tabs/state-logger'
import {SidePanel} from './app/components/side-panel' import {SidePanel} from './app/components/side-panel'
import {StatusBar} from './app/components/status-bar'
import {HiddenPanel} from './app/components/hidden-panel' import {HiddenPanel} from './app/components/hidden-panel'
import {PinnedPanel} from './app/components/pinned-panel' import {PinnedPanel} from './app/components/pinned-panel'
import {VerticalIcons} from './app/components/vertical-icons' import {VerticalIcons} from './app/components/vertical-icons'
@ -301,7 +302,7 @@ class AppComponent {
const permissionHandler = new PermissionHandlerPlugin() const permissionHandler = new PermissionHandlerPlugin()
// ----------------- run script after each compilation results ----------- // ----------------- run script after each compilation results -----------
const pluginStateLogger = new PluginStateLogger() const pluginStateLogger = new PluginStateLogger()
this.engine.register([ this.engine.register([
permissionHandler, permissionHandler,
this.layout, this.layout,
@ -339,7 +340,7 @@ class AppComponent {
hardhatProvider, hardhatProvider,
ganacheProvider, ganacheProvider,
foundryProvider, foundryProvider,
externalHttpProvider, externalHttpProvider,
this.walkthroughService, this.walkthroughService,
search, search,
solidityumlgen, solidityumlgen,
@ -386,10 +387,11 @@ class AppComponent {
const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine) const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine)
const filePanel = new FilePanel(appManager) const filePanel = new FilePanel(appManager)
this.statusBar = new StatusBar(filePanel, this.menuicons)
const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport) const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport)
this.settings = new SettingsTab(Registry.getInstance().get('config').api, editor, appManager) this.settings = new SettingsTab(Registry.getInstance().get('config').api, editor, appManager)
this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel]) this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, this.statusBar, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel])
// CONTENT VIEWS & DEFAULT PLUGINS // CONTENT VIEWS & DEFAULT PLUGINS
const openZeppelinProxy = new OpenZeppelinProxy(blockchain) const openZeppelinProxy = new OpenZeppelinProxy(blockchain)
@ -471,6 +473,7 @@ class AppComponent {
'pluginStateLogger' 'pluginStateLogger'
]) ])
await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await this.appManager.activatePlugin(['statusBar'])
await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await this.appManager.activatePlugin(['pinnedPanel']) await this.appManager.activatePlugin(['pinnedPanel'])
await this.appManager.activatePlugin(['home']) await this.appManager.activatePlugin(['home'])

@ -31,8 +31,10 @@ export const Preload = (props: any) => {
) )
function loadAppComponent() { function loadAppComponent() {
console.log('loading remix in the preloader')
import('../../app') import('../../app')
.then((AppComponent) => { .then((AppComponent) => {
console.log('loading remix in the preloader', AppComponent)
const appComponent = new AppComponent.default() const appComponent = new AppComponent.default()
appComponent.run().then(() => { appComponent.run().then(() => {
props.root.render(<RemixApp app={appComponent} />) props.root.render(<RemixApp app={appComponent} />)
@ -68,6 +70,7 @@ export const Preload = (props: any) => {
if (fsLoaded) { if (fsLoaded) {
console.log(fsLoaded.name + ' activated') console.log(fsLoaded.name + ' activated')
_paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name]) _paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name])
console.log('loading remix in the preloader')
loadAppComponent() loadAppComponent()
} else { } else {
_paq.push(['trackEvent', 'Storage', 'error', 'no supported storage']) _paq.push(['trackEvent', 'Storage', 'error', 'no supported storage'])

@ -12,7 +12,7 @@ const sidePanel = {
displayName: 'Side Panel', displayName: 'Side Panel',
description: 'Remix IDE side panel', description: 'Remix IDE side panel',
version: packageJson.version, version: packageJson.version,
methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView'] methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView', 'focus']
} }
export class SidePanel extends AbstractPanel { export class SidePanel extends AbstractPanel {

@ -0,0 +1,106 @@
import React from 'react'
import { EventEmitter } from 'events'
import { Plugin } from '@remixproject/engine'
import packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
import { PluginProfile, StatusBarInterface } from '../../types'
import { RemixUIStatusBar } from '@remix-ui/statusbar'
import { FilePanelType } from '@remix-ui/workspace'
import { VerticalIcons } from './vertical-icons'
const statusBarProfile: PluginProfile = {
name: 'statusBar',
displayName: 'Status Bar',
description: 'Remix IDE status bar panel',
methods: ['isAIActive'],
version: packageJson.version,
}
export class StatusBar extends Plugin implements StatusBarInterface {
htmlElement: HTMLDivElement
events: EventEmitter
filePanelPlugin: FilePanelType
verticalIcons: VerticalIcons
dispatch: React.Dispatch<any> = () => {}
currentWorkspaceName: string = ''
isGitRepo: boolean = false
isAiActive: boolean = false
constructor(filePanel: FilePanelType, veritcalIcons: VerticalIcons) {
super(statusBarProfile)
this.filePanelPlugin = filePanel
this.verticalIcons = veritcalIcons
this.events = new EventEmitter()
this.htmlElement = document.createElement('div')
this.htmlElement.setAttribute('id', 'status-bar')
this.filePanelPlugin
}
async isWorkspaceAGitRepo() {
const isGit = await this.call('fileManager', 'isGitRepo')
if (!isGit) return
this.isGitRepo = true
this.renderComponent()
}
async setCurrentGitWorkspaceName() {
if (!this.isGitRepo) return
const workspaceName = localStorage.getItem('currentWorkspace')
workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = 'unknown'
this.renderComponent()
}
async isAIActive() {
let aiActive
this.on('settings', 'copilotChoiceUpdated', async (isChecked) => {
aiActive = isChecked
this.isAiActive = isChecked
})
this.renderComponent()
return aiActive
}
onActivation(): void {
this.on('filePanel', 'workspaceInitializationCompleted', async () => {
const isGit = await this.call('fileManager', 'isGitRepo')
if (!isGit) return
const workspaceName = localStorage.getItem('currentWorkspace')
workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = ''
})
this.on('filePanel', 'switchToWorkspace', async (workspace: string) => {
console.log('from status bar switchToWorkspace')
await this.isWorkspaceAGitRepo()
if (!this.isGitRepo) {
this.currentWorkspaceName = 'Not a git repo'
return
}
const workspaceName = localStorage.getItem('currentWorkspace')
workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = 'error'
})
this.on('settings', 'copilotChoiceChanged', (isAiActive) => {
this.isAiActive = isAiActive
})
this.renderComponent()
}
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
renderComponent() {
this.dispatch({
plugins: this,
})
}
updateComponent(state: any) {
return <RemixUIStatusBar statusBarPlugin={state.plugins} />
}
render() {
return (
<div data-id="status-bar-container">
<PluginViewWrapper plugin={this} />
</div>
)
}
}

@ -34,6 +34,7 @@ const profile = {
methods: [ methods: [
'createNewFile', 'createNewFile',
'uploadFile', 'uploadFile',
'echoCall',
'getCurrentWorkspace', 'getCurrentWorkspace',
'getAvailableWorkspaceName', 'getAvailableWorkspaceName',
'getWorkspaces', 'getWorkspaces',
@ -43,7 +44,7 @@ const profile = {
'registerContextMenuItem', 'registerContextMenuItem',
'renameWorkspace', 'renameWorkspace',
'deleteWorkspace', 'deleteWorkspace',
'loadTemplate', 'loadTemplate',
'clone', 'clone',
'isExpanded', 'isExpanded',
'isGist' 'isGist'
@ -154,10 +155,10 @@ module.exports = class Filepanel extends ViewPlugin {
return this.workspaces return this.workspaces
} }
getAvailableWorkspaceName(name) { getAvailableWorkspaceName(name) {
if (!this.workspaces) return name if (!this.workspaces) return name
let index = 1 let index = 1
let workspace = this.workspaces.find((workspace) => workspace.name === name + ' - ' + index) let workspace = this.workspaces.find((workspace) => workspace.name === name + ' - ' + index)
while (workspace) { while (workspace) {
index++ index++
workspace = this.workspaces.find((workspace) => workspace.name === name + ' - ' + index) workspace = this.workspaces.find((workspace) => workspace.name === name + ' - ' + index)

@ -174,7 +174,7 @@ export class TabProxy extends Plugin {
this.on('fileDecorator', 'fileDecoratorsChanged', async (items) => { this.on('fileDecorator', 'fileDecoratorsChanged', async (items) => {
this.tabsApi.setFileDecorations(items) this.tabsApi.setFileDecorations(items)
}) })
try { try {
this.themeQuality = (await this.call('theme', 'currentTheme') ).quality this.themeQuality = (await this.call('theme', 'currentTheme') ).quality
} catch (e) { } catch (e) {
@ -240,6 +240,11 @@ export class TabProxy extends Plugin {
if ((name.endsWith('.vy') && icon === undefined) || title.includes('Vyper')) { if ((name.endsWith('.vy') && icon === undefined) || title.includes('Vyper')) {
icon = 'assets/img/vyperLogo2.webp' icon = 'assets/img/vyperLogo2.webp'
} }
if (title === 'Solidity Compile Details') {
icon = 'assets/img/solidity.webp'
}
var slash = name.split('/') var slash = name.split('/')
const tabPath = slash.reverse() const tabPath = slash.reverse()
@ -357,7 +362,9 @@ export class TabProxy extends Plugin {
const onZoomIn = () => this.editor.editorFontSize(1) const onZoomIn = () => this.editor.editorFontSize(1)
const onZoomOut = () => this.editor.editorFontSize(-1) const onZoomOut = () => this.editor.editorFontSize(-1)
const onReady = (api) => { this.tabsApi = api } const onReady = (api) => {
this.tabsApi = api
}
this.dispatch({ this.dispatch({
plugin: this, plugin: this,

@ -45,7 +45,6 @@ export class CompilationDetailsPlugin extends ViewPlugin {
async showDetails(sentPayload: any) { async showDetails(sentPayload: any) {
await this.call('tabs', 'focus', 'compilationDetails') await this.call('tabs', 'focus', 'compilationDetails')
setTimeout(() => { setTimeout(() => {
// TODO: use the react API to render when the tab is focused and the plugin in the view.
this.payload = sentPayload this.payload = sentPayload
this.renderComponent() this.renderComponent()
}, 2000) }, 2000)

@ -29,7 +29,7 @@
"home.dgitPluginDesc": "Add source control to your projects.", "home.dgitPluginDesc": "Add source control to your projects.",
"home.oneClickDappDesc": "Quickly generate smart contract interfaces", "home.oneClickDappDesc": "Quickly generate smart contract interfaces",
"home.getStarted": "Get Started", "home.getStarted": "Get Started",
"home.projectTemplates": "Project Templates", "home.projectTemplates": "Explore. Prototype. Create.",
"home.blankTemplateDesc": "Create an empty workspace.", "home.blankTemplateDesc": "Create an empty workspace.",
"home.remixDefaultTemplateDesc": "Create a workspace with sample files.", "home.remixDefaultTemplateDesc": "Create a workspace with sample files.",
"home.ozerc20TemplateDesc": "Create an ERC20 token by importing OpenZeppelin library.", "home.ozerc20TemplateDesc": "Create an ERC20 token by importing OpenZeppelin library.",
@ -39,7 +39,7 @@
"home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.", "home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.",
"home.learn": "Learn", "home.learn": "Learn",
"home.learnEth1": "Remix Basics", "home.learnEth1": "Remix Basics",
"home.learnEth1Desc":"An introduction to Remix's interface and basic operations.", "home.learnEth1Desc": "An introduction to Remix's interface and basic operations.",
"home.learnEth2": "Intro to Solidity", "home.learnEth2": "Intro to Solidity",
"home.learnEth2Desc": "Interactively learn Solidity beginner concepts.", "home.learnEth2Desc": "Interactively learn Solidity beginner concepts.",
"home.remixAdvanced": "Deploying with Libraries", "home.remixAdvanced": "Deploying with Libraries",
@ -56,15 +56,16 @@
"home.remixDesktop": "Remix Desktop", "home.remixDesktop": "Remix Desktop",
"home.searchDocumentation": "Search Documentation", "home.searchDocumentation": "Search Documentation",
"home.files": "Files", "home.files": "Files",
"home.newFile": "New File", "home.newFile": "New",
"home.startCoding": "Start Coding", "home.startCoding": "Start Coding",
"home.startCodingPlayground": "Open a playground for prototyping and simple learning", "home.startCodingPlayground": "Open a playground for prototyping and simple learning",
"home.openFile": "Open File", "home.openFile": "Open",
"home.openFileTooltip": "Open a File from your File System", "home.openFileTooltip": "Open a File from your File System",
"home.accessFileSystem": "Access File System", "home.accessFileSystem": "Connect to Local Filesystem",
"home.loadFrom": "Load from", "home.loadFrom": "Load from",
"home.resources": "Resources", "home.resources": "Resources",
"home.connectToLocalhost": "Connect to Localhost", "home.connectToLocalhost": "Connect to Localhost",
"home.seeAllTutorials": "See all tutorials", "home.seeAllTutorials": "See all tutorials",
"home.maintainedByRemix": "Maintained by Remix" "home.maintainedByRemix": "Maintained by Remix"
} }

@ -82,7 +82,7 @@
"udapp.solScan.iconTooltip": "Click to scan this contract for vulnerabilities using third-party SolidityScan [BETA]", "udapp.solScan.iconTooltip": "Click to scan this contract for vulnerabilities using third-party SolidityScan [BETA]",
"udapp.solScan.modalTitle": "Permission to share code", "udapp.solScan.modalTitle": "Permission to share code",
"udapp.solScan.modalMessage": "To scan the contract for vulnerabilities & possible risks, smart contract code will be shared to third-party SolidityScan. ", "udapp.solScan.modalMessage": "To scan and analyze the contract for risks and vulnerabilities, its code will be shared with SolidityScan, a third-party provider. ",
"udapp.solScan.likeToContinue": "Would you like to continue?", "udapp.solScan.likeToContinue": "Would you like to continue?",
"udapp.solScan.modalOkLabel": "Continue", "udapp.solScan.modalOkLabel": "Continue",
"udapp.solScan.modalCancelLabel": "Cancel", "udapp.solScan.modalCancelLabel": "Cancel",

@ -97,6 +97,7 @@ module.exports = class SettingsTab extends ViewPlugin {
updateCopilotChoice(isChecked) { updateCopilotChoice(isChecked) {
this.config.set('settings/copilot/suggest/activate', isChecked) this.config.set('settings/copilot/suggest/activate', isChecked)
this.useCopilot = isChecked this.useCopilot = isChecked
this.emit('copilotChoiceUpdated', isChecked)
this.dispatch({ this.dispatch({
...this ...this
}) })

@ -31,6 +31,7 @@ let requiredModules = [ // services + layout views + system views
'menuicons', 'menuicons',
'filePanel', 'filePanel',
'terminal', 'terminal',
'statusBar',
'settings', 'settings',
'pluginManager', 'pluginManager',
'tabs', 'tabs',
@ -103,7 +104,7 @@ const isVM = (name) => {
} }
export function isNative(name) { export function isNative(name) {
// nativePlugin allows to bypass the permission request // nativePlugin allows to bypass the permission request
const nativePlugins = [ const nativePlugins = [
'vyper', 'vyper',
@ -117,6 +118,7 @@ export function isNative(name) {
'solhint', 'solhint',
'solidityUnitTesting', 'solidityUnitTesting',
'layout', 'layout',
'statusBar',
'notification', 'notification',
'hardhat-provider', 'hardhat-provider',
'ganache-provider', 'ganache-provider',

@ -0,0 +1,19 @@
export interface PluginProfile {
name: string
displayName: string
description: string
keywords?: string[]
icon?: string
url?: string
methods?: string[]
events?: string[]
version?: string
}
export interface StatusBarInterface {
htmlElement: HTMLDivElement
events: EventEmitter
filePanelPlugin: FilePanelType
dispatch: React.Dispatch<any>
setDispatch(dispatch: React.Dispatch<any>): void
}

@ -1,23 +1,19 @@
import React, {useState, useEffect, useRef} from 'react' import { useState, useEffect, useRef } from 'react'
import {remixClient} from './utils' import { remixClient } from './utils'
import {CompilationResult} from '@remixproject/plugin-api' import { CompilationResult } from '@remixproject/plugin-api'
// Components // Components
import CompilerButton from './components/CompilerButton' import CompilerButton from './components/CompilerButton'
import WarnRemote from './components/WarnRemote'
import VyperResult from './components/VyperResult' import VyperResult from './components/VyperResult'
import LocalUrlInput from './components/LocalUrl' import LocalUrlInput from './components/LocalUrl'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
import ToggleButton from 'react-bootstrap/ToggleButton'
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button'
import Accordion from 'react-bootstrap/Accordion' import Accordion from 'react-bootstrap/Accordion'
import Card from 'react-bootstrap/Card'
import './app.css' import './app.css'
import {CustomTooltip} from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import {Form} from 'react-bootstrap' import { Form } from 'react-bootstrap'
import {CompileErrorCard} from './components/CompileErrorCard' import { CompileErrorCard } from './components/CompileErrorCard'
import CustomAccordionToggle from './components/CustomAccordionToggle' import CustomAccordionToggle from './components/CustomAccordionToggle'
interface AppState { interface AppState {
@ -89,11 +85,11 @@ const App = () => {
/** Update the environment state value */ /** Update the environment state value */
function setEnvironment(environment: 'local' | 'remote') { function setEnvironment(environment: 'local' | 'remote') {
setState({...state, environment}) setState({ ...state, environment })
} }
function setLocalUrl(url: string) { function setLocalUrl(url: string) {
setState({...state, localUrl: url}) setState({ ...state, localUrl: url })
} }
function compilerUrl() { function compilerUrl() {
@ -111,14 +107,20 @@ const App = () => {
spinnerIcon.current.classList.add('remixui_spinningIcon') spinnerIcon.current.classList.add('remixui_spinningIcon')
} }
const [toggleAccordion, setToggleAccordion] = useState(false) const [cloneCount, setCloneCount] = useState(0)
return ( return (
<main id="vyper-plugin"> <main id="vyper-plugin">
<section> <section>
<div className="px-3 pt-3 mb-3 w-100"> <div className="px-3 pt-3 mb-3 w-100">
<CustomTooltip placement="bottom" tooltipText="Clone a repo of Vyper examples. Switch to the File Explorer to see the examples."> <CustomTooltip placement="bottom" tooltipText="Clone a repo of Vyper examples. Switch to the File Explorer to see the examples.">
<Button data-id="add-repository" className="w-100 btn btn-secondary" onClick={() => remixClient.cloneVyperRepo()}> <Button data-id="add-repository" className="w-100 btn btn-secondary" onClick={() => {
{cloneCount === 0 ? remixClient.cloneVyperRepo() : remixClient.cloneVyperRepo(cloneCount)}
setCloneCount((prev) => {
console.log(prev)
return ++prev
})
}}>
Clone a repo of Vyper examples Clone a repo of Vyper examples
</Button> </Button>
</CustomTooltip> </CustomTooltip>
@ -162,7 +164,7 @@ const App = () => {
in the .vy file. in the .vy file.
</span> </span>
<div className="px-3 w-100 mb-3 mt-1" id="compile-btn"> <div className="px-3 w-100 mb-3 mt-1" id="compile-btn">
<CompilerButton compilerUrl={compilerUrl()} contract={contract} setOutput={(name, update) => setOutput({...output, [name]: update})} resetCompilerState={resetCompilerResultState} output={output} remixClient={remixClient}/> <CompilerButton compilerUrl={compilerUrl()} contract={contract} setOutput={(name, update) => setOutput({ ...output, [name]: update })} resetCompilerState={resetCompilerResultState} output={output} remixClient={remixClient}/>
</div> </div>
<article id="result" className="p-2 mx-3 border-top mt-2"> <article id="result" className="p-2 mx-3 border-top mt-2">

@ -1,9 +1,8 @@
import { ABIDescription} from '@remixproject/plugin-api' import { ABIDescription } from '@remixproject/plugin-api'
import axios from 'axios' import axios from 'axios'
import { remixClient } from './remix-client' import { remixClient } from './remix-client'
import _ from 'lodash' import _ from 'lodash'
export interface Contract { export interface Contract {
name: string name: string
content: string content: string
@ -72,8 +71,8 @@ const buildError = (output) => {
const line = output.line const line = output.line
if (line) { if (line) {
const lineColumnPos = { const lineColumnPos = {
start: {line: line - 1, column: 10}, start: { line: line - 1, column: 10 },
end: {line: line - 1, column: 10} end: { line: line - 1, column: 10 }
} }
// remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4') // remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
} else { } else {
@ -92,8 +91,8 @@ const buildError = (output) => {
} }
if (location?.length > 0) { if (location?.length > 0) {
const lineColumnPos = { const lineColumnPos = {
start: {line: parseInt(location[0]) - 1, column: 10}, start: { line: parseInt(location[0]) - 1, column: 10 },
end: {line: parseInt(location[0]) - 1, column: 10} end: { line: parseInt(location[0]) - 1, column: 10 }
} }
// remixClient.highlight(lineColumnPos as any, _contract.name, message) // remixClient.highlight(lineColumnPos as any, _contract.name, message)
} }
@ -175,13 +174,11 @@ export async function compile(url: string, contract: Contract): Promise<any> {
throw new Error('Use extension .vy for Vyper.') throw new Error('Use extension .vy for Vyper.')
} }
let contractName = contract['name'] let contractName = contract['name']
const compilePackage = { const compilePackage = {
manifest: 'ethpm/3', manifest: 'ethpm/3',
sources: { sources: {
[contractName] : {content : fixContractContent(contract.content)} [contractName] : { content : fixContractContent(contract.content) }
} }
} }
let response = await axios.post(`${url}compile`, compilePackage ) let response = await axios.post(`${url}compile`, compilePackage )
@ -266,7 +263,6 @@ export function toStandardOutput(fileName: string, compilationResult: any): any
} }
} }
export async function compileContract(contract: string, compilerUrl: string, setOutput?: any, setLoadingSpinnerState?: React.Dispatch<React.SetStateAction<boolean>>, spinner?: boolean) { export async function compileContract(contract: string, compilerUrl: string, setOutput?: any, setLoadingSpinnerState?: React.Dispatch<React.SetStateAction<boolean>>, spinner?: boolean) {
remixClient.eventEmitter.emit('resetCompilerState', {}) remixClient.eventEmitter.emit('resetCompilerState', {})
spinner && spinner === true ? setLoadingSpinnerState && setLoadingSpinnerState(true) : null spinner && spinner === true ? setLoadingSpinnerState && setLoadingSpinnerState(true) : null
@ -301,7 +297,7 @@ export async function compileContract(contract: string, compilerUrl: string, set
}) })
setLoadingSpinnerState && setLoadingSpinnerState(false) setLoadingSpinnerState && setLoadingSpinnerState(false)
remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: output.message, title: 'Error compiling...', line: output.line, column: output.column, key: 1 }) remixClient.eventEmitter.emit('setOutput', { status: 'failed', message: output.message, title: 'Error compiling...', line: output.line, column: output.column, key: 1 })
output = null output = null
return return
} }
@ -332,13 +328,10 @@ export async function compileContract(contract: string, compilerUrl: string, set
}) })
setLoadingSpinnerState && setLoadingSpinnerState(false) setLoadingSpinnerState && setLoadingSpinnerState(false)
remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: err.message}) remixClient.eventEmitter.emit('setOutput', { status: 'failed', message: err.message })
} }
} }
export type StandardOutput = { export type StandardOutput = {
sources: { sources: {
[fileName: string]: { [fileName: string]: {

@ -1,9 +1,9 @@
import {HighlightPosition, CompilationResult, RemixApi, customAction} from '@remixproject/plugin-api' import { HighlightPosition, CompilationResult, RemixApi, customAction } from '@remixproject/plugin-api'
import {Api, Status} from '@remixproject/plugin-utils' import { Api, Status } from '@remixproject/plugin-utils'
import {createClient} from '@remixproject/plugin-webview' import { createClient } from '@remixproject/plugin-webview'
import {PluginClient} from '@remixproject/plugin' import { PluginClient } from '@remixproject/plugin'
import {Contract, compileContract} from './compiler' import { Contract, compileContract } from './compiler'
import {ExampleContract} from '../components/VyperResult' import { ExampleContract } from '../components/VyperResult'
import EventEmitter from 'events' import EventEmitter from 'events'
export type VyperComplierAddress = 'https://vyper2.remixproject.org/' | 'http://localhost:8000/' export type VyperComplierAddress = 'https://vyper2.remixproject.org/' | 'http://localhost:8000/'
@ -49,7 +49,7 @@ export class RemixClient extends PluginClient {
} }
/** Load Ballot contract example into the file manager */ /** Load Ballot contract example into the file manager */
async loadContract({name, address}: ExampleContract) { async loadContract({ name, address }: ExampleContract) {
try { try {
const content = await this.client.call('contentImport', 'resolve', address) const content = await this.client.call('contentImport', 'resolve', address)
await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content) await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content)
@ -77,23 +77,24 @@ export class RemixClient extends PluginClient {
} }
} }
async cloneVyperRepo() { async cloneVyperRepo(count?: number) {
try { try {
// @ts-ignore // @ts-ignore
this.call('notification', 'toast', 'cloning Snekmate Vyper repository...') this.call('notification', 'toast', 'cloning Vyper-lang repository...')
await this.call('manager', 'activatePlugin', 'dGitProvider') await this.call('manager', 'activatePlugin', 'dGitProvider')
await this.call( await this.call(
'dGitProvider', 'dGitProvider',
'clone', 'clone',
{url: 'https://github.com/pcaversaccio/snekmate', token: null, branch: 'v0.0.5'}, { url: 'https://github.com/vyperlang/vyper', token: null, branch: 'v0.3.10' },
// @ts-ignore // @ts-ignore
'snekmate' (count === undefined || count === 0) ? 'vyper-lang' : `vyper-lang_${count}`
) )
this.call( this.call(
// @ts-ignore // @ts-ignore
'notification', 'notification',
'toast', 'toast',
'Snekmate Vyper repository cloned, the workspace snekmate has been created.' 'Vyper repository cloned, the workspace Vyper has been created.'
) )
} catch (e) { } catch (e) {
// @ts-ignore // @ts-ignore

@ -23,7 +23,18 @@ const DragBar = (props: IRemixDragBarUi) => {
if (props.hidden) { if (props.hidden) {
setDragBarPosX(offset) setDragBarPosX(offset)
} else if (props.layoutPosition === 'left') { } else if (props.layoutPosition === 'left') {
setDragBarPosX(offset + props.refObject.current.offsetWidth) const checkResolution = () => {
const width = window.innerWidth
const height = window.innerHeight
if (height <= 781 && width <= 1150) {
setDragBarPosX(props.minWidth - 50)
} else {
setDragBarPosX(props.minWidth + 50)
}
}
checkResolution()
props.refObject.current.style.width = props.minWidth + 'px'
} else if (props.layoutPosition === 'right') { } else if (props.layoutPosition === 'right') {
setDragBarPosX(offset) setDragBarPosX(offset)
} }

@ -217,8 +217,11 @@ const RemixApp = (props: IRemixAppUi) => {
layoutPosition='right' layoutPosition='right'
></DragBar> ></DragBar>
} }
<div>{props.app.hiddenPanel.render()}</div>
<div className="statusBar fixed-bottom">
{props.app.statusBar.render()}
</div>
</div> </div>
<div>{props.app.hiddenPanel.render()}</div>
<AppDialogs></AppDialogs> <AppDialogs></AppDialogs>
<DialogViewPlugin></DialogViewPlugin> <DialogViewPlugin></DialogViewPlugin>
</AppProvider> </AppProvider>

@ -20,6 +20,7 @@ pre {
overflow : hidden; overflow : hidden;
flex : 1; flex : 1;
min-width : 320px; min-width : 320px;
padding-bottom : 1.4rem;
} }
.iconpanel { .iconpanel {
display : flex; display : flex;
@ -27,12 +28,17 @@ pre {
overflow : hidden; overflow : hidden;
width : 50px; width : 50px;
user-select : none; user-select : none;
padding-bottom : 1.4rem;
} }
.sidepanel { .sidepanel {
display : flex; display : flex;
flex-direction : row-reverse; flex-direction : row-reverse;
width : 320px; width : 320px;
transition : width 0.25s; transition : width 0.25s;
padding-bottom : 1.4rem;
}
.statusBar {
} }
.pinnedpanel { .pinnedpanel {
width : 320px; width : 320px;
@ -77,4 +83,4 @@ pre {
width : 4rem; width : 4rem;
right : -10px; right : -10px;
filter : opacity(0.5); filter : opacity(0.5);
} }

@ -148,10 +148,10 @@ export const EditorUI = (props: EditorUIProps) => {
const intl = useIntl() const intl = useIntl()
const [, setCurrentBreakpoints] = useState({}) const [, setCurrentBreakpoints] = useState({})
const defaultEditorValue = ` const defaultEditorValue = `
\t\t\t\t\t\t\t ____ _____ __ __ ___ __ __ ___ ____ _____ \t\t\t\t\t\t\t ____ _____ __ __ ___ __ __ ___ ____ _____
\t\t\t\t\t\t\t| _ \\ | ____| | \\/ | |_ _| \\ \\/ / |_ _| | _ \\ | ____| \t\t\t\t\t\t\t| _ \\ | ____| | \\/ | |_ _| \\ \\/ / |_ _| | _ \\ | ____|
\t\t\t\t\t\t\t| |_) | | _| | |\\/| | | | \\ / | | | | | | | _| \t\t\t\t\t\t\t| |_) | | _| | |\\/| | | | \\ / | | | | | | | _|
\t\t\t\t\t\t\t| _ < | |___ | | | | | | / \\ | | | |_| | | |___ \t\t\t\t\t\t\t| _ < | |___ | | | | | | / \\ | | | |_| | | |___
\t\t\t\t\t\t\t|_| \\_\\ |_____| |_| |_| |___| /_/\\_\\ |___| |____/ |_____|\n\n \t\t\t\t\t\t\t|_| \\_\\ |_____| |_| |_| |___| /_/\\_\\ |___| |____/ |_____|\n\n
\t\t\t\t\t\t\t${intl.formatMessage({ id: 'editor.keyboardShortcuts' })}:\n \t\t\t\t\t\t\t${intl.formatMessage({ id: 'editor.keyboardShortcuts' })}:\n
\t\t\t\t\t\t\t\tCTRL + S: ${intl.formatMessage({ id: 'editor.keyboardShortcuts.text1' })}\n \t\t\t\t\t\t\t\tCTRL + S: ${intl.formatMessage({ id: 'editor.keyboardShortcuts.text1' })}\n
@ -718,7 +718,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.generateDocumentation' }), label: intl.formatMessage({ id: 'editor.generateDocumentation' }),
contextMenuOrder: 0, // choose the order contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [], keybindings: [
// Keybinding for Ctrl + D
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyD
],
run: async () => { run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file) const content = await props.plugin.call('fileManager', 'readFile', file)
@ -734,7 +737,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.explainFunction' }), label: intl.formatMessage({ id: 'editor.explainFunction' }),
contextMenuOrder: 1, // choose the order contextMenuOrder: 1, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [], keybindings: [
// Keybinding for Ctrl + Shift + E
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyMod.Shift | monacoRef.current.KeyCode.KeyE
],
run: async () => { run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file) const content = await props.plugin.call('fileManager', 'readFile', file)
@ -750,7 +756,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.explainFunctionSol' }), label: intl.formatMessage({ id: 'editor.explainFunctionSol' }),
contextMenuOrder: 1, // choose the order contextMenuOrder: 1, // choose the order
contextMenuGroupId: 'sol-gtp', // create a new grouping contextMenuGroupId: 'sol-gtp', // create a new grouping
keybindings: [], keybindings: [
// Keybinding for Ctrl + E
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyE
],
run: async () => { run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file) const content = await props.plugin.call('fileManager', 'readFile', file)

@ -12,12 +12,9 @@ function HomeTabFeatured() {
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
return ( return (
<div className="pt-3 pl-2" id="hTFeaturedeSection"> <div className="pt-1 pl-2" id="hTFeaturedeSection">
<label style={{ fontSize: '1.2rem' }}> <div className="mb-2 remix_ui-carousel-container">
<FormattedMessage id="home.featured" /> <div className="w-100 d-flex flex-column rounded-3 remix_ui-carouselbox">
</label>
<div className="mb-2">
<div className="w-100 d-flex flex-column" style={{ height: '200px' }}>
<ThemeContext.Provider value={themeFilter}> <ThemeContext.Provider value={themeFilter}>
<Carousel <Carousel
arrows={false} arrows={false}
@ -34,16 +31,16 @@ function HomeTabFeatured() {
centerMode={false} centerMode={false}
autoPlay={true} autoPlay={true}
keyBoardControl={true} keyBoardControl={true}
containerClass="border w-full carousel-container" containerClass="border w-full carousel-container d-flex align-items-center"
sliderClass="h-100 justify-content-between" sliderClass="h-100 justify-content-between"
deviceType={'desktop'} deviceType={'desktop'}
itemClass="" itemClass=""
autoPlaySpeed={10000} autoPlaySpeed={10000}
dotListClass="position-relative mt-2" dotListClass="position-relative mt-2"
> >
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href={releaseDetails.moreLink} target="__blank"> <a href={releaseDetails.moreLink} target="__blank">
<img src={'assets/img/remi_drums_whatsnew.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'assets/img/remi_drums_whatsnew.webp'} className="remixui_carouselImage" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>{releaseDetails.version} {releaseDetails.title}</h5> <h5>{releaseDetails.version} {releaseDetails.title}</h5>
@ -65,9 +62,9 @@ function HomeTabFeatured() {
</a> </a>
</div> </div>
</div> </div>
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://remix-project.org" target="__blank"> <a href="https://remix-project.org" target="__blank">
<img src={'assets/img/bgRemi_small.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'assets/img/bgRemi_small.webp'} className="remixui_carouselImage" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5> <h5>
@ -86,9 +83,9 @@ function HomeTabFeatured() {
</a> </a>
</div> </div>
</div> </div>
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://www.youtube.com/@EthereumRemix/videos" target="__blank"> <a href="https://www.youtube.com/@EthereumRemix/videos" target="__blank">
<img src={'/assets/img/YouTubeLogo.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'/assets/img/YouTubeLogo.webp'} className="remixui_carouselImage" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5> <h5>
@ -110,9 +107,9 @@ function HomeTabFeatured() {
</a> </a>
</div> </div>
</div> </div>
<div className="mr-1 pr-1 d-flex"> <div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform" target="__blank"> <a href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform" target="__blank">
<img src={'/assets/img/remixRewardBetaTester_small.webp'} style={{ flex: '1', height: '170px', maxWidth: '170px' }} alt=""></img> <img src={'/assets/img/remixRewardBetaTester_small.webp'} className="remixui_carouselImage" alt=""></img>
</a> </a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}> <div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5> <h5>

@ -84,7 +84,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
} }
return ( return (
<div className="pl-2 w-100" id="hTFeaturedPlugins"> <div className="pl-2 w-100 align-items-end remixui_featuredplugins_container" id="hTFeaturedPlugins">
<label className="" style={{ fontSize: '1.2rem' }}> <label className="" style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.featuredPlugins" /> <FormattedMessage id="home.featuredPlugins" />
</label> </label>

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useRef, useReducer, useEffect } from 'react' import React, { useState, useRef, useReducer, useEffect } from 'react'
import { FormattedMessage } from 'react-intl' import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import {ModalDialog} from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import {Toaster} from '@remix-ui/toaster' // eslint-disable-line
const _paq = (window._paq = window._paq || []) // eslint-disable-line const _paq = (window._paq = window._paq || []) // eslint-disable-line
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import { TEMPLATE_NAMES } from '@remix-ui/workspace' import { TEMPLATE_NAMES } from '@remix-ui/workspace'
@ -14,7 +14,7 @@ interface HomeTabFileProps {
const loadingInitialState = { const loadingInitialState = {
tooltip: '', tooltip: '',
showModalDialog: false, showModalDialog: false,
importSource: '' importSource: '',
} }
const loadingReducer = (state = loadingInitialState, action) => { const loadingReducer = (state = loadingInitialState, action) => {
@ -22,7 +22,7 @@ const loadingReducer = (state = loadingInitialState, action) => {
...state, ...state,
tooltip: action.tooltip, tooltip: action.tooltip,
showModalDialog: false, showModalDialog: false,
importSource: '' importSource: '',
} }
} }
@ -45,7 +45,7 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
modalInfo: { title: '', loadItem: '', examples: [], prefix: '' }, modalInfo: { title: '', loadItem: '', examples: [], prefix: '' },
importSource: '', importSource: '',
toasterMsg: '', toasterMsg: '',
recentWorkspaces: [] recentWorkspaces: [],
}) })
const [, dispatch] = useReducer(loadingReducer, loadingInitialState) const [, dispatch] = useReducer(loadingReducer, loadingInitialState)
@ -71,7 +71,9 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
if (!recents) { if (!recents) {
newRecents = [] newRecents = []
} else { } else {
newRecents = recents.filter((el) => { return el !== name}) newRecents = recents.filter((el) => {
return el !== name
})
localStorage.setItem('recentWorkspaces', JSON.stringify(newRecents)) localStorage.setItem('recentWorkspaces', JSON.stringify(newRecents))
} }
setState((prevState) => { setState((prevState) => {
@ -85,8 +87,7 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
try { try {
plugin.off('filePanel', 'setWorkspace') plugin.off('filePanel', 'setWorkspace')
plugin.off('filePanel', 'workspaceDeleted') plugin.off('filePanel', 'workspaceDeleted')
} catch (e) { } catch (e) {}
}
} }
}, [plugin]) }, [plugin])
@ -162,7 +163,6 @@ contract HelloWorld {
} else { } else {
await plugin.call('fileManager', 'open', '/contracts/HelloWorld.sol') await plugin.call('fileManager', 'open', '/contracts/HelloWorld.sol')
} }
} }
const uploadFile = async (target) => { const uploadFile = async (target) => {
@ -216,14 +216,7 @@ contract HelloWorld {
return ( return (
<> <>
<ModalDialog <ModalDialog id="homeTab" title={'Import from ' + state.modalInfo.title} okLabel="Import" hide={!state.showModalDialog} handleHide={() => hideFullMessage()} okFn={() => processLoading(state.modalInfo.title)}>
id="homeTab"
title={'Import from ' + state.modalInfo.title}
okLabel="Import"
hide={!state.showModalDialog}
handleHide={() => hideFullMessage()}
okFn={() => processLoading(state.modalInfo.title)}
>
<div className="p-2 user-select-auto"> <div className="p-2 user-select-auto">
{state.modalInfo.loadItem !== '' && <span>Enter the {state.modalInfo.loadItem} you would like to load.</span>} {state.modalInfo.loadItem !== '' && <span>Enter the {state.modalInfo.loadItem} you would like to load.</span>}
{state.modalInfo.examples.length !== 0 && ( {state.modalInfo.examples.length !== 0 && (
@ -236,9 +229,9 @@ contract HelloWorld {
{state.modalInfo.prefix && <span className="text-nowrap align-self-center mr-2">ipfs://</span>} {state.modalInfo.prefix && <span className="text-nowrap align-self-center mr-2">ipfs://</span>}
<input <input
ref={inputValue} ref={inputValue}
type='text' type="text"
name='prompt_text' name="prompt_text"
id='inputPrompt_text' id="inputPrompt_text"
className="w-100 mt-1 form-control" className="w-100 mt-1 form-control"
data-id="homeTabModalDialogCustomPromptText" data-id="homeTabModalDialogCustomPromptText"
value={state.importSource} value={state.importSource}
@ -253,62 +246,11 @@ contract HelloWorld {
</ModalDialog> </ModalDialog>
<Toaster message={state.toasterMsg} /> <Toaster message={state.toasterMsg} />
<div className="justify-content-start mt-1 p-2 d-flex flex-column" id="hTFileSection"> <div className="justify-content-start mt-1 p-2 d-flex flex-column" id="hTFileSection">
<label style={{ fontSize: '1.2rem' }}> <div className="mb-3">
<FormattedMessage id="home.files" />
</label>
<div className="d-flex flex-column">
<div className="d-flex flex-row">
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='home.startCodingPlayground' />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button className="btn btn-primary text-nowrap p-2 mr-2 border my-1" data-id="homeTabStartCoding" style={{ width: 'fit-content' }} onClick={() => startCoding()}>
<FormattedMessage id="home.startCoding" />
</button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='home.openFileTooltip' />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<span>
<label className="btn text-nowrap p-2 mr-2 border my-1" style={{ width: 'fit-content', cursor: 'pointer' }} htmlFor="openFileInput">
<FormattedMessage id="home.openFile" />
</label>
<input
title="open file"
type="file"
id="openFileInput"
onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}}
multiple
/>
</span>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.connectToLocalhost" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button className="btn text-nowrap p-2 border my-1" style={{ width: 'fit-content' }} onClick={() => connectToLocalhost()}>
<FormattedMessage id="home.accessFileSystem" />
</button>
</CustomTooltip>
</div>
{(state.recentWorkspaces[0] || state.recentWorkspaces[1] || state.recentWorkspaces[2]) && ( {(state.recentWorkspaces[0] || state.recentWorkspaces[1] || state.recentWorkspaces[2]) && (
<div className="d-flex flex-column"> <div className="d-flex flex-column mb-5 remixui_recentworkspace">
<label style={{ fontSize: '0.8rem' }} className="mt-3"> <label style={{ fontSize: '0.8rem' }} className="mt-3">
Recent workspaces Recent Workspaces
</label> </label>
{state.recentWorkspaces[0] && state.recentWorkspaces[0] !== '' && ( {state.recentWorkspaces[0] && state.recentWorkspaces[0] !== '' && (
<a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[0])}> <a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[0])}>
@ -328,36 +270,63 @@ contract HelloWorld {
</div> </div>
)} )}
</div> </div>
<label style={{ fontSize: '0.8rem' }} className="pt-3"> <div className="d-flex flex-column flex-nowrap pt-3">
<FormattedMessage id="home.loadFrom" /> <label style={{ fontSize: '1.2rem' }}>
</label> <FormattedMessage id="home.files" />
<div className="d-flex"> </label>
<button <div className="d-flex flex-column">
className="btn p-2 border mr-2" <div className="d-flex flex-row">
data-id="landingPageImportFromGitHubButton" <CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.startCodingPlayground" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3">
onClick={() => <button className="btn btn-primary text-nowrap p-2 mr-2 border my-1" data-id="homeTabNewFile" style={{ width: 'fit-content' }} onClick={async () => await plugin.call('filePanel', 'createNewFile')}>
showFullMessage('GitHub', 'github URL', [ <FormattedMessage id="home.newFile" />
'https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', </button>
'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol', </CustomTooltip>
]) <CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.openFileTooltip" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3">
} <span>
> <label className="btn text-nowrap p-2 mr-2 border my-1" style={{ width: 'fit-content', cursor: 'pointer' }} htmlFor="openFileInput">
GitHub <FormattedMessage id="home.openFile" />
</button> </label>
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}> <input
title="open file"
type="file"
id="openFileInput"
onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}}
multiple
/>
</span>
</CustomTooltip>
<button className="btn text-nowrap p-2 mr-2 border my-1" onClick={() => showFullMessage('Ipfs', 'ipfs hash', ['ipfs://QmQQfBMkpDgmxKzYaoAtqfaybzfgGm9b2LWYyT56Chv6xH'], 'ipfs://')}>
IPFS
</button>
<button className="btn text-nowrap p-2 mr-2 border my-1" data-id="landingPageImportFromGitHubButton" onClick={() => showFullMessage('GitHub', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'])}>
Git Clone
</button>
<button className="btn text-nowrap p-2 mr-2 border my-1" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>
Gist Gist
</button> </button>
<button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs hash', ['ipfs://QmQQfBMkpDgmxKzYaoAtqfaybzfgGm9b2LWYyT56Chv6xH'], 'ipfs://')}>
IPFS <button
</button> className="btn text-nowrap p-2 mr-2 border my-1"
<button onClick={() =>
className="btn p-2 border" showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])
onClick={() => }
showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol']) >
}
>
HTTPS HTTPS
</button> </button>
</div>
</div>
<div className="d-flex mt-2 align-items-end w-100">
<CustomTooltip placement={'top'} tooltipId="overlay-tooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="home.connectToLocalhost" />} tooltipTextClasses="border bg-light text-dark p-1 pr-3">
<button className="btn btn-block text-nowrap p-2 border my-1" onClick={() => connectToLocalhost()}>
<i className="fa-regular fa-desktop pr-2"></i>
<FormattedMessage id="home.accessFileSystem" />
</button>
</CustomTooltip>
</div>
</div> </div>
</div> </div>
</> </>

@ -1,13 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useEffect, useRef, useContext } from 'react' import React, { useEffect, useRef, useContext, SyntheticEvent, useState } from 'react'
import { useIntl, FormattedMessage } from 'react-intl' import { useIntl, FormattedMessage } from 'react-intl'
import { TEMPLATE_NAMES,TEMPLATE_METADATA } from '@remix-ui/workspace' import { TEMPLATE_NAMES, TEMPLATE_METADATA } from '@remix-ui/workspace'
import { ThemeContext } from '../themeContext' import { ThemeContext } from '../themeContext'
import Carousel from 'react-multi-carousel'
import WorkspaceTemplate from './workspaceTemplate' import WorkspaceTemplate from './workspaceTemplate'
import 'react-multi-carousel/lib/styles.css' import 'react-multi-carousel/lib/styles.css'
import CustomNavButtons from './customNavButtons'
import { appPlatformTypes, platformContext } from '@remix-ui/app' import { appPlatformTypes, platformContext } from '@remix-ui/app'
import { CustomTooltip } from '@remix-ui/helper'
declare global { declare global {
interface Window { interface Window {
@ -19,12 +18,65 @@ interface HomeTabGetStartedProps {
plugin: any plugin: any
} }
type WorkspaceTemplate = {
gsID: string
workspaceTitle: string
description: string
projectLogo: string
templateName: string
}
const workspaceTemplates: WorkspaceTemplate[] = [
{
gsID: 'sUTLogo',
workspaceTitle: 'Start Coding',
description: 'Create a new project using this template.',
projectLogo: 'assets/img/remixverticaltextLogo.png',
templateName: 'remixDefault',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'Circom',
description: 'Create a new ZK Project with Circom using this template.',
projectLogo: 'assets/img/circom.webp',
templateName: 'semaphore',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'Uniswap',
description: 'Create a new MultiSig wallet using this template.',
projectLogo: 'assets/img/gnosissafeLogo.png',
templateName: 'uniswapV4Template',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'ERC20',
description: 'Create a new ERC20 token using this template.',
projectLogo: 'assets/img/oxprojectLogo.png',
templateName: 'ozerc20',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'NFT / ERC721',
description: 'Create a new ERC721 token using this template.',
projectLogo: 'assets/img/openzeppelinLogo.png',
templateName: 'ozerc721',
},
{
gsID: 'sUTLogo',
workspaceTitle: 'MultiSig',
description: 'Create a new MultiSig wallet using this template.',
projectLogo: 'assets/img/gnosissafeLogo.png',
templateName: 'gnosisSafeMultisig',
},
]
function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) { function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
const platform = useContext(platformContext) const platform = useContext(platformContext)
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
const intl = useIntl()
const carouselRef = useRef<any>({}) const carouselRef = useRef<any>({})
const carouselRefDiv = useRef(null) const carouselRefDiv = useRef(null)
const intl = useIntl()
useEffect(() => { useEffect(() => {
document.addEventListener('wheel', handleScroll) document.addEventListener('wheel', handleScroll)
@ -61,8 +113,7 @@ function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
} }
const createWorkspace = async (templateName) => { const createWorkspace = async (templateName) => {
if (platform === appPlatformTypes.desktop) {
if (platform === appPlatformTypes.desktop){
await plugin.call('remix-templates', 'loadTemplateInNewWindow', templateName) await plugin.call('remix-templates', 'loadTemplateInNewWindow', templateName)
return return
} }
@ -92,90 +143,45 @@ function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
return ( return (
<div className="pl-2" id="hTGetStartedSection"> <div className="pl-2" id="hTGetStartedSection">
<label style={{ fontSize: '1.2rem' }}> <label className="pt-3" style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.projectTemplates" /> <FormattedMessage id="home.projectTemplates" />
</label> </label>
<div ref={carouselRefDiv} className="w-100 d-flex flex-column"> <div ref={carouselRefDiv} className="w-100 d-flex flex-column pt-1">
<ThemeContext.Provider value={themeFilter}> <ThemeContext.Provider value={themeFilter}>
<Carousel <div className="pt-3">
ref={carouselRef} <div className="d-flex flex-row align-items-center mb-3 flex-nowrap">
focusOnSelect={true} {workspaceTemplates.slice(0, 3).map((template, index) => (
customButtonGroup={<CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} parent={carouselRef} />} <CustomTooltip tooltipText={template.description} tooltipId={template.gsID} tooltipClasses="text-nowrap" tooltipTextClasses="border bg-light text-dark p-1 pr-3" placement="top-start" key={`${template.gsID}-${template.workspaceTitle}-${index}`}>
arrows={false} <button
swipeable={false} key={index}
draggable={true} className={index === 0 ? 'btn btn-primary border p-2 text-nowrap mr-3' : index === workspaceTemplates.length - 1 ? 'btn border p-2 text-nowrap mr-2' : 'btn border p-2 text-nowrap mr-3'}
showDots={false} onClick={(e) => {
responsive={{ createWorkspace(template.templateName)
superLargeDesktop: { }}
breakpoint: { max: 4000, min: 3000 }, data-id={`homeTabGetStarted${template.templateName}`}
items: 5 >
}, {template.workspaceTitle}
desktop: { </button>
breakpoint: { max: 3000, min: 1024 }, </CustomTooltip>
items: 5, ))}
partialVisibilityGutter: 0 </div>
} <div className="d-flex flex-row align-items-center mb-2 flex-nowrap">
}} {workspaceTemplates.slice(3, workspaceTemplates.length).map((template, index) => (
renderButtonGroupOutside={true} <CustomTooltip tooltipText={template.description} tooltipId={template.gsID} tooltipClasses="text-nowrap" tooltipTextClasses="border bg-light text-dark p-1 pr-3" placement="bottom-start" key={`${template.gsID}-${template.workspaceTitle}-${index}`}>
ssr={true} // means to render carousel on server-side. <button
keyBoardControl={true} key={index}
containerClass="carousel-container" className={'btn border p-2 text-nowrap mr-3'}
deviceType={'desktop'} onClick={() => {
itemClass="w-100" createWorkspace(template.templateName)
> }}
<WorkspaceTemplate data-id={`homeTabGetStarted${template.workspaceTitle}`}
gsID="sUTLogo" >
workspaceTitle="MultiSig" {template.workspaceTitle}
description={ </button>
intl.formatMessage({ id: 'home.gnosisSafeMultisigTemplateDesc' }) </CustomTooltip>
} ))}
projectLogo="assets/img/gnosissafeLogo.png" </div>
callback={() => createWorkspace("gnosisSafeMultisig")} </div>
/>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="ERC20"
description={
intl.formatMessage({ id: 'home.zeroxErc20TemplateDesc' })
}
projectLogo="assets/img/oxprojectLogo.png"
callback={() => createWorkspace("zeroxErc20")}
/>
<WorkspaceTemplate
gsID="sourcifyLogo"
workspaceTitle="ERC20"
description={intl.formatMessage({ id: 'home.ozerc20TemplateDesc' })}
projectLogo="assets/img/openzeppelinLogo.png"
callback={() => createWorkspace('ozerc20')}
/>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="ERC721"
description={intl.formatMessage({
id: 'home.ozerc721TemplateDesc'
})}
projectLogo="assets/img/openzeppelinLogo.png"
callback={() => createWorkspace("ozerc721")}
/>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="ERC1155"
description={intl.formatMessage({
id: 'home.ozerc1155TemplateDesc'
})}
projectLogo="assets/img/openzeppelinLogo.png"
callback={() => createWorkspace("ozerc1155")}
/>
<WorkspaceTemplate
gsID="solhintLogo"
workspaceTitle="Basic"
description={intl.formatMessage({
id: 'home.remixDefaultTemplateDesc'
})}
projectLogo="assets/img/remixverticaltextLogo.png"
callback={() => createWorkspace("remixDefault")}
/>
</Carousel>
</ThemeContext.Provider> </ThemeContext.Provider>
</div> </div>
</div> </div>

@ -3,8 +3,55 @@
import React, { useEffect, useState, useRef, useContext } from 'react' import React, { useEffect, useState, useRef, useContext } from 'react'
import { FormattedMessage, useIntl } from 'react-intl' import { FormattedMessage, useIntl } from 'react-intl'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import { Placement } from 'react-bootstrap/esm/Overlay'
const _paq = (window._paq = window._paq || []) // eslint-disable-line const _paq = (window._paq = window._paq || []) // eslint-disable-line
type HometabIconSection = {
textToolip: JSX.Element
urlLink: string
iconClass: 'fa-youtube'|'fa-x-twitter'|'fa-linkedin'|'fa-medium'|'fa-discord'
placement: Placement
matomoTrackingEntry: string[]
}
const iconButtons: HometabIconSection[] = [
{
textToolip: <FormattedMessage id="home.remixYoutubePlaylist" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialMedia', 'youtube'],
urlLink: 'https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA',
iconClass: 'fa-youtube',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.remixTwitterProfile" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialMedia', 'twitter'],
urlLink: 'https://twitter.com/EthereumRemix',
iconClass: 'fa-x-twitter',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.remixLinkedinProfile" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'linkedin'],
urlLink: 'https://www.linkedin.com/company/ethereum-remix/',
iconClass: 'fa-linkedin',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.remixMediumPosts" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'medium'],
urlLink: 'https://medium.com/remix-ide',
iconClass: 'fa-medium',
placement: 'top'
},
{
textToolip: <FormattedMessage id="home.joinUsOnDiscord" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'discord'],
urlLink: 'https://discord.gg/mh9hFCKkEq',
iconClass: 'fa-discord',
placement: 'top'
}
]
function HomeTabTitle() { function HomeTabTitle() {
useEffect(() => { useEffect(() => {
document.addEventListener('keyup', (e) => handleSearchKeyDown(e)) document.addEventListener('keyup', (e) => handleSearchKeyDown(e))
@ -64,82 +111,25 @@ function HomeTabTitle() {
</div> </div>
</div> </div>
<span className="d-flex flex-nowrap align-self-end"> <span className="d-flex flex-nowrap align-self-end">
<CustomTooltip {iconButtons.map((button, index) => (
placement={'top'} <CustomTooltip
tooltipId="overlay-tooltip" key={index}
tooltipClasses="text-nowrap" placement={button.placement}
tooltipText={<FormattedMessage id="home.remixYoutubePlaylist" />} tooltipId="overlay-tooltip"
tooltipTextClasses="border bg-light text-dark p-1 pr-3" tooltipClasses="text-nowrap"
> tooltipText={button.textToolip}
<button tooltipTextClasses="border bg-light text-dark p-1 pr-3"
onClick={() => { >
openLink('https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA') <button
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'youtube']) key={index}
}} onClick={() => {
className="border-0 px-1 h-100 btn fab fa-youtube" openLink(button.urlLink)
></button> _paq.push(button.matomoTrackingEntry)
</CustomTooltip> }}
<CustomTooltip className={`border-0 h-100 pl-1 pr-0 btn fab ${button.iconClass}`}
placement={'top'} ></button>
tooltipId="overlay-tooltip" </CustomTooltip>
tooltipClasses="text-nowrap" ))}
tooltipText={<FormattedMessage id="home.remixTwitterProfile" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://twitter.com/EthereumRemix')
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'twitter'])
}}
className="border-0 px-1 h-100 btn fab fa-x-twitter"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.remixLinkedinProfile" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://www.linkedin.com/company/ethereum-remix/')
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'linkedin'])
}}
className="border-0 px-1 h-100 btn fab fa-linkedin"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.remixMediumPosts" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://medium.com/remix-ide')
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'medium'])
}}
className="border-0 h-100 px-1 btn fab fa-medium"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.joinUsOnDiscord" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://discord.gg/mh9hFCKkEq')
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'discord'])
}}
className="border-0 h-100 pl-1 pr-0 btn fab fa-discord"
></button>
</CustomTooltip>
</span> </span>
</div> </div>
<b className="py-1 text-dark" style={{ fontStyle: 'italic' }}> <b className="py-1 text-dark" style={{ fontStyle: 'italic' }}>
@ -149,27 +139,19 @@ function HomeTabTitle() {
<a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'webSite'])} target="__blank" href="https://remix-project.org"> <a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'webSite'])} target="__blank" href="https://remix-project.org">
<FormattedMessage id="home.website" /> <FormattedMessage id="home.website" />
</a> </a>
<a {/* <a
className="pl-2 remixui_home_text" className="pl-2 remixui_home_text"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'documentation'])} onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'documentation'])}
target="__blank" target="__blank"
href="https://remix-ide.readthedocs.io/en/latest" href="https://remix-ide.readthedocs.io/en/latest"
> >
<FormattedMessage id="home.documentation" /> <FormattedMessage id="home.documentation" />
</a> </a> */}
<a
className="pl-2 remixui_home_text"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'remixPlugin'])}
target="__blank"
href="https://remix-plugin-docs.readthedocs.io/en/latest/"
>
<FormattedMessage id="home.remixPlugin" />
</a>
<a <a
className="pl-2 remixui_home_text" className="pl-2 remixui_home_text"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'remixDesktop'])} onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'remixDesktop'])}
target="__blank" target="__blank"
href="https://github.com/ethereum/remix-desktop/releases" href="https://github.com/remix-project-org/remix-desktop-insiders"
> >
<FormattedMessage id="home.remixDesktop" /> <FormattedMessage id="home.remixDesktop" />
</a> </a>

@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'
import { Dropdown, DropdownButton } from 'react-bootstrap' import { Dropdown, DropdownButton } from 'react-bootstrap'
import DropdownItem from 'react-bootstrap/DropdownItem' import DropdownItem from 'react-bootstrap/DropdownItem'
import { localeLang } from './types/carouselTypes' import { localeLang } from './types/carouselTypes'
import { FormattedMessage } from 'react-intl'
export function LanguageOptions({ plugin }: { plugin: any }) { export function LanguageOptions({ plugin }: { plugin: any }) {
const [langOptions, setLangOptions] = useState<string>() const [langOptions, setLangOptions] = useState<string>()
@ -24,7 +25,10 @@ export function LanguageOptions({ plugin }: { plugin: any }) {
return ( return (
<> <>
<div style={{ position: 'absolute', right: "1rem", paddingTop: "0.4rem" }}> <div className="d-flex justify-content-between w-100 align-items-center pt-4">
<label style={{ fontSize: '1.2rem' }} className="ml-2 pb-0 mb-0">
<FormattedMessage id="home.featured" />
</label>
<Dropdown> <Dropdown>
<Dropdown.Toggle title={langOptions} id="languagedropdown" size="sm" style={{ backgroundColor: 'var(--secondary)', color: 'var(--text)' }}> <Dropdown.Toggle title={langOptions} id="languagedropdown" size="sm" style={{ backgroundColor: 'var(--secondary)', color: 'var(--text)' }}>
{langOptions} {langOptions}

@ -76,7 +76,7 @@
text-align: left; text-align: left;
} }
.remixui_home_cursorStyle { .remixui_home_cursorStyle {
cursor: pointer; cursor: pointer;
font-weight: 900; font-weight: 900;
} }
.remixui_home_envButton { .remixui_home_envButton {
@ -114,3 +114,36 @@
background-color: var(--body-bg); background-color: var(--body-bg);
color: var(--text); color: var(--text);
} }
.remixui_recentworkspace {
height: 2.4rem;
}
.remixui_carouselImage {
flex: 1;
height: 20rem;
width: 20rem;
}
.remixui_carouselbox {
min-height: 25.12rem;
}
.remix_ui-carousel-container {
container: remix_ui-carousel-container / inline-size;
}
@container remix_ui-carousel-container (inline-size < 700px) {
.remix_ui-carouselbox {
min-height: 20rem;
margin-right: 10rem;
}
.remixui_carouselImage {
height: 12.5rem;
width: 12.5rem;
}
.remixui_recentworkspace {
height: 0.2rem;
}
}

@ -32,6 +32,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}>({ }>({
themeQuality: themes.light themeQuality: themes.light
}) })
const [carouselWidth, setCarouselWidth] = useState(65)
useEffect(() => { useEffect(() => {
plugin.call('theme', 'currentTheme').then((theme) => { plugin.call('theme', 'currentTheme').then((theme) => {
@ -54,23 +55,39 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}) })
}, []) }, [])
useEffect(() => {
const checkResolution = () => {
const width = window.innerWidth
const height = window.innerHeight
if (height < 781 && width < 1150) {
setCarouselWidth(75)
}
}
checkResolution()
return () => {
checkResolution()
}
}, [])
// border-right
return ( return (
<div className="d-flex flex-column w-100" data-id="remixUIHTAll"> <div className="d-flex flex-column w-100 h-100" data-id="remixUIHTAll">
<ThemeContext.Provider value={state.themeQuality}> <ThemeContext.Provider value={state.themeQuality}>
<div className="d-flex flex-row w-100 custom_home_bg"> <div className="d-flex flex-row w-100 h-100 custom_home_bg">
<div className="px-2 pl-3 justify-content-start d-flex border-right flex-column" id="remixUIHTLeft" style={{ width: 'inherit' }}> <div className="px-2 pl-3 justify-content-start border-right d-flex flex-column" id="remixUIHTLeft" style={{ width: `${100 - carouselWidth}%` }}>
<HomeTabTitle /> <HomeTabTitle />
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
{!(platform === appPlatformTypes.desktop) ? {!(platform === appPlatformTypes.desktop) ?
<HomeTabFile plugin={plugin} />: <HomeTabFile plugin={plugin} />:
<HomeTabFileElectron plugin={plugin}></HomeTabFileElectron>} <HomeTabFileElectron plugin={plugin}></HomeTabFileElectron>}
<HomeTabLearn plugin={plugin} /> {/* <HomeTabLearn plugin={plugin} /> */}
</div> </div>
<div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{ width: '65%' }} id="remixUIHTRight"> <div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{ width: `${carouselWidth}%` }} id="remixUIHTRight">
<LanguageOptions plugin={plugin}/> <LanguageOptions plugin={plugin}/>
<HomeTabFeatured></HomeTabFeatured> <HomeTabFeatured></HomeTabFeatured>
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
<HomeTabFeaturedPlugins plugin={plugin}></HomeTabFeaturedPlugins> <HomeTabFeaturedPlugins plugin={plugin}></HomeTabFeaturedPlugins>
<HomeTabScamAlert></HomeTabScamAlert>
</div> </div>
</div> </div>
</ThemeContext.Provider> </ThemeContext.Provider>

@ -1,4 +1,4 @@
export { default as RemixPluginPanel } from './lib/plugins/remix-ui-panel' export { default as RemixPluginPanel } from './lib/plugins/remix-ui-panel'
export { default as RemixUIMainPanel } from './lib/main/main-panel' export { default as RemixUIMainPanel } from './lib/main/main-panel'
export { PluginRecord } from './lib/types' export { PluginRecord } from './lib/types'
export { default as RemixUIPanelHeader } from './lib/plugins/panel-header' export { default as RemixUIPanelHeader } from './lib/plugins/panel-header'

@ -110,3 +110,7 @@ iframe {
.highlight { .highlight {
animation: highlight 2s forwards; animation: highlight 2s forwards;
} }
.remixui_height {
height: 97vh;
}

@ -1,4 +1,5 @@
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
import EventEmitter from 'events'
export type PluginRecord = { export type PluginRecord = {
profile: Profile profile: Profile
@ -8,3 +9,22 @@ export type PluginRecord = {
class?: string class?: string
minimized?: boolean minimized?: boolean
} }
export interface PluginProfile {
name: string
displayName: string
description: string
keywords?: string[]
icon?: string
url?: string
methods?: string[]
events?: string[]
version?: string
}
export interface StatusBarInterface extends Plugin {
htmlElement: HTMLDivElement
events: EventEmitter
dispatch: React.Dispatch<any>
setDispatch(dispatch: React.Dispatch<any>): void
}

@ -1,6 +1,7 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import parse from 'html-react-parser'; import parse from 'html-react-parser'
const _paq = (window._paq = window._paq || [])
interface SolScanTableProps { interface SolScanTableProps {
scanDetails: Record<string, any>[], scanDetails: Record<string, any>[],
@ -13,7 +14,13 @@ export function SolScanTable(props: SolScanTableProps) {
return ( return (
<> <>
<p>Scanning successful! <b>{scanDetails.length} warnings </b> found for file: <b>{fileName}</b></p> <p>Scanning successful! <b>{scanDetails.length} warnings </b> found for file: <b>{fileName}</b></p>
<p>See the warning details below. For more details, <a href="https://solidityscan.com/signup" target='blank'>Go to SolidityScan</a></p> <p>See the warning details below. For more details,&nbsp;
<a href="https://solidityscan.com/signup"
target='_blank'
onClick={() => _paq.push(['trackEvent', 'udapp', 'solidityScan', 'goToSolidityScan'])}>
go to SolidityScan.
</a>
</p>
<table className="table table-bordered table-hover"> <table className="table table-bordered table-hover">
<thead> <thead>
<tr> <tr>

@ -304,7 +304,13 @@ export function UniversalDappUI(props: UdappProps) {
title: <FormattedMessage id="udapp.solScan.modalTitle" />, title: <FormattedMessage id="udapp.solScan.modalTitle" />,
message: <div className='d-flex flex-column'> message: <div className='d-flex flex-column'>
<span><FormattedMessage id="udapp.solScan.modalMessage" /> <span><FormattedMessage id="udapp.solScan.modalMessage" />
<a href={'https://solidityscan.com'} target="_blank" >Learn more</a></span><br/> <a href={'https://solidityscan.com'}
target="_blank"
onClick={() => _paq.push(['trackEvent', 'udapp', 'solidityScan', 'learnMore'])}>
Learn more
</a>
</span>
<br/>
<FormattedMessage id="udapp.solScan.likeToContinue" /> <FormattedMessage id="udapp.solScan.likeToContinue" />
</div>, </div>,
okLabel: <FormattedMessage id="udapp.solScan.modalOkLabel" />, okLabel: <FormattedMessage id="udapp.solScan.modalOkLabel" />,

@ -29,16 +29,19 @@ export default function SolidityCompile({ contractProperties, selectedContract,
</span> </span>
) )
const questionMark = ( const questionMark = (
<span className="remixui_questionMark"> <CustomTooltip
<i tooltipText={intl.formatMessage({
title={intl.formatMessage({ id: `solidity.${propertyName}`,
id: `solidity.${propertyName}`, defaultMessage: help[propertyName]
defaultMessage: help[propertyName] })}
})} >
className="fas fa-question-circle" <span className="remixui_questionMark">
aria-hidden="true" <i
></i> className="fas fa-info-circle"
</span> aria-hidden="true"
></i>
</span>
</CustomTooltip>
) )
return ( return (

@ -77,7 +77,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
key={keyPath} key={keyPath}
label={ label={
<div className="d-flex mt-2 flex-row remixui_label_item"> <div className="d-flex mt-2 flex-row remixui_label_item">
<label className="small font-weight-bold pr-1 remixui_label_key">{key}:</label> <label className="font-weight-bold pr-1 remixui_label_key">{key}:</label>
<label className="m-0 remixui_label_value">{typeof data.self === 'boolean' ? `${data.self}` : data.self}</label> <label className="m-0 remixui_label_value">{typeof data.self === 'boolean' ? `${data.self}` : data.self}</label>
</div> </div>
} }
@ -94,7 +94,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
key={keyPath} key={keyPath}
label={ label={
<div className="d-flex mt-2 flex-row remixui_label_item"> <div className="d-flex mt-2 flex-row remixui_label_item">
<label className="small font-weight-bold pr-1 remixui_label_key">{key}:</label> <label className="font-weight-bold pr-1 remixui_label_key">{key}:</label>
<label className="m-0 remixui_label_value">{typeof data.self === 'boolean' ? `${data.self}` : data.self}</label> <label className="m-0 remixui_label_value">{typeof data.self === 'boolean' ? `${data.self}` : data.self}</label>
</div> </div>
} }
@ -311,25 +311,25 @@ export const ContractSelection = (props: ContractSelectionProps) => {
</CustomTooltip> </CustomTooltip>
</button> </button>
<button <CustomTooltip
data-id="compilation-details" placement={'right-end'}
className="btn btn-secondary btn-block" tooltipId="CompilationDetailsTooltip"
onClick={async () => { tooltipClasses="text-nowrap"
details() tooltipText={<FormattedMessage id="solidity.displayContractDetails" />}
await (api as any).call('compilationDetails', 'showDetails', payload)
}}
> >
<CustomTooltip <button
placement={'auto-end'} data-id="compilation-details"
tooltipId="CompilationDetailsTooltip" className="btn btn-secondary btn-block"
tooltipClasses="text-nowrap" onClick={async () => {
tooltipText={<FormattedMessage id="solidity.displayContractDetails" />} details()
await (api as any).call('compilationDetails', 'showDetails', payload)
}}
> >
<span> <span>
<FormattedMessage id="solidity.compilationDetails" /> <FormattedMessage id="solidity.compilationDetails" />
</span> </span>
</CustomTooltip> </button>
</button> </CustomTooltip>
{/* Copy to Clipboard */} {/* Copy to Clipboard */}
<div className="remixui_contractHelperButtons"> <div className="remixui_contractHelperButtons">
<div className="input-group"> <div className="input-group">

@ -0,0 +1,23 @@
remixui_statusbar:hover {
cursor: pointer;
}
.remixui_statusbar_gitstatus
.remixui_statusbar_gitstatus:hover {
cursor: pointer;
}
/**
* approximately same height with vscode statusbar
**/
.remixui_statusbar_height {
height: 21px;
}
.remixui_statusbar_activelink {
text-decoration: none;
}
.remixui_statusbar_activelink:active {
color: var(--danger);
}

@ -0,0 +1,2 @@
export * from './lib/remixui-statusbar-panel'
export { StatusBarInterface } from './lib/types'

@ -0,0 +1,44 @@
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { StatusBar } from 'apps/remix-ide/src/app/components/status-bar'
import { CustomTooltip } from '@remix-ui/helper'
import React, { useEffect, useState } from 'react'
interface AIStatusProps {
plugin: StatusBar
isAiActive: boolean
setIsAiActive: (isAiActive: boolean) => void
aiActive: () => Promise<any>
}
export default function AIStatus(props: AIStatusProps) {
const [copilotActive, setCopilotActive] = useState(false)
useEffect(() => {
const run = async () => {
props.plugin.on('fileManager', 'currentFileChanged', async (isAiActive) => {
const aiActivate = await props.plugin.call('settings', 'get', 'settings/copilot/suggest/activate')
setCopilotActive(aiActivate)
})
}
run()
}, [props.plugin.isAiActive, props.isAiActive])
useEffect(() => {
const run = async () => {
props.plugin.on('settings', 'copilotChoiceUpdated', async (isChecked) => {
await props.plugin.isAIActive()
setCopilotActive(isChecked)
})
}
run()
}, [props.plugin.isAiActive])
return (
<CustomTooltip
tooltipText={copilotActive ? "Remix Copilot activated" : "Remix Copilot disabled."}
>
<div className="d-flex flex-row pr-2 text-white justify-content-center align-items-center">
<span className={copilotActive === false ? "fa-regular fa-microchip-ai ml-1 text-danger" : "fa-regular fa-microchip-ai ml-1"}></span>
<span className={copilotActive === false ? "small mx-1 text-danger semi-bold" : "small mx-1 semi-bold" }>Remix Copilot</span>
</div>
</CustomTooltip>
)
}

@ -0,0 +1,88 @@
import React, { useEffect, Dispatch } from 'react'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { StatusBar } from 'apps/remix-ide/src/app/components/status-bar'
import '../../css/statusbar.css'
import { CustomTooltip } from '@remix-ui/helper'
export interface GitStatusProps {
plugin: StatusBar
gitBranchName: string
setGitBranchName: Dispatch<React.SetStateAction<string>>
}
export default function GitStatus({ plugin, gitBranchName, setGitBranchName }: GitStatusProps) {
useEffect(() => {
const run = async () => {
plugin.on('filePanel', 'setWorkspace', async (workspace) => {
const isGit = await plugin.call('fileManager', 'isGitRepo')
if (isGit) {
setGitBranchName(workspace.name)
} else {
setGitBranchName('Not a git repo')
}
})
}
run()
}, [gitBranchName, plugin.isGitRepo])
useEffect(() => {
const run = async () => {
plugin.on('filePanel', 'workspaceInitializationCompleted', async () => {
const isGit = await plugin.call('fileManager', 'isGitRepo')
if (isGit) {
const workspace = localStorage.getItem('currentWorkspace')
setGitBranchName(workspace)
} else {
setGitBranchName('Not a git repo')
}
})
}
run()
}, [gitBranchName, plugin.isGitRepo])
useEffect(() => {
const run = async () => {
plugin.on('dGitProvider', 'init', async () => {
const isGit = await plugin.call('fileManager', 'isGitRepo')
if (isGit) {
const workspace = localStorage.getItem('currentWorkspace')
setGitBranchName(workspace)
}
})
}
run()
}, [gitBranchName, plugin.isGitRepo])
const lightDgitUp = async () => {
const isActive = await plugin.call('manager', 'isActive', 'dgit')
const isGit = await plugin.call('fileManager', 'isGitRepo')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
if (gitBranchName.length > 0 && isGit) {
plugin.verticalIcons.select('dgit')
}
}
const initializeNewGitRepo = async () => {
await plugin.call('dGitProvider', 'init')
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
// plugin.verticalIcons.select('dgit')
}
return (
<CustomTooltip
tooltipText={`${gitBranchName === 'Not a git repo' ? 'Initialize as a git repo' : gitBranchName} (Git)`}
>
<div
className="d-flex flex-row pl-3 text-white justify-content-center align-items-center remixui_statusbar_gitstatus"
onClick={async () => await lightDgitUp()}
>
{gitBranchName.length > 0 && gitBranchName !== 'Not a git repo' ? <span className="fa-regular fa-code-branch ml-1"></span>
: <span className=" ml-1" onClick={initializeNewGitRepo}> Initialize as git repo</span>}
{gitBranchName.length > 0 && gitBranchName !== 'Not a git repo' && <span className="ml-1">{gitBranchName}</span>}
{gitBranchName.length > 0 && gitBranchName !== 'Not a git repo' && <span className="fa-solid fa-arrows-rotate fa-1 ml-1"></span>}
</div>
</CustomTooltip>
)
}

@ -0,0 +1,27 @@
import React from 'react'
import { FormattedMessage } from 'react-intl'
import { ExtendedRefs, ReferenceType } from '@floating-ui/react'
import { CustomTooltip } from '@remix-ui/helper'
export interface ScamAlertStatusProps {
refs: ExtendedRefs<ReferenceType>
getReferenceProps: (userProps?: React.HTMLProps<HTMLElement> | undefined) => Record<string, unknown>
}
export default function ScamAlertStatus ({ refs, getReferenceProps }: ScamAlertStatusProps) {
return (
<>
<CustomTooltip
tooltipText={"Scam Alerts"}
>
<div className="mr-2 d-flex align-items-center justify-content-center" id="hTScamAlertSection" ref={refs.setReference} {...getReferenceProps()}>
<span className="pr-2 far fa-exclamation-triangle text-white"></span>
<span className="text-white font-semibold small">
<FormattedMessage id="home.scamAlert" />
</span>
</div>
</CustomTooltip>
</>
)
}

@ -0,0 +1,48 @@
import { ExtendedRefs, ReferenceType } from '@floating-ui/react'
import React, { CSSProperties } from 'react'
import { FormattedMessage } from 'react-intl'
import { ScamAlert } from '../remixui-statusbar-panel'
import '../../css/statusbar.css'
const _paq = (window._paq = window._paq || []) // eslint-disable-line
export interface ScamDetailsProps {
refs: ExtendedRefs<ReferenceType>
floatStyle: CSSProperties
getFloatingProps: (userProps?: React.HTMLProps<HTMLElement> | undefined) => Record<string, unknown>
scamAlerts: ScamAlert[]
}
export default function ScamDetails ({ refs, floatStyle, scamAlerts }: ScamDetailsProps) {
return (
<div
ref={refs.setFloating}
style={ floatStyle }
className="px-1 ml-1 mb-1 d-flex w-25 alert alert-danger border border-danger"
>
<span className="align-self-center pl-4 mt-1">
<i style={{ fontSize: 'xxx-large', fontWeight: 'lighter' }} className="pr-2 far text-danger fa-exclamation-triangle"></i>
</span>
<div className="d-flex flex-column text-danger">
{scamAlerts && scamAlerts.map((alert, index) => (
<span className="pl-4 mt-1" key={`${alert.url}${index}`}>
{alert.url.length < 1 ? <FormattedMessage id={`home.scamAlertText${index + 1}`} defaultMessage={alert.message} />
: (<><FormattedMessage id={`home.scamAlertText${index + 1}`} defaultMessage={alert.message} /> : &nbsp;
<a
className={`remixui_home_text text-decoration-none ${index === 1 ? 'pl-2' : ''}`}
onClick={() => {
index === 1 && _paq.push(['trackEvent', 'hometab', 'scamAlert', 'learnMore'])
index === 2 && _paq.push(['trackEvent', 'hometab', 'scamAlert', 'safetyTips'])
}}
target="__blank"
href={scamAlerts[index].url}
>
<FormattedMessage id="home.here" defaultMessage={scamAlerts[index].message} />
</a></>)}
</span>
))}
</div>
</div>
)
}

@ -0,0 +1,86 @@
import React, { useEffect, useState } from 'react'
import GitStatus from './components/gitStatus'
import AIStatus from './components/aiStatus'
import ScamAlertStatus from './components/scamAlertStatus'
import ScamDetails from './components/scamDetails'
import { FloatingFocusManager, autoUpdate, flip, offset, shift, size, useClick, useDismiss, useFloating, useInteractions, useRole } from '@floating-ui/react'
import axios from 'axios'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { StatusBar } from 'apps/remix-ide/src/app/components/status-bar'
export interface RemixUIStatusBarProps {
statusBarPlugin: StatusBar
}
export type ScamAlert = {
message: string
url: string
}
export function RemixUIStatusBar({ statusBarPlugin }: RemixUIStatusBarProps) {
const [showScamDetails, setShowScamDetails] = useState(false)
const [scamAlerts, setScamAlerts] = useState<ScamAlert[]>([])
const [gitBranchName, setGitBranchName] = useState('')
const [isAiActive, setIsAiActive] = useState(false)
const { refs, context, floatingStyles } = useFloating({
open: showScamDetails,
onOpenChange: setShowScamDetails,
middleware: [offset(10), flip({ fallbackAxisSideDirection: 'end' }), shift({
mainAxis: true, padding: 10
}), size({
apply({ availableWidth, availableHeight, elements, ...state }) {
console.log(state)
Object.assign(elements.floating.style, {
maxWidth: `${availableWidth}`,
maxHeight: `auto`
})
}
})],
whileElementsMounted: autoUpdate,
})
const click = useClick(context)
const dismiss = useDismiss(context)
const role = useRole(context)
const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role])
useEffect(() => {
const abortController = new AbortController()
const signal = abortController.signal
async function getScamAlerts() {
const response = await axios.get('https://raw.githubusercontent.com/remix-project-org/remix-dynamics/main/ide/scam-alerts.json', { signal })
if (signal.aborted) return
setScamAlerts(response.data.alerts)
}
getScamAlerts()
return () => {
abortController.abort()
}
}, [])
const lightAiUp = async () => {
const aiActive = await statusBarPlugin.call('settings', 'get', 'settings/copilot/suggest/activate')
if (!aiActive) return
setIsAiActive(aiActive)
return aiActive
}
return (
<>
{showScamDetails && (
<FloatingFocusManager context={context} modal={false}>
<ScamDetails refs={refs} floatStyle={{ ...floatingStyles, minHeight: 'auto', alignContent: 'center', paddingRight: '0.5rem' }} getFloatingProps={getFloatingProps} scamAlerts={scamAlerts} />
</FloatingFocusManager>
)}
<div className="d-flex remixui_statusbar_height flex-row bg-primary justify-content-between align-items-center">
<div className="remixui_statusbar remixui_statusbar_gitstatus">
<GitStatus plugin={statusBarPlugin} gitBranchName={gitBranchName} setGitBranchName={setGitBranchName} />
</div>
<div className="remixui_statusbar"></div>
<div className="remixui_statusbar d-flex flex-row">
<ScamAlertStatus refs={refs} getReferenceProps={getReferenceProps} />
<AIStatus plugin={statusBarPlugin} aiActive={lightAiUp} isAiActive={isAiActive} setIsAiActive={setIsAiActive} />
</div>
</div>
</>
)
}

@ -0,0 +1,27 @@
import EventEmitter from 'events'
import { Plugin } from '@remixproject/engine'
import { FilePanelType } from '@remix-ui/workspace'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { VerticalIcons } from 'apps/remix-ide/src/app/components/vertical-icons'
export interface PluginProfile {
name: string
displayName: string
description: string
keywords?: string[]
icon?: string
url?: string
methods?: string[]
events?: string[]
version?: string
}
export interface StatusBarInterface extends Plugin {
htmlElement: HTMLDivElement
events: EventEmitter
dispatch: React.Dispatch<any>
filePanel: FilePanelType
verticalIcons: VerticalIcons
setDispatch(dispatch: React.Dispatch<any>): void
getGitBranchName: () => Promise<any>
currentWorkspaceName: string
}

@ -10,7 +10,7 @@ const _paq = (window._paq = window._paq || [])
/* eslint-disable-next-line */ /* eslint-disable-next-line */
export interface TabsUIProps { export interface TabsUIProps {
tabs: Array<any> tabs: Array<Tab>
plugin: Plugin plugin: Plugin
onSelect: (index: number) => void onSelect: (index: number) => void
onClose: (index: number) => void onClose: (index: number) => void
@ -19,6 +19,15 @@ export interface TabsUIProps {
onReady: (api: any) => void onReady: (api: any) => void
themeQuality: string themeQuality: string
} }
export interface Tab {
id: string
icon: string
iconClass: string
name: string
title: string
tooltip: string
}
export interface TabsUIApi { export interface TabsUIApi {
activateTab: (name: string) => void activateTab: (name: string) => void
active: () => string active: () => string
@ -96,7 +105,8 @@ export const TabsUI = (props: TabsUIProps) => {
return <FileDecorationIcons file={{ path: tab.name }} fileDecorations={tabsState.fileDecorations} /> return <FileDecorationIcons file={{ path: tab.name }} fileDecorations={tabsState.fileDecorations} />
} }
const renderTab = (tab, index) => { const renderTab = (tab: Tab, index) => {
const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass
const classNameTab = 'nav-item nav-link d-flex justify-content-center align-items-center px-2 py-1 tab' + (index === currentIndexRef.current ? ' active' : '') const classNameTab = 'nav-item nav-link d-flex justify-content-center align-items-center px-2 py-1 tab' + (index === currentIndexRef.current ? ' active' : '')
const invert = props.themeQuality === 'dark' ? 'invert(1)' : 'invert(0)' const invert = props.themeQuality === 'dark' ? 'invert(1)' : 'invert(0)'

@ -597,7 +597,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
return ( return (
( props.visible && ( props.visible &&
<div style={{ flexGrow: 1 }} className="remix_ui_terminal_panel h-100" ref={panelRef}> <div style={{ flexGrow: 1 }} className="remix_ui_terminal_panel h-100 mb-2" ref={panelRef}>
<div tabIndex={-1} className="remix_ui_terminal_container d-flex h-100 m-0 flex-column" data-id="terminalContainer"> <div tabIndex={-1} className="remix_ui_terminal_container d-flex h-100 m-0 flex-column" data-id="terminalContainer">
{handleAutoComplete()} {handleAutoComplete()}
<div className="position-relative d-flex flex-column-reverse h-100"> <div className="position-relative d-flex flex-column-reverse h-100">

@ -131,7 +131,7 @@ export const Toaster = (props: ToasterProps) => {
{!state.hide && ( {!state.hide && (
<div <div
data-shared="tooltipPopup" data-shared="tooltipPopup"
className={`remixui_tooltip alert alert-info p-2 ${state.hiding ? 'remixui_animateTop' : 'remixui_animateBottom'}`} className={`remixui_tooltip mb-4 alert alert-info p-2 ${state.hiding ? 'remixui_animateTop' : 'remixui_animateBottom'}`}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
> >

@ -0,0 +1,7 @@
.remixui_mouseover {
}
.remixui_mouseover:hover {
cursor: pointer;
}

@ -4,7 +4,7 @@ import { TreeViewItemProps } from '../../types'
import './tree-view-item.css' import './tree-view-item.css'
export const TreeViewItem = (props: TreeViewItemProps) => { export const TreeViewItem = (props: TreeViewItemProps) => {
const { id, children, label, labelClass, expand, iconX = 'fas fa-caret-right', iconY = '', icon, controlBehaviour = false, innerRef, showIcon = true, ...otherProps } = props const { id, children, label, labelClass, expand, iconX = 'fas fa-caret-right', iconY = 'fas fa-caret-down', icon, controlBehaviour = false, innerRef, showIcon = true, ...otherProps } = props
const [isExpanded, setIsExpanded] = useState(false) const [isExpanded, setIsExpanded] = useState(false)
useEffect(() => { useEffect(() => {
@ -12,7 +12,13 @@ export const TreeViewItem = (props: TreeViewItemProps) => {
}, [expand]) }, [expand])
return ( return (
<li ref={innerRef} key={`treeViewLi${id}`} data-id={`treeViewLi${id}`} className="li_tv" {...otherProps}> <li
ref={innerRef}
key={`treeViewLi${id}`}
data-id={`treeViewLi${id}`}
className="li_tv remixui_mouseover"
{...otherProps}
>
<div <div
key={`treeViewDiv${id}`} key={`treeViewDiv${id}`}
data-id={`treeViewDiv${id}`} data-id={`treeViewDiv${id}`}
@ -20,13 +26,17 @@ export const TreeViewItem = (props: TreeViewItemProps) => {
onClick={() => !controlBehaviour && setIsExpanded(!isExpanded)} onClick={() => !controlBehaviour && setIsExpanded(!isExpanded)}
> >
{children && showIcon ? ( {children && showIcon ? (
<div className={isExpanded ? `pl-2 ${iconY}` : `pl-2 ${iconX} caret caret_tv`} style={{ visibility: children ? 'visible' : 'hidden' }}></div> <div className={isExpanded ? `pl-2 ${iconY}` : `pl-2 ${iconX} caret caret_tv`}
style={{ visibility: children ? 'visible' : 'hidden' }}
></div>
) : icon ? ( ) : icon ? (
<div className={`pr-2 pl-2 ${icon} caret caret_tv`}></div> <div className={`pr-2 pl-2 ${icon} caret caret_tv`}></div>
) : null} ) : null}
<span className="w-100 ml-1 pl-2">{label}</span> <span className="w-100 ml-1 pl-2">{label}</span>
</div> </div>
{isExpanded ? children : null} {isExpanded ? <div className="pl-3">
{children}
</div> : null}
</li> </li>
) )
} }

@ -84,7 +84,7 @@
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
} }
.remixui_verticalIconContextcontainer { .remixui_verticalIconContextcontainer {
display: block; display: block;
position: fixed; position: fixed;
@ -105,7 +105,7 @@
.remixui_liitem:hover { .remixui_liitem:hover {
background-color: var(--secondary); background-color: var(--secondary);
} }
.remixui_scrollbar { .remixui_scrollbar {
overflow-y: scroll; overflow-y: scroll;
scrollbar-width: none; /* Firefox hide scrollbar */ scrollbar-width: none; /* Firefox hide scrollbar */
@ -141,6 +141,10 @@
flex-basis: 50px; flex-basis: 50px;
} }
.remixui_icons_height {
height: 97vh;
}
#menuitems { #menuitems {
list-style: none; list-style: none;
margin: 0px; margin: 0px;

@ -69,7 +69,7 @@ const RemixUiVerticalIconsPanel = ({ verticalIconsPlugin, icons }: RemixUiVertic
return ( return (
<div id="iconsP" className="h-100"> <div id="iconsP" className="h-100">
<div className="remixui_icons d-flex flex-column vh-100" ref={iconPanelRef}> <div className="remixui_icons d-flex flex-column remixui_icons_height" ref={iconPanelRef}>
<Home verticalIconPlugin={verticalIconsPlugin} /> <Home verticalIconPlugin={verticalIconsPlugin} />
<div <div
className={ className={

@ -1,4 +1,4 @@
export * from './lib/providers/FileSystemProvider' export * from './lib/providers/FileSystemProvider'
export * from './lib/contexts' export * from './lib/contexts'
export * from './lib/utils/constants' export * from './lib/utils/constants'
export { FileType } from './lib/types/index' export { FileType, FilePanelType } from './lib/types/index'

@ -97,6 +97,7 @@
"@ethereumjs/util": "9.0.3", "@ethereumjs/util": "9.0.3",
"@ethereumjs/vm": "8.0.0", "@ethereumjs/vm": "8.0.0",
"@ethersphere/bee-js": "^3.2.0", "@ethersphere/bee-js": "^3.2.0",
"@floating-ui/react": "^0.26.15",
"@gradio/client": "^0.10.1", "@gradio/client": "^0.10.1",
"@isomorphic-git/lightning-fs": "^4.4.1", "@isomorphic-git/lightning-fs": "^4.4.1",
"@microlink/react-json-view": "^1.23.0", "@microlink/react-json-view": "^1.23.0",

@ -163,6 +163,9 @@
"@remix-ui/solidity-uml-gen": [ "@remix-ui/solidity-uml-gen": [
"libs/remix-ui/solidity-uml-gen/src/index.ts" "libs/remix-ui/solidity-uml-gen/src/index.ts"
], ],
"@remix-ui/statusbar": [
"libs/remix-ui/statusbar/src/index.ts"
],
"@remix-project/ghaction-helper": [ "@remix-project/ghaction-helper": [
"libs/ghaction-helper/src/index.ts" "libs/ghaction-helper/src/index.ts"
], ],

@ -2846,6 +2846,42 @@
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8" resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8"
integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ== integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==
"@floating-ui/core@^1.0.0":
version "1.6.2"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.2.tgz#d37f3e0ac1f1c756c7de45db13303a266226851a"
integrity sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==
dependencies:
"@floating-ui/utils" "^0.2.0"
"@floating-ui/dom@^1.0.0":
version "1.6.5"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.5.tgz#323f065c003f1d3ecf0ff16d2c2c4d38979f4cb9"
integrity sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==
dependencies:
"@floating-ui/core" "^1.0.0"
"@floating-ui/utils" "^0.2.0"
"@floating-ui/react-dom@^2.0.0":
version "2.0.9"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.9.tgz#264ba8b061000baa132b5910f0427a6acf7ad7ce"
integrity sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==
dependencies:
"@floating-ui/dom" "^1.0.0"
"@floating-ui/react@^0.26.15":
version "0.26.15"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.15.tgz#d3103a2c77923749458edb304598b37ea852ef56"
integrity sha512-WKmfLkxTwCm09Dxq4LpjL3EPbZVSp5wvnap1jmculsfnzg2Ag/pCkP+OPyjE5dFMXqX97hsLIqJehboZ5XAHXw==
dependencies:
"@floating-ui/react-dom" "^2.0.0"
"@floating-ui/utils" "^0.2.0"
tabbable "^6.0.0"
"@floating-ui/utils@^0.2.0":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.2.tgz#d8bae93ac8b815b2bd7a98078cf91e2724ef11e5"
integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==
"@formatjs/ecma402-abstract@1.11.7": "@formatjs/ecma402-abstract@1.11.7":
version "1.11.7" version "1.11.7"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.7.tgz#47f1a854f679f813d9baa1ee55adae94880ec706" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.7.tgz#47f1a854f679f813d9baa1ee55adae94880ec706"
@ -27506,6 +27542,11 @@ system-architecture@^0.1.0:
resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d" resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d"
integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA== integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==
tabbable@^6.0.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
tap-out@^2.1.0: tap-out@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/tap-out/-/tap-out-2.1.0.tgz#c093079a915036de8b835bfa3297f14458b15358" resolved "https://registry.yarnpkg.com/tap-out/-/tap-out-2.1.0.tgz#c093079a915036de8b835bfa3297f14458b15358"

Loading…
Cancel
Save