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. 47
      apps/remix-ide-e2e/src/tests/homeTab.test.ts
  8. 3
      apps/remix-ide-e2e/src/tests/url.test.ts
  9. 90
      apps/remix-ide-e2e/src/tests/vyper_api.test.ts
  10. 105
      apps/remix-ide-e2e/src/tests/workspace_git.test.ts
  11. 2
      apps/remix-ide/contracts/ballot.sol
  12. 5
      apps/remix-ide/src/app.js
  13. 3
      apps/remix-ide/src/app/components/preload.tsx
  14. 2
      apps/remix-ide/src/app/components/side-panel.tsx
  15. 106
      apps/remix-ide/src/app/components/status-bar.tsx
  16. 1
      apps/remix-ide/src/app/panels/file-panel.js
  17. 9
      apps/remix-ide/src/app/panels/tab-proxy.js
  18. 1
      apps/remix-ide/src/app/plugins/compile-details.tsx
  19. 11
      apps/remix-ide/src/app/tabs/locales/en/home.json
  20. 2
      apps/remix-ide/src/app/tabs/locales/en/udapp.json
  21. 1
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  22. 2
      apps/remix-ide/src/remixAppManager.js
  23. 19
      apps/remix-ide/src/types/index.d.ts
  24. 32
      apps/vyper/src/app/app.tsx
  25. 23
      apps/vyper/src/app/utils/compiler.tsx
  26. 25
      apps/vyper/src/app/utils/remix-client.tsx
  27. 13
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  28. 5
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  29. 6
      libs/remix-ui/app/src/lib/remix-app/style/remix-app.css
  30. 15
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  31. 27
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  32. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx
  33. 173
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  34. 182
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  35. 156
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  36. 6
      libs/remix-ui/home-tab/src/lib/components/homeTablangOptions.tsx
  37. 33
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css
  38. 31
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  39. 4
      libs/remix-ui/panel/src/lib/plugins/panel.css
  40. 20
      libs/remix-ui/panel/src/lib/types/index.ts
  41. 11
      libs/remix-ui/run-tab/src/lib/components/solScanTable.tsx
  42. 8
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  43. 23
      libs/remix-ui/solidity-compile-details/src/lib/components/solidityCompile.tsx
  44. 32
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  45. 23
      libs/remix-ui/statusbar/src/css/statusbar.css
  46. 2
      libs/remix-ui/statusbar/src/index.ts
  47. 44
      libs/remix-ui/statusbar/src/lib/components/aiStatus.tsx
  48. 88
      libs/remix-ui/statusbar/src/lib/components/gitStatus.tsx
  49. 27
      libs/remix-ui/statusbar/src/lib/components/scamAlertStatus.tsx
  50. 48
      libs/remix-ui/statusbar/src/lib/components/scamDetails.tsx
  51. 86
      libs/remix-ui/statusbar/src/lib/remixui-statusbar-panel.tsx
  52. 27
      libs/remix-ui/statusbar/src/lib/types/index.ts
  53. 14
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  54. 2
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  55. 2
      libs/remix-ui/toaster/src/lib/toaster.tsx
  56. 7
      libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.css
  57. 18
      libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx
  58. 4
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css
  59. 2
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx
  60. 2
      libs/remix-ui/workspace/src/index.ts
  61. 1
      package.json
  62. 3
      tsconfig.paths.json
  63. 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)
.getEditorValue((result) => {
if(result != content.content) {
if (result != content.content) {
browser.setEditorValue(content.content)
}
})

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

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

@ -6,7 +6,6 @@ const checkBrowserIsChrome = function (browser: NightwatchBrowser) {
return browser.browserName.indexOf('chrome') > -1
}
module.exports = {
'@disabled': true,
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'
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
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
.waitForElementVisible('*[data-id="homeTabStartCoding"]')
.click('*[data-id="homeTabStartCoding"]')
.waitForElementVisible('div[data-id="treeViewDivtreeViewItemcontracts/HelloWorld.sol"]')
.click('*[data-path="home"]')
.waitForElementVisible('*[data-id="homeTabNewFile"]')
.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)
}
}

@ -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
.url('http://127.0.0.1:8080/#address=0xdAC17F958D2ee523a2206206994597C13D831ec7&blockscout=eth.blockscout.com')
.refreshPage()
@ -175,6 +175,7 @@ module.exports = {
'code has been loaded')
})
.url('http://127.0.0.1:8080') // refresh without loading the code sample
.pause(2000)
.currentWorkspaceIs('default_workspace')
.execute(() => {
return document.querySelector('[data-id="dropdown-item-code-sample"]') === null

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

@ -159,11 +159,13 @@ module.exports = {
.setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ioedeveloper/test-branch-change')
.click('[data-id="fileSystem-modal-footer-ok-react"]')
.waitForElementPresent('.fa-spinner')
.pause(5000)
.pause(7000)
.waitForElementNotPresent('.fa-spinner')
.waitForElementContainsText('[data-id="workspacesSelect"]', 'test-branch-change')
.waitForElementVisible('[data-id="workspaceGitPanel"]')
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]')
.pause()
.waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev')
.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) {
browser
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/empty')
.waitForElementPresent('[data-id="workspaceGit-origin/empty"]')
.click('[data-id="workspaceGit-origin/empty"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemlibdeep"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]')
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.pause()
.click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/empty')
.waitForElementPresent('[data-id="workspaceGit-origin/empty"]')
.click('[data-id="workspaceGit-origin/empty"]')
.waitForElementNotPresent('[data-id="treeViewDivtreeViewItemlibdeep"]')
.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) {
browser
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/main')
.waitForElementPresent('[data-id="workspaceGit-origin/main"]')
.click('[data-id="workspaceGit-origin/main"]')
.waitForElementVisible('[data-id="updatesubmodules"]')
.click('[data-id="updatesubmodules"]')
.waitForElementPresent('.fa-spinner')
.waitForElementVisible({
selector: '*[data-id="treeViewLitreeViewItem.git"]',
timeout: 240000
})
.pause(2000)
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/main')
.waitForElementPresent('[data-id="workspaceGit-origin/main"]')
.click('[data-id="workspaceGit-origin/main"]')
.waitForElementVisible('[data-id="updatesubmodules"]')
.click('[data-id="updatesubmodules"]')
.waitForElementPresent('.fa-spinner')
.waitForElementVisible({
selector: '*[data-id="treeViewLitreeViewItem.git"]',
timeout: 240000
})
.pause(2000)
// check recursive submodule
.waitForElementVisible('[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"]')
.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"]')
.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"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-recursive/test-branch-submodule-2/submodule2.ts"]')
// check test-branch-submodule-2 submodule
.waitForElementVisible('[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"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]')
.click('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemtest-branch-submodule-2/submodule2.ts"]')
// check libdeep submodule
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep"]')
.waitForElementVisible('[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"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep"]')
.waitForElementVisible('[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"]')
// check libdeep2 submodule
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]')
.waitForElementVisible('[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"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep2"]')
.waitForElementVisible('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]')
.click('[data-id="treeViewDivtreeViewItemlibdeep2/recursive"]')
.waitForElementVisible('[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"]')
},
// 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
.click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
@ -393,11 +396,9 @@ module.exports = {
// GIT WORKSPACE E2E ENDS
tearDown: sauce,
}
const gitmodules = `[submodule "subdemo3"]
path = subdemo3
url = https://github.com/bunsenstraat/empty3

@ -60,7 +60,7 @@ contract Ballot {
!voters[voter].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;
}

@ -9,6 +9,7 @@ import {Web3ProviderModule} from './app/tabs/web3-provider'
import {CompileAndRun} from './app/tabs/compile-and-run'
import {PluginStateLogger} from './app/tabs/state-logger'
import {SidePanel} from './app/components/side-panel'
import {StatusBar} from './app/components/status-bar'
import {HiddenPanel} from './app/components/hidden-panel'
import {PinnedPanel} from './app/components/pinned-panel'
import {VerticalIcons} from './app/components/vertical-icons'
@ -386,10 +387,11 @@ class AppComponent {
const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine)
const filePanel = new FilePanel(appManager)
this.statusBar = new StatusBar(filePanel, this.menuicons)
const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport)
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
const openZeppelinProxy = new OpenZeppelinProxy(blockchain)
@ -471,6 +473,7 @@ class AppComponent {
'pluginStateLogger'
])
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(['pinnedPanel'])
await this.appManager.activatePlugin(['home'])

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

@ -12,7 +12,7 @@ const sidePanel = {
displayName: 'Side Panel',
description: 'Remix IDE side panel',
version: packageJson.version,
methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView']
methods: ['addView', 'removeView', 'currentFocus', 'pinView', 'unPinView', 'focus']
}
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: [
'createNewFile',
'uploadFile',
'echoCall',
'getCurrentWorkspace',
'getAvailableWorkspaceName',
'getWorkspaces',

@ -240,6 +240,11 @@ export class TabProxy extends Plugin {
if ((name.endsWith('.vy') && icon === undefined) || title.includes('Vyper')) {
icon = 'assets/img/vyperLogo2.webp'
}
if (title === 'Solidity Compile Details') {
icon = 'assets/img/solidity.webp'
}
var slash = name.split('/')
const tabPath = slash.reverse()
@ -357,7 +362,9 @@ export class TabProxy extends Plugin {
const onZoomIn = () => this.editor.editorFontSize(1)
const onZoomOut = () => this.editor.editorFontSize(-1)
const onReady = (api) => { this.tabsApi = api }
const onReady = (api) => {
this.tabsApi = api
}
this.dispatch({
plugin: this,

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

@ -29,7 +29,7 @@
"home.dgitPluginDesc": "Add source control to your projects.",
"home.oneClickDappDesc": "Quickly generate smart contract interfaces",
"home.getStarted": "Get Started",
"home.projectTemplates": "Project Templates",
"home.projectTemplates": "Explore. Prototype. Create.",
"home.blankTemplateDesc": "Create an empty workspace.",
"home.remixDefaultTemplateDesc": "Create a workspace with sample files.",
"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.learn": "Learn",
"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.learnEth2Desc": "Interactively learn Solidity beginner concepts.",
"home.remixAdvanced": "Deploying with Libraries",
@ -56,15 +56,16 @@
"home.remixDesktop": "Remix Desktop",
"home.searchDocumentation": "Search Documentation",
"home.files": "Files",
"home.newFile": "New File",
"home.newFile": "New",
"home.startCoding": "Start Coding",
"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.accessFileSystem": "Access File System",
"home.accessFileSystem": "Connect to Local Filesystem",
"home.loadFrom": "Load from",
"home.resources": "Resources",
"home.connectToLocalhost": "Connect to Localhost",
"home.seeAllTutorials": "See all tutorials",
"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.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.modalOkLabel": "Continue",
"udapp.solScan.modalCancelLabel": "Cancel",

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

@ -31,6 +31,7 @@ let requiredModules = [ // services + layout views + system views
'menuicons',
'filePanel',
'terminal',
'statusBar',
'settings',
'pluginManager',
'tabs',
@ -117,6 +118,7 @@ export function isNative(name) {
'solhint',
'solidityUnitTesting',
'layout',
'statusBar',
'notification',
'hardhat-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 {CompilationResult} from '@remixproject/plugin-api'
import { remixClient } from './utils'
import { CompilationResult } from '@remixproject/plugin-api'
// Components
import CompilerButton from './components/CompilerButton'
import WarnRemote from './components/WarnRemote'
import VyperResult from './components/VyperResult'
import LocalUrlInput from './components/LocalUrl'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
import ToggleButton from 'react-bootstrap/ToggleButton'
import Button from 'react-bootstrap/Button'
import Accordion from 'react-bootstrap/Accordion'
import Card from 'react-bootstrap/Card'
import './app.css'
import {CustomTooltip} from '@remix-ui/helper'
import {Form} from 'react-bootstrap'
import {CompileErrorCard} from './components/CompileErrorCard'
import { CustomTooltip } from '@remix-ui/helper'
import { Form } from 'react-bootstrap'
import { CompileErrorCard } from './components/CompileErrorCard'
import CustomAccordionToggle from './components/CustomAccordionToggle'
interface AppState {
@ -89,11 +85,11 @@ const App = () => {
/** Update the environment state value */
function setEnvironment(environment: 'local' | 'remote') {
setState({...state, environment})
setState({ ...state, environment })
}
function setLocalUrl(url: string) {
setState({...state, localUrl: url})
setState({ ...state, localUrl: url })
}
function compilerUrl() {
@ -111,14 +107,20 @@ const App = () => {
spinnerIcon.current.classList.add('remixui_spinningIcon')
}
const [toggleAccordion, setToggleAccordion] = useState(false)
const [cloneCount, setCloneCount] = useState(0)
return (
<main id="vyper-plugin">
<section>
<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.">
<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
</Button>
</CustomTooltip>
@ -162,7 +164,7 @@ const App = () => {
in the .vy file.
</span>
<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>
<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 { remixClient } from './remix-client'
import _ from 'lodash'
export interface Contract {
name: string
content: string
@ -72,8 +71,8 @@ const buildError = (output) => {
const line = output.line
if (line) {
const lineColumnPos = {
start: {line: line - 1, column: 10},
end: {line: line - 1, column: 10}
start: { line: line - 1, column: 10 },
end: { line: line - 1, column: 10 }
}
// remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
} else {
@ -92,8 +91,8 @@ const buildError = (output) => {
}
if (location?.length > 0) {
const lineColumnPos = {
start: {line: parseInt(location[0]) - 1, column: 10},
end: {line: parseInt(location[0]) - 1, column: 10}
start: { line: parseInt(location[0]) - 1, column: 10 },
end: { line: parseInt(location[0]) - 1, column: 10 }
}
// 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.')
}
let contractName = contract['name']
const compilePackage = {
manifest: 'ethpm/3',
sources: {
[contractName] : {content : fixContractContent(contract.content)}
[contractName] : { content : fixContractContent(contract.content) }
}
}
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) {
remixClient.eventEmitter.emit('resetCompilerState', {})
spinner && spinner === true ? setLoadingSpinnerState && setLoadingSpinnerState(true) : null
@ -301,7 +297,7 @@ export async function compileContract(contract: string, compilerUrl: string, set
})
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
return
}
@ -332,13 +328,10 @@ export async function compileContract(contract: string, compilerUrl: string, set
})
setLoadingSpinnerState && setLoadingSpinnerState(false)
remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: err.message})
remixClient.eventEmitter.emit('setOutput', { status: 'failed', message: err.message })
}
}
export type StandardOutput = {
sources: {
[fileName: string]: {

@ -1,9 +1,9 @@
import {HighlightPosition, CompilationResult, RemixApi, customAction} from '@remixproject/plugin-api'
import {Api, Status} from '@remixproject/plugin-utils'
import {createClient} from '@remixproject/plugin-webview'
import {PluginClient} from '@remixproject/plugin'
import {Contract, compileContract} from './compiler'
import {ExampleContract} from '../components/VyperResult'
import { HighlightPosition, CompilationResult, RemixApi, customAction } from '@remixproject/plugin-api'
import { Api, Status } from '@remixproject/plugin-utils'
import { createClient } from '@remixproject/plugin-webview'
import { PluginClient } from '@remixproject/plugin'
import { Contract, compileContract } from './compiler'
import { ExampleContract } from '../components/VyperResult'
import EventEmitter from 'events'
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 */
async loadContract({name, address}: ExampleContract) {
async loadContract({ name, address }: ExampleContract) {
try {
const content = await this.client.call('contentImport', 'resolve', address)
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 {
// @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(
'dGitProvider',
'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
'snekmate'
(count === undefined || count === 0) ? 'vyper-lang' : `vyper-lang_${count}`
)
this.call(
// @ts-ignore
'notification',
'toast',
'Snekmate Vyper repository cloned, the workspace snekmate has been created.'
'Vyper repository cloned, the workspace Vyper has been created.'
)
} catch (e) {
// @ts-ignore

@ -23,7 +23,18 @@ const DragBar = (props: IRemixDragBarUi) => {
if (props.hidden) {
setDragBarPosX(offset)
} 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') {
setDragBarPosX(offset)
}

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

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

@ -718,7 +718,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.generateDocumentation' }),
contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [],
keybindings: [
// Keybinding for Ctrl + D
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyD
],
run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file)
@ -734,7 +737,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.explainFunction' }),
contextMenuOrder: 1, // choose the order
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 () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file)
@ -750,7 +756,10 @@ export const EditorUI = (props: EditorUIProps) => {
label: intl.formatMessage({ id: 'editor.explainFunctionSol' }),
contextMenuOrder: 1, // choose the order
contextMenuGroupId: 'sol-gtp', // create a new grouping
keybindings: [],
keybindings: [
// Keybinding for Ctrl + E
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyE
],
run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file)

@ -12,12 +12,9 @@ function HomeTabFeatured() {
const themeFilter = useContext(ThemeContext)
return (
<div className="pt-3 pl-2" id="hTFeaturedeSection">
<label style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.featured" />
</label>
<div className="mb-2">
<div className="w-100 d-flex flex-column" style={{ height: '200px' }}>
<div className="pt-1 pl-2" id="hTFeaturedeSection">
<div className="mb-2 remix_ui-carousel-container">
<div className="w-100 d-flex flex-column rounded-3 remix_ui-carouselbox">
<ThemeContext.Provider value={themeFilter}>
<Carousel
arrows={false}
@ -34,16 +31,16 @@ function HomeTabFeatured() {
centerMode={false}
autoPlay={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"
deviceType={'desktop'}
itemClass=""
autoPlaySpeed={10000}
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">
<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>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>{releaseDetails.version} {releaseDetails.title}</h5>
@ -65,9 +62,9 @@ function HomeTabFeatured() {
</a>
</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">
<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>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>
@ -86,9 +83,9 @@ function HomeTabFeatured() {
</a>
</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">
<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>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>
@ -110,9 +107,9 @@ function HomeTabFeatured() {
</a>
</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">
<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>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>

@ -84,7 +84,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
}
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' }}>
<FormattedMessage id="home.featuredPlugins" />
</label>

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useRef, useReducer, useEffect } from 'react'
import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import {ModalDialog} from '@remix-ui/modal-dialog' // eslint-disable-line
import {Toaster} from '@remix-ui/toaster' // eslint-disable-line
const _paq = (window._paq = window._paq || []) // eslint-disable-line
import { CustomTooltip } from '@remix-ui/helper'
import { TEMPLATE_NAMES } from '@remix-ui/workspace'
@ -14,7 +14,7 @@ interface HomeTabFileProps {
const loadingInitialState = {
tooltip: '',
showModalDialog: false,
importSource: ''
importSource: '',
}
const loadingReducer = (state = loadingInitialState, action) => {
@ -22,7 +22,7 @@ const loadingReducer = (state = loadingInitialState, action) => {
...state,
tooltip: action.tooltip,
showModalDialog: false,
importSource: ''
importSource: '',
}
}
@ -45,7 +45,7 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
modalInfo: { title: '', loadItem: '', examples: [], prefix: '' },
importSource: '',
toasterMsg: '',
recentWorkspaces: []
recentWorkspaces: [],
})
const [, dispatch] = useReducer(loadingReducer, loadingInitialState)
@ -71,7 +71,9 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
if (!recents) {
newRecents = []
} else {
newRecents = recents.filter((el) => { return el !== name})
newRecents = recents.filter((el) => {
return el !== name
})
localStorage.setItem('recentWorkspaces', JSON.stringify(newRecents))
}
setState((prevState) => {
@ -85,8 +87,7 @@ function HomeTabFile({ plugin }: HomeTabFileProps) {
try {
plugin.off('filePanel', 'setWorkspace')
plugin.off('filePanel', 'workspaceDeleted')
} catch (e) {
}
} catch (e) {}
}
}, [plugin])
@ -162,7 +163,6 @@ contract HelloWorld {
} else {
await plugin.call('fileManager', 'open', '/contracts/HelloWorld.sol')
}
}
const uploadFile = async (target) => {
@ -216,14 +216,7 @@ contract HelloWorld {
return (
<>
<ModalDialog
id="homeTab"
title={'Import from ' + state.modalInfo.title}
okLabel="Import"
hide={!state.showModalDialog}
handleHide={() => hideFullMessage()}
okFn={() => processLoading(state.modalInfo.title)}
>
<ModalDialog 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">
{state.modalInfo.loadItem !== '' && <span>Enter the {state.modalInfo.loadItem} you would like to load.</span>}
{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>}
<input
ref={inputValue}
type='text'
name='prompt_text'
id='inputPrompt_text'
type="text"
name="prompt_text"
id="inputPrompt_text"
className="w-100 mt-1 form-control"
data-id="homeTabModalDialogCustomPromptText"
value={state.importSource}
@ -253,62 +246,11 @@ contract HelloWorld {
</ModalDialog>
<Toaster message={state.toasterMsg} />
<div className="justify-content-start mt-1 p-2 d-flex flex-column" id="hTFileSection">
<label style={{ fontSize: '1.2rem' }}>
<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>
<div className="mb-3">
{(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">
Recent workspaces
Recent Workspaces
</label>
{state.recentWorkspaces[0] && 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>
<label style={{ fontSize: '0.8rem' }} className="pt-3">
<FormattedMessage id="home.loadFrom" />
</label>
<div className="d-flex">
<button
className="btn p-2 border mr-2"
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',
])
}
>
GitHub
</button>
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>
<div className="d-flex flex-column flex-nowrap pt-3">
<label style={{ fontSize: '1.2rem' }}>
<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="homeTabNewFile" style={{ width: 'fit-content' }} onClick={async () => await plugin.call('filePanel', 'createNewFile')}>
<FormattedMessage id="home.newFile" />
</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>
<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
</button>
<button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs hash', ['ipfs://QmQQfBMkpDgmxKzYaoAtqfaybzfgGm9b2LWYyT56Chv6xH'], 'ipfs://')}>
IPFS
</button>
<button
className="btn p-2 border"
onClick={() =>
showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])
}
>
</button>
<button
className="btn text-nowrap p-2 mr-2 border my-1"
onClick={() =>
showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])
}
>
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>
</>

@ -1,13 +1,12 @@
/* 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 { TEMPLATE_NAMES,TEMPLATE_METADATA } from '@remix-ui/workspace'
import { TEMPLATE_NAMES, TEMPLATE_METADATA } from '@remix-ui/workspace'
import { ThemeContext } from '../themeContext'
import Carousel from 'react-multi-carousel'
import WorkspaceTemplate from './workspaceTemplate'
import 'react-multi-carousel/lib/styles.css'
import CustomNavButtons from './customNavButtons'
import { appPlatformTypes, platformContext } from '@remix-ui/app'
import { CustomTooltip } from '@remix-ui/helper'
declare global {
interface Window {
@ -19,12 +18,65 @@ interface HomeTabGetStartedProps {
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) {
const platform = useContext(platformContext)
const themeFilter = useContext(ThemeContext)
const intl = useIntl()
const carouselRef = useRef<any>({})
const carouselRefDiv = useRef(null)
const intl = useIntl()
useEffect(() => {
document.addEventListener('wheel', handleScroll)
@ -61,8 +113,7 @@ function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
}
const createWorkspace = async (templateName) => {
if (platform === appPlatformTypes.desktop){
if (platform === appPlatformTypes.desktop) {
await plugin.call('remix-templates', 'loadTemplateInNewWindow', templateName)
return
}
@ -92,90 +143,45 @@ function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
return (
<div className="pl-2" id="hTGetStartedSection">
<label style={{ fontSize: '1.2rem' }}>
<label className="pt-3" style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.projectTemplates" />
</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}>
<Carousel
ref={carouselRef}
focusOnSelect={true}
customButtonGroup={<CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} parent={carouselRef} />}
arrows={false}
swipeable={false}
draggable={true}
showDots={false}
responsive={{
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 5
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 5,
partialVisibilityGutter: 0
}
}}
renderButtonGroupOutside={true}
ssr={true} // means to render carousel on server-side.
keyBoardControl={true}
containerClass="carousel-container"
deviceType={'desktop'}
itemClass="w-100"
>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="MultiSig"
description={
intl.formatMessage({ id: 'home.gnosisSafeMultisigTemplateDesc' })
}
projectLogo="assets/img/gnosissafeLogo.png"
callback={() => createWorkspace("gnosisSafeMultisig")}
/>
<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>
<div className="pt-3">
<div className="d-flex flex-row align-items-center mb-3 flex-nowrap">
{workspaceTemplates.slice(0, 3).map((template, index) => (
<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}`}>
<button
key={index}
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'}
onClick={(e) => {
createWorkspace(template.templateName)
}}
data-id={`homeTabGetStarted${template.templateName}`}
>
{template.workspaceTitle}
</button>
</CustomTooltip>
))}
</div>
<div className="d-flex flex-row align-items-center mb-2 flex-nowrap">
{workspaceTemplates.slice(3, workspaceTemplates.length).map((template, index) => (
<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}`}>
<button
key={index}
className={'btn border p-2 text-nowrap mr-3'}
onClick={() => {
createWorkspace(template.templateName)
}}
data-id={`homeTabGetStarted${template.workspaceTitle}`}
>
{template.workspaceTitle}
</button>
</CustomTooltip>
))}
</div>
</div>
</ThemeContext.Provider>
</div>
</div>

@ -3,8 +3,55 @@
import React, { useEffect, useState, useRef, useContext } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { CustomTooltip } from '@remix-ui/helper'
import { Placement } from 'react-bootstrap/esm/Overlay'
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() {
useEffect(() => {
document.addEventListener('keyup', (e) => handleSearchKeyDown(e))
@ -64,82 +111,25 @@ function HomeTabTitle() {
</div>
</div>
<span className="d-flex flex-nowrap align-self-end">
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.remixYoutubePlaylist" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
onClick={() => {
openLink('https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA')
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'youtube'])
}}
className="border-0 px-1 h-100 btn fab fa-youtube"
></button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
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>
{iconButtons.map((button, index) => (
<CustomTooltip
key={index}
placement={button.placement}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={button.textToolip}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button
key={index}
onClick={() => {
openLink(button.urlLink)
_paq.push(button.matomoTrackingEntry)
}}
className={`border-0 h-100 pl-1 pr-0 btn fab ${button.iconClass}`}
></button>
</CustomTooltip>
))}
</span>
</div>
<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">
<FormattedMessage id="home.website" />
</a>
<a
{/* <a
className="pl-2 remixui_home_text"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'documentation'])}
target="__blank"
href="https://remix-ide.readthedocs.io/en/latest"
>
<FormattedMessage id="home.documentation" />
</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"
onClick={() => _paq.push(['trackEvent', 'hometab', 'header', 'remixDesktop'])}
target="__blank"
href="https://github.com/ethereum/remix-desktop/releases"
href="https://github.com/remix-project-org/remix-desktop-insiders"
>
<FormattedMessage id="home.remixDesktop" />
</a>

@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'
import { Dropdown, DropdownButton } from 'react-bootstrap'
import DropdownItem from 'react-bootstrap/DropdownItem'
import { localeLang } from './types/carouselTypes'
import { FormattedMessage } from 'react-intl'
export function LanguageOptions({ plugin }: { plugin: any }) {
const [langOptions, setLangOptions] = useState<string>()
@ -24,7 +25,10 @@ export function LanguageOptions({ plugin }: { plugin: any }) {
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.Toggle title={langOptions} id="languagedropdown" size="sm" style={{ backgroundColor: 'var(--secondary)', color: 'var(--text)' }}>
{langOptions}

@ -114,3 +114,36 @@
background-color: var(--body-bg);
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
})
const [carouselWidth, setCarouselWidth] = useState(65)
useEffect(() => {
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 (
<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}>
<div className="d-flex flex-row w-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="d-flex flex-row w-100 h-100 custom_home_bg">
<div className="px-2 pl-3 justify-content-start border-right d-flex flex-column" id="remixUIHTLeft" style={{ width: `${100 - carouselWidth}%` }}>
<HomeTabTitle />
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
{!(platform === appPlatformTypes.desktop) ?
<HomeTabFile plugin={plugin} />:
<HomeTabFileElectron plugin={plugin}></HomeTabFileElectron>}
<HomeTabLearn plugin={plugin} />
{/* <HomeTabLearn plugin={plugin} /> */}
</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}/>
<HomeTabFeatured></HomeTabFeatured>
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
<HomeTabFeaturedPlugins plugin={plugin}></HomeTabFeaturedPlugins>
<HomeTabScamAlert></HomeTabScamAlert>
</div>
</div>
</ThemeContext.Provider>

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

@ -1,4 +1,5 @@
import { Profile } from '@remixproject/plugin-utils'
import EventEmitter from 'events'
export type PluginRecord = {
profile: Profile
@ -8,3 +9,22 @@ export type PluginRecord = {
class?: string
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
import React from 'react'
import parse from 'html-react-parser';
import parse from 'html-react-parser'
const _paq = (window._paq = window._paq || [])
interface SolScanTableProps {
scanDetails: Record<string, any>[],
@ -13,7 +14,13 @@ export function SolScanTable(props: SolScanTableProps) {
return (
<>
<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">
<thead>
<tr>

@ -304,7 +304,13 @@ export function UniversalDappUI(props: UdappProps) {
title: <FormattedMessage id="udapp.solScan.modalTitle" />,
message: <div className='d-flex flex-column'>
<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" />
</div>,
okLabel: <FormattedMessage id="udapp.solScan.modalOkLabel" />,

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

@ -77,7 +77,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
key={keyPath}
label={
<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>
</div>
}
@ -94,7 +94,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
key={keyPath}
label={
<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>
</div>
}
@ -311,25 +311,25 @@ export const ContractSelection = (props: ContractSelectionProps) => {
</CustomTooltip>
</button>
<button
data-id="compilation-details"
className="btn btn-secondary btn-block"
onClick={async () => {
details()
await (api as any).call('compilationDetails', 'showDetails', payload)
}}
<CustomTooltip
placement={'right-end'}
tooltipId="CompilationDetailsTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="solidity.displayContractDetails" />}
>
<CustomTooltip
placement={'auto-end'}
tooltipId="CompilationDetailsTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="solidity.displayContractDetails" />}
<button
data-id="compilation-details"
className="btn btn-secondary btn-block"
onClick={async () => {
details()
await (api as any).call('compilationDetails', 'showDetails', payload)
}}
>
<span>
<FormattedMessage id="solidity.compilationDetails" />
</span>
</CustomTooltip>
</button>
</button>
</CustomTooltip>
{/* Copy to Clipboard */}
<div className="remixui_contractHelperButtons">
<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 */
export interface TabsUIProps {
tabs: Array<any>
tabs: Array<Tab>
plugin: Plugin
onSelect: (index: number) => void
onClose: (index: number) => void
@ -19,6 +19,15 @@ export interface TabsUIProps {
onReady: (api: any) => void
themeQuality: string
}
export interface Tab {
id: string
icon: string
iconClass: string
name: string
title: string
tooltip: string
}
export interface TabsUIApi {
activateTab: (name: string) => void
active: () => string
@ -96,7 +105,8 @@ export const TabsUI = (props: TabsUIProps) => {
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 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)'

@ -597,7 +597,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
return (
( 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">
{handleAutoComplete()}
<div className="position-relative d-flex flex-column-reverse h-100">

@ -131,7 +131,7 @@ export const Toaster = (props: ToasterProps) => {
{!state.hide && (
<div
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}
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'
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)
useEffect(() => {
@ -12,7 +12,13 @@ export const TreeViewItem = (props: TreeViewItemProps) => {
}, [expand])
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
key={`treeViewDiv${id}`}
data-id={`treeViewDiv${id}`}
@ -20,13 +26,17 @@ export const TreeViewItem = (props: TreeViewItemProps) => {
onClick={() => !controlBehaviour && setIsExpanded(!isExpanded)}
>
{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 ? (
<div className={`pr-2 pl-2 ${icon} caret caret_tv`}></div>
) : null}
<span className="w-100 ml-1 pl-2">{label}</span>
</div>
{isExpanded ? children : null}
{isExpanded ? <div className="pl-3">
{children}
</div> : null}
</li>
)
}

@ -141,6 +141,10 @@
flex-basis: 50px;
}
.remixui_icons_height {
height: 97vh;
}
#menuitems {
list-style: none;
margin: 0px;

@ -69,7 +69,7 @@ const RemixUiVerticalIconsPanel = ({ verticalIconsPlugin, icons }: RemixUiVertic
return (
<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} />
<div
className={

@ -1,4 +1,4 @@
export * from './lib/providers/FileSystemProvider'
export * from './lib/contexts'
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/vm": "8.0.0",
"@ethersphere/bee-js": "^3.2.0",
"@floating-ui/react": "^0.26.15",
"@gradio/client": "^0.10.1",
"@isomorphic-git/lightning-fs": "^4.4.1",
"@microlink/react-json-view": "^1.23.0",

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

@ -2846,6 +2846,42 @@
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8"
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":
version "1.11.7"
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"
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:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tap-out/-/tap-out-2.1.0.tgz#c093079a915036de8b835bfa3297f14458b15358"

Loading…
Cancel
Save