Merge branch 'fixethdoc' of https://github.com/ethereum/remix-project into testethdoc

pull/3542/head
filip mertens 2 years ago
commit 065fb52ce6
  1. 17
      apps/doc-gen/webpack.config.js
  2. 22
      apps/doc-viewer/webpack.config.js
  3. 2
      apps/etherscan/src/app/components/HeaderWithSettings.tsx
  4. 2
      apps/etherscan/src/app/views/VerifyView.tsx
  5. 6
      apps/remix-ide-e2e/src/commands/addFile.ts
  6. 26
      apps/remix-ide-e2e/src/helpers/init.ts
  7. 5
      apps/remix-ide-e2e/src/tests/ballot_0_4_14.test.ts
  8. 2
      apps/remix-ide-e2e/src/tests/defaultLayout.test.ts
  9. 4
      apps/remix-ide-e2e/src/tests/file_decorator.test.ts
  10. 6
      apps/remix-ide-e2e/src/tests/gist.test.ts
  11. 13
      apps/remix-ide-e2e/src/tests/importFromGithub.test.ts
  12. 10
      apps/remix-ide-e2e/src/tests/recorder.test.ts
  13. 4
      apps/remix-ide-e2e/src/tests/search.test.ts
  14. 4
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  15. 27
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  16. 7
      apps/remix-ide/src/app.js
  17. 12
      apps/remix-ide/src/app/panels/tab-proxy.js
  18. 102
      apps/remix-ide/src/app/plugins/solidity-script.tsx
  19. 2
      apps/remix-ide/src/app/plugins/solidity-umlgen.tsx
  20. 7
      apps/remix-ide/src/app/tabs/compile-tab.js
  21. 18
      apps/remix-ide/src/app/tabs/locales/en/udapp.json
  22. 2
      apps/remix-ide/src/app/ui/landing-page/landing-page.js
  23. 8
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  24. 8
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  25. 8
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  26. 9
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  27. 8
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  28. 8
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  29. 8
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  30. 8
      apps/remix-ide/src/assets/css/themes/remix-hacker_owl.css
  31. 8
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  32. 10
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  33. 2
      apps/remix-ide/src/blockchain/blockchain.js
  34. 16
      apps/remix-ide/src/blockchain/providers/vm.js
  35. 32
      apps/remix-ide/src/blockchain/providers/worker-vm.ts
  36. 2
      apps/remix-ide/src/index.html
  37. 2
      apps/remix-ide/src/remixAppManager.js
  38. 28
      libs/remix-solidity/src/compiler/compiler-helpers.ts
  39. 10
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  40. 18
      libs/remix-ui/app/src/lib/remix-app/style/remix-app.css
  41. 14
      libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx
  42. 35
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  43. 15
      libs/remix-ui/editor/src/lib/helpers/retrieveNodesAtPosition.ts
  44. 12
      libs/remix-ui/editor/src/lib/providers/completionProvider.ts
  45. 101
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  46. 56
      libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx
  47. 46
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx
  48. 18
      libs/remix-ui/helper/src/lib/components/custom-tooltip.tsx
  49. 4
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  50. 2
      libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx
  51. 2
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
  52. 2
      libs/remix-ui/panel/src/lib/main/main-panel.css
  53. 27
      libs/remix-ui/panel/src/lib/plugins/panel-header.tsx
  54. 70
      libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx
  55. 66
      libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx
  56. 14
      libs/remix-ui/run-tab/src/lib/components/account.tsx
  57. 150
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  58. 2
      libs/remix-ui/run-tab/src/lib/components/deployButton.tsx
  59. 2
      libs/remix-ui/run-tab/src/lib/components/deployInput.tsx
  60. 12
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  61. 15
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  62. 2
      libs/remix-ui/run-tab/src/lib/css/run-tab.css
  63. 41
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  64. 12
      libs/remix-ui/solidity-compiler/src/lib/api/compiler-api.ts
  65. 43
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  66. 6
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  67. 66
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.css
  68. 48
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  69. 71
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  70. 6
      libs/remix-ui/tooltip-popup/src/lib/tooltip-popup.tsx
  71. 6
      libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx
  72. 22
      libs/remix-ui/vertical-icons-panel/src/lib/components/Badge.tsx
  73. 3
      libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx
  74. 8
      libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx
  75. 138
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  76. 11
      libs/remix-ui/workspace/src/lib/components/file-label.tsx
  77. 4
      libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx
  78. 3
      libs/remix-ui/workspace/src/lib/css/file-explorer.css
  79. 12
      libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
  80. 2
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx

@ -11,24 +11,9 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// add fallback for node modules
config.resolve.fallback = {
// ...config.resolve.fallback,
// "crypto": require.resolve("crypto-browserify"),
// "stream": require.resolve("stream-browserify"),
...config.resolve.fallback,
"path": require.resolve("path-browserify"),
// "http": require.resolve("stream-http"),
// "https": require.resolve("https-browserify"),
// "constants": require.resolve("constants-browserify"),
// "os": false, //require.resolve("os-browserify/browser"),
// "timers": false, // require.resolve("timers-browserify"),
// "zlib": require.resolve("browserify-zlib"),
"fs": false,
// "module": false,
// "tls": false,
// "net": false,
// "readline": false,
// "child_process": false,
// "buffer": require.resolve("buffer/"),
// "vm": require.resolve('vm-browserify'),
}
// add externals

@ -9,28 +9,6 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
// add fallback for node modules
config.resolve.fallback = {
// ...config.resolve.fallback,
// "crypto": require.resolve("crypto-browserify"),
// "stream": require.resolve("stream-browserify"),
// "path": require.resolve("path-browserify"),
// "http": require.resolve("stream-http"),
// "https": require.resolve("https-browserify"),
// "constants": require.resolve("constants-browserify"),
// "os": false, //require.resolve("os-browserify/browser"),
// "timers": false, // require.resolve("timers-browserify"),
// "zlib": require.resolve("browserify-zlib"),
// "fs": false,
// "module": false,
// "tls": false,
// "net": false,
// "readline": false,
// "child_process": false,
// "buffer": require.resolve("buffer/"),
// "vm": require.resolve('vm-browserify'),
}
// add externals
config.externals = {
...config.externals,

@ -148,9 +148,7 @@ export const HeaderWithSettings: React.FC<Props> = ({
<h6>{title}</h6>
<div style={{ float: "right" }}>
<HomeIcon from={from} themeType={themeType} />
<ReceiptsIcon from={from} themeType={themeType} />
<SettingsIcon from={from} themeType={themeType} />
</div>
</div>

@ -23,8 +23,6 @@ interface FormValues {
contractAddress: string
}
export const VerifyView: React.FC<Props> = ({
apiKey,
client,

@ -54,9 +54,9 @@ function addFile(browser: NightwatchBrowser, name: string, content: NightwatchCo
suppressNotFoundErrors: true,
timeout: 60000
})
.waitForElementPresent({
selector: `//*[@data-id="tab-active" and contains(@title, "${name}")]`,
locateStrategy: 'xpath',
.waitForElementVisible({
selector: `//*[@data-id='tab-active' and contains(@data-path, "${name}")]`,
locateStrategy: 'xpath'
})
.setEditorValue(content.content)
.getEditorValue((result) => {

@ -7,7 +7,7 @@ type LoadPlugin = {
url: string
}
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true, loadPlugin?: LoadPlugin): void {
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true, loadPlugin?: LoadPlugin, hideToolTips: boolean = true): void {
browser
.url(url || 'http://127.0.0.1:8080')
//.switchBrowserTab(0)
@ -27,6 +27,30 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url
})
.verifyLoad()
.perform(() => {
if (hideToolTips) {
browser.execute(function () { // hide tooltips
function addStyle(styleString) {
const style = document.createElement('style');
style.textContent = styleString;
document.head.append(style);
}
addStyle(`
.bs-popover-right {
display:none !important;
}
.bs-popover-top {
display:none !important;
}
.bs-popover-left {
display:none !important;
}
.bs-popover-bottom {
display:none !important;
}
`);
})
}
if (preloadPlugins) {
initModules(browser, () => {
browser

@ -11,7 +11,7 @@ const sources = [
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, null, false)
init(browser, done)
},
'@sources': function () {
return sources
@ -25,9 +25,6 @@ module.exports = {
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('solidity')
.setSolidityCompilerVersion('soljson-v0.4.14+commit.c2215d46.js')
.waitForElementVisible('[for="autoCompile"]')
.click('[for="autoCompile"]')
.verify.elementPresent('[data-id="compilerContainerAutoCompile"]:checked')
},
'Compile Ballot with compiler version 0.4.14': function (browser: NightwatchBrowser) {

@ -66,7 +66,7 @@ module.exports = {
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.click('[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/3_Ballot.sol')
.assert.containsText('div[title="default_workspace/contracts/3_Ballot.sol"]', '3_Ballot.sol')
.assert.containsText('div[data-path="default_workspace/contracts/3_Ballot.sol"]', '3_Ballot.sol')
.end()
}
}

@ -29,8 +29,8 @@ module.exports = {
.waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 'customtext')
.waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 'customtext')
.moveToElement('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', 0, 0)
.waitForElementVisible('//*[@id="error-tooltip-contracts/2_Owner.sol"]')
.waitForElementContainsText('//*[@id="error-tooltip-contracts/2_Owner.sol"]', 'error on owner')
//.waitForElementVisible('//*[@id="error-tooltip-contracts/2_Owner.sol"]')
//.waitForElementContainsText('//*[@id="error-tooltip-contracts/2_Owner.sol"]', 'error on owner')
},
'clear ballot decorator': function (browser: NightwatchBrowser) {

@ -73,7 +73,7 @@ module.exports = {
browser.clickLaunchIcon('home')
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('filePanel')
.click('div[title="home"]')
.click('div[data-id="verticalIconsHomeIcon"]')
.waitForElementVisible('button[data-id="landingPageImportFromGistButton"]')
.pause(1000)
.scrollAndClick('button[data-id="landingPageImportFromGistButton"]')
@ -143,8 +143,8 @@ module.exports = {
.modalFooterOKClick('gisthandler')
.pause(10000)
.openFile(`gist-${testData.validGistId}/README.txt`)
.waitForElementVisible(`div[title='default_workspace/gist-${testData.validGistId}/README.txt']`)
.assert.containsText(`div[title='default_workspace/gist-${testData.validGistId}/README.txt'] > span`, 'README.txt')
.waitForElementVisible(`div[data-path='default_workspace/gist-${testData.validGistId}/README.txt']`)
.assert.containsText(`div[data-path='default_workspace/gist-${testData.validGistId}/README.txt'] > span`, 'README.txt')
.end()
}
}

@ -18,7 +18,7 @@ module.exports = {
browser.clickLaunchIcon('home')
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('filePanel')
.click('div[title="home"]')
.click('div[data-id="verticalIconsHomeIcon"]')
.waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]')
.pause(1000)
.click('button[data-id="landingPageImportFromGitHubButton"]')
@ -45,7 +45,7 @@ module.exports = {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('filePanel')
.click('div[title="home"]')
.click('div[data-id="verticalIconsHomeIcon"]')
.waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]').pause(1000)
.click('button[data-id="landingPageImportFromGitHubButton"]')
.waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]')
@ -57,14 +57,17 @@ module.exports = {
.waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]')
.click('[data-id="homeTab-modal-footer-ok-react"]')
.openFile('github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol')
.waitForElementVisible("div[title='default_workspace/github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol'")
.waitForElementVisible({
selector: `//*[@data-id='tab-active' and @data-path="default_workspace/github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol"]`,
locateStrategy: 'xpath'
})
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('library Roles {') !== -1, 'content does contain "library Roles {"')
})
},
'Import JSON From GitHub For Valid URL #group2': function (browser: NightwatchBrowser) {
browser
.click('div[title="home"]')
.click('div[data-id="verticalIconsHomeIcon"]')
.click('button[data-id="landingPageImportFromGitHubButton"]')
.waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]').pause(1000)
.execute(() => {
@ -75,7 +78,7 @@ module.exports = {
.waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]')
.click('[data-id="homeTab-modal-footer-ok-react"]')
.openFile('github/ethereum/remix-project/package.json')
.waitForElementVisible("div[title='default_workspace/github/ethereum/remix-project/package.json'")
.waitForElementVisible("div[data-path='default_workspace/github/ethereum/remix-project/package.json'")
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('"name": "remix-project",') !== -1, 'content does contain "name": "remix-project"')
})

@ -95,12 +95,10 @@ module.exports = {
.clickInstance(1)
.pause(1000)
.clickFunction('set2 - transact (not payable)', { types: 'uint256 _po', values: '10' })
.testFunction('last',
{
status: 'true Transaction mined and execution succeed',
'decoded input': { 'uint256 _po': '10' }
})
.testFunction('last', {
status: 'true Transaction mined and execution succeed',
'decoded input': { 'uint256 _po': '10' }
})
},
'Run with live "mode" #group2': function (browser: NightwatchBrowser) {

@ -150,14 +150,14 @@ module.exports = {
browser.assert.ok(content.includes("123test' contract"), 'should replace text ok')
})
.waitForElementVisible('*[data-id="undo-replace-README.txt"]')
.click('div[title="default_workspace/contracts/1_Storage.sol"]').pause(2000)
.click('div[data-path="default_workspace/contracts/1_Storage.sol"]').pause(2000)
.waitForElementVisible('*[data-id="undo-replace-contracts/1_Storage.sol"]')
.click('*[data-id="undo-replace-contracts/1_Storage.sol"]').pause(2000)
.getEditorValue((content) => {
browser.assert.ok(content.includes('contract Storage'), 'should undo text ok')
browser.assert.ok(content.includes('title Storage'), 'should undo text ok')
})
.click('div[title="default_workspace/README.txt"]').pause(2000)
.click('div[data-path="default_workspace/README.txt"]').pause(2000)
.waitForElementVisible('*[data-id="undo-replace-README.txt"]')
.click('*[data-id="undo-replace-README.txt"]').pause(2000)
.getEditorValue((content) => {

@ -48,7 +48,7 @@ module.exports = {
.waitForElementPresent('*[data-id="testTabGenerateTestFile"]')
.click('*[data-id="testTabGenerateTestFile"]')
.clickLaunchIcon('filePanel')
.waitForElementPresent('*[title="default_workspace/tests/simple_storage_test.sol"]')
.waitForElementPresent('*[data-path="default_workspace/tests/simple_storage_test.sol"]')
.removeFile('tests/simple_storage_test.sol', 'default_workspace')
},
@ -106,7 +106,7 @@ module.exports = {
'Should fail on compilation, open file on error click, not disappear error #group2': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('tests/compilationError_test.sol', sources[0]['compilationError_test.sol'])
.click('div[title="default_workspace/tests/compilationError_test.sol"] span[class="close-tabs"]')
.click('div[data-path="default_workspace/tests/compilationError_test.sol"] span[class="close-tabs"]')
.clickLaunchIcon('solidityUnitTesting')
.pause(2000)
.click('*[data-id="testTabCheckAllTests"]')

@ -317,6 +317,33 @@ module.exports = {
.executeScriptInTerminal(`web3.eth.getCode('0x75F509A4eDA030470272DfBAf99A47D587E76709')`) // sepolia contract
.waitForElementContainsText('*[data-id="terminalJournal"]', byteCodeInSepolia, 120000)
},
'Should run free function which logs in the terminal #group10': function (browser: NightwatchBrowser) {
const script = `import "hardhat/console.sol";
function runSomething () view {
console.log("test running free function");
}
`
browser
.addFile('test.sol', { content: script })
.scrollToLine(3)
const path = "//*[@class='view-line' and contains(.,'runSomething') and contains(.,'view')]//span//span[contains(.,'(')]"
const pathRunFunction = `//li//*[@aria-label='Run the free function "runSomething" in the Remix VM']`
browser.waitForElementVisible('#editorView')
.useXpath()
.click(path)
.pause(3000) // the parser need to parse the code
.perform(function () {
const actions = this.actions({ async: true });
return actions
.keyDown(this.Keys.SHIFT)
.keyDown(this.Keys.ALT)
.sendKeys('r')
})
.useCss()
.waitForElementContainsText('*[data-id="terminalJournal"]', 'test running free function', 120000)
}
}

@ -16,6 +16,7 @@ import { PermissionHandlerPlugin } from './app/plugins/permission-handler-plugin
import { AstWalker } from '@remix-project/remix-astwalker'
import { LinkLibraries, DeployLibraries, OpenZeppelinProxy } from '@remix-project/core-plugin'
import { CodeParser } from './app/plugins/parser/code-parser'
import { SolidityScript } from './app/plugins/solidity-script'
import { WalkthroughService } from './walkthroughService'
@ -246,7 +247,7 @@ class AppComponent {
)
const codeParser = new CodeParser(new AstWalker())
const solidityScript = new SolidityScript()
this.notification = new NotificationPlugin()
@ -298,7 +299,8 @@ class AppComponent {
this.walkthroughService,
search,
solidityumlgen,
contractFlattener
contractFlattener,
solidityScript
])
// LAYOUT & SYSTEM VIEWS
@ -414,6 +416,7 @@ class AppComponent {
await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'codeParser', 'codeFormatter', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler'])
await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder'])
await this.appManager.activatePlugin(['solidity-script'])
this.appManager.on(
'filePanel',

@ -146,7 +146,7 @@ export class TabProxy extends Plugin {
}
})
this.on('manager', 'pluginActivated', ({ name, location, displayName, icon }) => {
this.on('manager', 'pluginActivated', ({ name, location, displayName, icon, description }) => {
if (location === 'mainPanel') {
this.addTab(
name,
@ -160,7 +160,8 @@ export class TabProxy extends Plugin {
this.emit('closeApp', name)
this.call('manager', 'deactivatePlugin', name)
},
icon
icon,
description
)
this.switchTab(name)
}
@ -223,7 +224,7 @@ export class TabProxy extends Plugin {
this.removeTab(oldName)
}
addTab (name, title, switchTo, close, icon) {
addTab (name, title, switchTo, close, icon, description = '') {
if (this._handlers[name]) return this.renderComponent()
var slash = name.split('/')
@ -252,6 +253,7 @@ export class TabProxy extends Plugin {
const index = this.loadedTabs.findIndex(({ title }) => title === formatPath.join('/'))
if (index > -1) {
const duplicateTabName = this.loadedTabs[index].name
const duplicateTabTooltip = this.loadedTabs[index].description
const duplicateTabPath = duplicateTabName.split('/')
const duplicateTabFormatPath = [...duplicateTabPath].reverse()
const duplicateTabTitle = duplicateTabFormatPath.slice(0, titleLength).reverse().join('/')
@ -260,7 +262,7 @@ export class TabProxy extends Plugin {
name: duplicateTabName,
title: duplicateTabTitle,
icon,
tooltip: duplicateTabName,
tooltip: duplicateTabTooltip || duplicateTabTitle,
iconClass: getPathIcon(duplicateTabName)
}
}
@ -274,7 +276,7 @@ export class TabProxy extends Plugin {
name,
title,
icon,
tooltip: name,
tooltip: description || title,
iconClass: getPathIcon(name)
})
}

@ -0,0 +1,102 @@
import React from 'react' // eslint-disable-line
import { format } from 'util'
import { Plugin } from '@remixproject/engine'
import { compile } from '@remix-project/remix-solidity'
import { TransactionConfig } from 'web3-core'
const _paq = window._paq = window._paq || [] //eslint-disable-line
const profile = {
name: 'solidity-script',
displayName: 'solidity-script',
description: 'solidity-script',
methods: ['execute']
}
export class SolidityScript extends Plugin {
constructor () {
super(profile)
}
async execute (path: string, functionName: string = 'run') {
_paq.push(['trackEvent', 'SolidityScript', 'execute', 'script'])
this.call('terminal', 'log', `running free function ${functionName} from ${path}...`)
let content = await this.call('fileManager', 'readFile', path)
const params = await this.call('solidity', 'getCompilerParameters')
content = `
import "${path}";
contract SolidityScript {
constructor () {}
function remixRun () public {
${functionName}();
}
}`
const targets = { 'script.sol': { content } }
// compile
const compilation = await compile(targets, params, async (url, cb) => {
await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message))
})
if (compilation.data.error) {
this.call('terminal', 'log', compilation.data.error.formattedMessage)
}
if (compilation.data.errors && compilation.data.errors.length > 0) {
compilation.data.errors.map((error) => {
this.call('terminal', 'log', error.formattedMessage)
})
}
// get the contract
const contract = compilation.getContract('SolidityScript')
if (!contract) {
console.log('compilation failed')
return
}
const bytecode = '0x' + contract.object.evm.bytecode.object
const web3 = await this.call('blockchain', 'web3VM')
const accounts = await this.call('blockchain', 'getAccounts')
if (!accounts || accounts.length === 0) {
throw new Error('no account available')
}
// deploy the contract
let tx: TransactionConfig = {
from: accounts[0],
data: bytecode
}
const receipt = await web3.eth.sendTransaction(tx)
tx = {
from: accounts[0],
to: receipt.contractAddress,
data: '0x69d4394b' // function remixRun() public
}
const receiptCall = await web3.eth.sendTransaction(tx)
const hhlogs = await web3.eth.getHHLogsForTx(receiptCall.transactionHash)
if (hhlogs && hhlogs.length) {
const finalLogs = <div><div><b>console.log:</b></div>
{
hhlogs.map((log) => {
let formattedLog
// Hardhat implements the same formatting options that can be found in Node.js' console.log,
// which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args
// For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6'
// We check first arg to determine if 'util.format' is needed
if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) {
formattedLog = format(log[0], ...log.slice(1))
} else {
formattedLog = log.join(' ')
}
return <div>{formattedLog}</div>
})}
</div>
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'logHtml', finalLogs)
}
}
}

@ -18,7 +18,7 @@ const _paq = window._paq = window._paq || []
const profile = {
name: 'solidityumlgen',
displayName: 'Solidity UML Generator',
description: 'Generate UML diagram in svg format from last compiled contract',
description: 'Generates UML diagram in svg format from last compiled contract',
location: 'mainPanel',
methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'],
events: [],

@ -21,7 +21,7 @@ const profile = {
documentation: 'https://remix-ide.readthedocs.io/en/latest/compile.html',
version: packageJson.version,
maintainedBy: 'Remix',
methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig', 'compileFile', 'getCompilerState']
methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig', 'compileFile', 'getCompilerState', 'getCompilerParameters', 'getCompiler']
}
// EditorApi:
@ -133,8 +133,13 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
}
}
getCompiler () {
return this.compileTabLogic.compiler
}
getCompilerParameters () {
const params = this.queryParams.get()
params.evmVersion = params.evmVersion === 'null' || params.evmVersion === 'undefined' ? null : params.evmVersion
params.optimize = (params.optimize === 'false' || params.optimize === null || params.optimize === undefined) ? false : params.optimize
params.optimize = params.optimize === 'true' ? true : params.optimize
return params

@ -5,8 +5,8 @@
"udapp.value": "Value",
"udapp.contract": "Contract",
"udapp.compiledBy": "Compiled by {compilerName}",
"udapp.infoSyncCompiledContractTooltip": "Click here to import contracts compiled from an external framework.{br}This action is enabled when Remix is connected to an external{br} framework (hardhat, truffle, foundry) through remixd.",
"udapp.remixIpfsUdappTooltip": "Publishing the source code and metadata to IPFS facilitates{br} source code verification using Sourcify and will greatly foster{br} contract adoption (auditing, debugging, calling it, etc...)",
"udapp.infoSyncCompiledContractTooltip": "Click here to import contracts compiled from an external framework.This action is enabled when Remix is connected to an external framework (hardhat, truffle, foundry) through remixd.",
"udapp.remixIpfsUdappTooltip": "Publishing the source code and metadata to IPFS facilitates source code verification using Sourcify and will greatly foster contract adoption (auditing, debugging, calling it, etc...)",
"udapp.signAMessage": "Sign a message",
"udapp.enterAMessageToSign": "Enter a message to sign",
"udapp.hash": "hash",
@ -19,15 +19,15 @@
"udapp.or": "or",
"udapp.atAddress": "At Address",
"udapp.atAddressOptionsTitle1": "address of contract",
"udapp.atAddressOptionsTitle2": "Interact with the deployed contract - requires the .abi file or {br} compiled .sol file to be selected in the editor {br}(with the same compiler configuration)",
"udapp.atAddressOptionsTitle2": "Interact with the deployed contract - requires the .abi file or compiled .sol file to be selected in the editor (with the same compiler configuration)",
"udapp.atAddressOptionsTitle3": "Compile a *.sol file or select a *.abi file.",
"udapp.atAddressOptionsTitle4": "To interact with a deployed contract, either{br} enter its address and compile its source *.sol file {br}(with the same compiler settings) or select its .abi file in the editor. ",
"udapp.atAddressOptionsTitle4": "To interact with a deployed contract, either enter its address and compile its source *.sol file (with the same compiler settings) or select its .abi file in the editor. ",
"udapp.contractOptionsTitle1": "Please compile *.sol file to deploy or access a contract",
"udapp.contractOptionsTitle2": "Select a compiled contract to deploy or to use with At Address.",
"udapp.contractOptionsTitle3": "Select and compile *.sol file to deploy or access a contract.",
"udapp.contractOptionsTitle4": "When there is a compiled .sol file, choose the {br} contract to deploy or to use with AtAddress.'",
"udapp.checkSumWarning": "It seems you are not using a checksumed address.{br}A checksummed address is an address that contains uppercase letters, as specified in {a}.{br}Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.",
"udapp.isOverSizePrompt": "Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. {br}More info: {a}",
"udapp.contractOptionsTitle4": "When there is a compiled .sol file, choose the contract to deploy or to use with AtAddress.'",
"udapp.checkSumWarning": "It seems you are not using a checksumed address.A checksummed address is an address that contains uppercase letters, as specified in {a}.Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.",
"udapp.isOverSizePrompt": "Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. More info: {a}",
"udapp.thisContractMayBeAbstract": "This contract may be abstract, it may not implement an abstract parent's methods completely or it may not invoke an inherited contract's constructor correctly.",
"udapp.noCompiledContracts": "No compiled contracts",
"udapp.addressOfContract": "Address of contract",
@ -40,8 +40,8 @@
"udapp.transactionSaveTooltip1": "No transactions to save",
"udapp.transactionSaveTooltip2": "Save {count} transaction as scenario file",
"udapp.transactionSaveTooltip3": "Save {count} transactions as scenario file",
"udapp.infoRecorderTooltip": "Save transactions (deployed contracts and function executions) {br}and replay them in another environment e.g Transactions created {br}in Remix VM can be replayed in the Injected Provider.",
"udapp.livemodeRecorderTooltip": "If contracts are updated after recording transactions,{br} checking this box will run recorded transactions {br}with the latest copy of the compiled contracts",
"udapp.infoRecorderTooltip": "Save transactions (deployed contracts and function executions) and replay them in another environment e.g Transactions created in Remix VM can be replayed in the Injected Provider.",
"udapp.livemodeRecorderTooltip": "If contracts are updated after recording transactions, checking this box will run recorded transactions with the latest copy of the compiled contracts",
"udapp.livemodeRecorderLabel": "Run transactions using the latest compilation result",
"udapp.runRecorderTooltip": "Run transaction(s) from the current scenario file",
"udapp.save": "Save",

@ -9,7 +9,7 @@ const profile = {
displayName: 'Home',
methods: [],
events: [],
description: 'Remix home tab ',
description: 'Remix Home',
icon: 'assets/img/home.webp',
location: 'mainPanel',
version: packageJson.version

@ -5250,7 +5250,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after {
bottom:1px;
border-width:.5rem .5rem 0;
border-top-color:#fff
border-top-color: var(--secondary)
}
.bs-popover-auto[x-placement^=right],.bs-popover-right {
margin-left:.5rem
@ -5269,7 +5269,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after {
left:1px;
border-width:.5rem .5rem .5rem 0;
border-right-color:#fff
border-right-color: var(--secondary)
}
.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom {
margin-top:.5rem
@ -5285,7 +5285,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after {
top:1px;
border-width:0 .5rem .5rem .5rem;
border-bottom-color:#fff
border-bottom-color: var(--secondary)
}
.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before {
position:absolute;
@ -5314,7 +5314,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after {
right:1px;
border-width:.5rem 0 .5rem .5rem;
border-left-color:#fff
border-left-color: var(--secondary)
}
.popover-header {
padding:.5rem .75rem;

@ -5252,7 +5252,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after {
bottom:1px;
border-width:.5rem .5rem 0;
border-top-color:#282828
border-top-color: var(--secondary);
}
.bs-popover-auto[x-placement^=right],.bs-popover-right {
margin-left:.5rem
@ -5271,7 +5271,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after {
left:1px;
border-width:.5rem .5rem .5rem 0;
border-right-color:#282828
border-right-color: var(--secondary);
}
.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom {
margin-top:.5rem
@ -5287,7 +5287,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after {
top:1px;
border-width:0 .5rem .5rem .5rem;
border-bottom-color:#282828
border-bottom-color: var(--secondary);
}
.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before {
position:absolute;
@ -5316,7 +5316,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after {
right:1px;
border-width:.5rem 0 .5rem .5rem;
border-left-color:#282828
border-left-color: var(--secondary);
}
.popover-header {
padding:.5rem .75rem;

@ -4253,7 +4253,7 @@ a.close.disabled {
bottom:0; border-width:.5rem .5rem 0; border-top-color:rgba(0,0,0,.25)
}
.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after {
bottom:1px; border-width:.5rem .5rem 0; border-top-color:#fff
bottom:1px; border-width:.5rem .5rem 0; border-top-color: var(--secondary)
}
.bs-popover-auto[x-placement^=right],.bs-popover-right {
margin-left:.5rem
@ -4265,7 +4265,7 @@ a.close.disabled {
left:0; border-width:.5rem .5rem .5rem 0; border-right-color:rgba(0,0,0,.25)
}
.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after {
left:1px; border-width:.5rem .5rem .5rem 0; border-right-color:#fff
left:1px; border-width:.5rem .5rem .5rem 0; border-right-color: var(--secondary)
}
.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom {
margin-top:.5rem
@ -4277,7 +4277,7 @@ a.close.disabled {
top:0; border-width:0 .5rem .5rem .5rem; border-bottom-color:rgba(0,0,0,.25)
}
.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after {
top:1px; border-width:0 .5rem .5rem .5rem; border-bottom-color:#fff
top:1px; border-width:0 .5rem .5rem .5rem; border-bottom-color: var(--secondary)
}
.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before {
position:absolute; top:0; left:50%; display:block; width:1rem; margin-left:-.5rem; content:""; border-bottom:1px solid #f7f7f7
@ -4292,7 +4292,7 @@ a.close.disabled {
right:0; border-width:.5rem 0 .5rem .5rem; border-left-color:rgba(0,0,0,.25)
}
.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after {
right:1px; border-width:.5rem 0 .5rem .5rem; border-left-color:#fff
right:1px; border-width:.5rem 0 .5rem .5rem; border-left-color: var(--secondary)
}
.popover-header {
padding:.5rem .75rem; margin-bottom:0; font-size:.9375rem; background-color:#f7f7f7; border-bottom:1px solid #ebebeb; border-top-left-radius:calc(.3rem - 1px); border-top-right-radius:calc(.3rem - 1px)

@ -5253,7 +5253,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after {
bottom:1px;
border-width:.5rem .5rem 0;
border-top-color:#fff
border-top-color: var(--secondary)
}
.bs-popover-auto[x-placement^=right],.bs-popover-right {
margin-left:.5rem
@ -5272,7 +5272,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after {
left:1px;
border-width:.5rem .5rem .5rem 0;
border-right-color:#fff
border-right-color: var(--secondary)
}
.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom {
margin-top:.5rem
@ -5288,7 +5288,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after {
top:1px;
border-width:0 .5rem .5rem .5rem;
border-bottom-color:#fff
border-bottom-color: var(--secondary)
}
.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before {
position:absolute;
@ -5317,7 +5317,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after {
right:1px;
border-width:.5rem 0 .5rem .5rem;
border-left-color:#fff
border-left-color: var(--secondary)
}
.popover-header {
padding:.5rem .75rem;
@ -5486,7 +5486,6 @@ a.close.disabled {
.carousel-indicators li {
transition:none
}
}
.carousel-indicators .active {
opacity:1

@ -5310,7 +5310,7 @@ a.close.disabled {
.bs-popover-top > .arrow::after {
bottom: 1px;
border-width: 5px 5px 0;
border-top-color: #000;
border-top-color: var(--secondary);
}
.bs-popover-auto[x-placement^="right"],
.bs-popover-right {
@ -5333,7 +5333,7 @@ a.close.disabled {
.bs-popover-right > .arrow::after {
left: 1px;
border-width: 5px 5px 5px 0;
border-right-color: #000;
border-right-color: var(--secondary);
}
.bs-popover-auto[x-placement^="bottom"],
.bs-popover-bottom {
@ -5353,7 +5353,7 @@ a.close.disabled {
.bs-popover-bottom > .arrow::after {
top: 1px;
border-width: 0 5px 5px 5px;
border-bottom-color: #000;
border-bottom-color: var(--secondary);
}
.bs-popover-auto[x-placement^="bottom"] .popover-header::before,
.bs-popover-bottom .popover-header::before {
@ -5387,7 +5387,7 @@ a.close.disabled {
.bs-popover-left > .arrow::after {
right: 1px;
border-width: 5px 0 5px 5px;
border-left-color: #000;
border-left-color: var(--secondary);
}
.popover-header {
padding: 8px 14px;

@ -5773,7 +5773,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="top"] > .arrow::after {
bottom: 1px;
border-width: 0.5rem 0.5rem 0;
border-top-color: #fff;
border-top-color: var(--secondary);
}
.bs-popover-right,
@ -5797,7 +5797,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="right"] > .arrow::after {
left: 1px;
border-width: 0.5rem 0.5rem 0.5rem 0;
border-right-color: #fff;
border-right-color: var(--secondary);
}
.bs-popover-bottom,
@ -5818,7 +5818,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="bottom"] > .arrow::after {
top: 1px;
border-width: 0 0.5rem 0.5rem 0.5rem;
border-bottom-color: #fff;
border-bottom-color: var(--secondary);
}
.bs-popover-bottom .popover-header::before,
.bs-popover-auto[x-placement^="bottom"] .popover-header::before {
@ -5853,7 +5853,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="left"] > .arrow::after {
right: 1px;
border-width: 0.5rem 0 0.5rem 0.5rem;
border-left-color: #fff;
border-left-color: var(--secondary);
}
.popover-header {

@ -5312,7 +5312,7 @@ a.close.disabled {
.bs-popover-top > .arrow::after {
bottom: 1px;
border-width: 5px 5px 0;
border-top-color: #000;
border-top-color: var(--secondary);
}
.bs-popover-auto[x-placement^="right"],
.bs-popover-right {
@ -5335,7 +5335,7 @@ a.close.disabled {
.bs-popover-right > .arrow::after {
left: 1px;
border-width: 5px 5px 5px 0;
border-right-color: #000;
border-right-color: var(--secondary);
}
.bs-popover-auto[x-placement^="bottom"],
.bs-popover-bottom {
@ -5355,7 +5355,7 @@ a.close.disabled {
.bs-popover-bottom > .arrow::after {
top: 1px;
border-width: 0 5px 5px 5px;
border-bottom-color: #000;
border-bottom-color: var(--secondary);
}
.bs-popover-auto[x-placement^="bottom"] .popover-header::before,
.bs-popover-bottom .popover-header::before {
@ -5389,7 +5389,7 @@ a.close.disabled {
.bs-popover-left > .arrow::after {
right: 1px;
border-width: 5px 0 5px 5px;
border-left-color: #000;
border-left-color: var(--secondary);
}
.popover-header {
padding: 8px 14px;

@ -5323,7 +5323,7 @@ a.close.disabled {
.bs-popover-top > .arrow::after {
bottom: 1px;
border-width: 5px 5px 0;
border-top-color: #000;
border-top-color: var(--secondary);
}
.bs-popover-auto[x-placement^="right"],
.bs-popover-right {
@ -5346,7 +5346,7 @@ a.close.disabled {
.bs-popover-right > .arrow::after {
left: 1px;
border-width: 5px 5px 5px 0;
border-right-color: #000;
border-right-color: var(--secondary);
}
.bs-popover-auto[x-placement^="bottom"],
.bs-popover-bottom {
@ -5366,7 +5366,7 @@ a.close.disabled {
.bs-popover-bottom > .arrow::after {
top: 1px;
border-width: 0 5px 5px 5px;
border-bottom-color: #000;
border-bottom-color: var(--secondary);
}
.bs-popover-auto[x-placement^="bottom"] .popover-header::before,
.bs-popover-bottom .popover-header::before {
@ -5400,7 +5400,7 @@ a.close.disabled {
.bs-popover-left > .arrow::after {
right: 1px;
border-width: 5px 0 5px 5px;
border-left-color: #000;
border-left-color: var(--secondary);
}
.popover-header {
padding: 8px 14px;

@ -5769,7 +5769,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="top"] > .arrow::after {
bottom: 1px;
border-width: 0.5rem 0.5rem 0;
border-top-color: #fff;
border-top-color: var(--secondary);
}
.bs-popover-right,
@ -5793,7 +5793,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="right"] > .arrow::after {
left: 1px;
border-width: 0.5rem 0.5rem 0.5rem 0;
border-right-color: #fff;
border-right-color: var(--secondary);
}
.bs-popover-bottom,
@ -5814,7 +5814,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="bottom"] > .arrow::after {
top: 1px;
border-width: 0 0.5rem 0.5rem 0.5rem;
border-bottom-color: #fff;
border-bottom-color: var(--secondary);
}
.bs-popover-bottom .popover-header::before,
.bs-popover-auto[x-placement^="bottom"] .popover-header::before {
@ -5849,7 +5849,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="left"] > .arrow::after {
right: 1px;
border-width: 0.5rem 0 0.5rem 0.5rem;
border-left-color: #fff;
border-left-color: var(--secondary);
}
.popover-header {

@ -5775,7 +5775,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="top"] > .arrow::after {
bottom: 1px;
border-width: 0.5rem 0.5rem 0;
border-top-color: #eeede9;
border-top-color: var(--secondary);
}
.bs-popover-right,
@ -5799,7 +5799,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="right"] > .arrow::after {
left: 1px;
border-width: 0.5rem 0.5rem 0.5rem 0;
border-right-color: #eeede9;
border-right-color: var(--secondary);
}
.bs-popover-bottom,
@ -5820,7 +5820,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="bottom"] > .arrow::after {
top: 1px;
border-width: 0 0.5rem 0.5rem 0.5rem;
border-bottom-color: #eeede9;
border-bottom-color: var(--secondary);
}
.bs-popover-bottom .popover-header::before,
.bs-popover-auto[x-placement^="bottom"] .popover-header::before {
@ -5855,7 +5855,7 @@ a.close.disabled {
.bs-popover-auto[x-placement^="left"] > .arrow::after {
right: 1px;
border-width: 0.5rem 0 0.5rem 0.5rem;
border-left-color: #eeede9;
border-left-color: var(--secondary);
}
.popover-header {
@ -6141,7 +6141,7 @@ button.bg-primary:focus {
}
.bg-secondary {
background-color: #EDC2A1 !important;
background-color: #f1f1f100 !important;
}
a.bg-secondary:hover,

@ -23,7 +23,7 @@ const profile = {
name: 'blockchain',
displayName: 'Blockchain',
description: 'Blockchain - Logic',
methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'web3VM', 'getProvider'],
methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'getProvider'],
version: packageJson.version
}

@ -7,6 +7,7 @@ class VMProvider {
this.executionContext = executionContext
this.worker = null
this.provider = null
this.newAccountCallback = {}
}
getAccounts (cb) {
@ -49,23 +50,30 @@ class VMProvider {
} else {
reject(new Error(msg.data.error))
}
} else if (msg.data.cmd === 'newAccountResult') {
if (this.newAccountCallback[msg.data.stamp]) {
this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result)
delete this.newAccountCallback[msg.data.stamp]
}
})
}
})
this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']})
})
})
}
// TODO: is still here because of the plugin API
// can be removed later when we update the API
createVMAccount (newAccount) {
const { privateKey, balance } = newAccount
this.RemixSimulatorProvider.Accounts._addAccount(privateKey, balance)
this.worker.postMessage({ cmd: 'addAccount', privateKey: privateKey, balance })
const privKey = Buffer.from(privateKey, 'hex')
return '0x' + privateToAddress(privKey).toString('hex')
}
newAccount (_passwordPromptCb, cb) {
this.RemixSimulatorProvider.Accounts.newAccount(cb)
const stamp = Date.now()
this.newAccountCallback[stamp] = cb
this.worker.postMessage({ cmd: 'newAccount', stamp })
}
async getBalanceInEther (address) {

@ -42,6 +42,36 @@ self.onmessage = (e: MessageEvent) => {
}
break
}
}
case 'addAccount':
{
if (provider) {
provider.Accounts._addAccount(data.privateKey, data.balance)
}
break
}
case 'newAccount':
{
if (provider) {
provider.Accounts.newAccount((error, address: string) => {
if (error) {
self.postMessage({
cmd: 'newAccountResult',
error,
stamp: data.stamp
})
} else {
self.postMessage({
cmd: 'newAccountResult',
result: address,
stamp: data.stamp
})
}
})
}
break
}
}
}

@ -1,6 +1,6 @@
<!DOCTYPE html>
<html>
<html class="overflow-hidden">
<head>
<meta charset="utf-8">

@ -11,7 +11,7 @@ const requiredModules = [ // services + layout views + system views
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy',
'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-trustwallet', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin',
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener', 'doc-gen', 'doc-viewer']
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener', 'doc-gen', 'doc-viewer', 'solidity-script']
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither']

@ -3,21 +3,17 @@ import { canUseWorker, urlFromVersion } from './compiler-utils'
import { CompilerAbstract } from './compiler-abstract'
import { Compiler } from './compiler'
export const compile = async (compilationTargets, settings, contentResolverCallback) => {
const res = await (() => {
return new Promise((resolve, reject) => {
console.log('compilationTargets', compilationTargets)
const compiler = new Compiler(contentResolverCallback)
compiler.set('evmVersion', settings.evmVersion)
compiler.set('optimize', settings.optimize)
compiler.set('language', settings.language)
compiler.set('runs', settings.runs)
compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version))
compiler.event.register('compilationFinished', (success, compilationData, source, input, version) => {
resolve(new CompilerAbstract(settings.version, compilationData, source, input))
})
compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, ''))
export const compile = (compilationTargets, settings, contentResolverCallback): Promise<CompilerAbstract> => {
return new Promise((resolve, reject) => {
const compiler = new Compiler(contentResolverCallback)
compiler.set('evmVersion', settings.evmVersion)
compiler.set('optimize', settings.optimize)
compiler.set('language', settings.language)
compiler.set('runs', settings.runs)
compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version))
compiler.event.register('compilationFinished', (success, compilationData, source, input, version) => {
resolve(new CompilerAbstract(settings.version, compilationData, source, input))
})
})()
return res
compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, ''))
})
}

@ -9,6 +9,7 @@ import AppDialogs from './components/modals/dialogs'
import DialogViewPlugin from './components/modals/dialogViewPlugin'
import { AppContext } from './context/context'
import { IntlProvider } from 'react-intl'
import { CustomTooltip } from '@remix-ui/helper';
interface IRemixAppUi {
app: any
@ -87,8 +88,15 @@ const RemixApp = (props: IRemixAppUi) => {
<div id="icon-panel" data-id="remixIdeIconPanel" className="iconpanel bg-light">{props.app.menuicons.render()}</div>
<div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>{props.app.sidePanel.render()}</div>
<DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={285} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar>
<div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'>
<div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel d-flex'>
<RemixUIMainPanel Context={AppContext}></RemixUIMainPanel>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-all-tabs"
tooltipText="Scroll to see all tabs"
>
<div className='remix-ui-tabs_end remix-bg-opacity position-absolute position-fixed'></div>
</CustomTooltip>
</div>
</div>
<div>{props.app.hiddenPanel.render()}</div>

@ -34,7 +34,6 @@ pre {
width : 320px;
transition : width 0.25s;
}
.highlightcode {
position : absolute;
z-index : 20;
@ -54,19 +53,24 @@ pre {
height : 200px;
}
.centered svg path {
fill: var(--secondary);
fill : var(--secondary);
}
.centered svg polygon {
fill : var(--secondary);
fill : var(--secondary);
}
.onboarding {
color : var(--text-info);
background-color : var(--info);
color : var(--text-info);
background-color : var(--info);
}
.matomoBtn {
width : 100px;
}
.splash {
text-align: center;
text-align : center;
}
.remix-ui-tabs_end {
height : 2.15rem;
width : 4rem;
right : -10px;
filter : opacity(0.5);
}

@ -38,11 +38,11 @@ export const RemixUiCheckbox = ({
}: RemixUiCheckboxProps) => {
const childJSXWithTooltip = (
<CustomTooltip
tooltipText={title}
tooltipId={`${name}Tooltip`}
placement={tooltipPlacement}
>
<CustomTooltip
tooltipText={title}
tooltipId={`${name}Tooltip`}
placement={tooltipPlacement}
>
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
<input
id={id}
@ -58,8 +58,8 @@ export const RemixUiCheckbox = ({
{label}
</label>
</div>
</CustomTooltip>
)
</CustomTooltip>
)
const childJSX = (
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
<input

@ -384,20 +384,31 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
<div className="px-2" ref={debuggerTopRef}>
<div>
<div className="mt-2 mb-2 debuggerConfig custom-control custom-checkbox">
<CustomTooltip
tooltipId="debuggerGenSourceCheckbox"
tooltipText={<FormattedMessage id='debugger.debugWithGeneratedSources' />}
placement="top-start"
>
{customJSX}
</CustomTooltip>
<CustomTooltip
tooltipId="debuggerGenSourceCheckbox"
tooltipText={<FormattedMessage id='debugger.debugWithGeneratedSources' />}
placement="top-start"
>
{customJSX}
</CustomTooltip>
</div>
{ state.isLocalNodeUsed && <div className="mb-2 debuggerConfig custom-control custom-checkbox">
<input className="custom-control-input" id="debugWithLocalNodeInput" onChange={({ target: { checked } }) => {
setState(prevState => {
return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } }
})
}} type="checkbox" title="Force the debugger to use the current local node" />
<CustomTooltip
tooltipId="debuggerGenSourceInput"
tooltipText="Force the debugger to use the current local node"
placement='right'
>
<input
className="custom-control-input"
id="debugWithLocalNodeInput"
onChange={({ target: { checked } }) => {
setState(prevState => {
return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } }
})
}}
type="checkbox"
/>
</CustomTooltip>
<label data-id="debugLocaNodeLabel" className="form-check-label custom-control-label" htmlFor="debugWithLocalNodeInput"><FormattedMessage id='debugger.debugLocaNodeLabel' /></label>
</div>
}

@ -0,0 +1,15 @@
import { EditorAPIType, PluginType } from "../remix-ui-editor"
export const retrieveNodesAtPosition = async (editorAPI: EditorAPIType, plugin: PluginType) => {
const cursorPosition = editorAPI.getCursorPosition()
let nodesAtPosition = await plugin.call('codeParser', 'nodesAtPosition', cursorPosition)
// if no nodes exits at position, try to get the block of which the position is in
const block = await plugin.call('codeParser', 'getANTLRBlockAtPosition', cursorPosition, null)
if (!nodesAtPosition.length) {
if (block) {
nodesAtPosition = await plugin.call('codeParser', 'nodesAtPosition', block.start)
}
}
return { nodesAtPosition, block }
}

@ -3,6 +3,7 @@ import { isArray } from "lodash"
import { EditorUIProps } from "../remix-ui-editor"
import { GeCompletionUnits, GetCompletionKeywords, getCompletionSnippets, GetCompletionTypes, getContextualAutoCompleteBTypeName, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable, GetImports } from "./completion/completionGlobals"
import { monacoTypes } from '@remix-ui/editor';
import { retrieveNodesAtPosition } from "../helpers/retrieveNodesAtPosition";
export class RemixCompletionProvider implements monacoTypes.languages.CompletionItemProvider {
props: EditorUIProps
@ -251,17 +252,8 @@ export class RemixCompletionProvider implements monacoTypes.languages.Completion
private getContractCompletions = async () => {
let nodes: any[] = []
const cursorPosition = this.props.editorAPI.getCursorPosition()
let nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', cursorPosition)
// if no nodes exits at position, try to get the block of which the position is in
const block = await this.props.plugin.call('codeParser', 'getANTLRBlockAtPosition', cursorPosition, null)
const { nodesAtPosition, block } = await retrieveNodesAtPosition(this.props.editorAPI, this.props.plugin)
const fileNodes = await this.props.plugin.call('codeParser', 'getCurrentFileNodes')
if (!nodesAtPosition.length) {
if (block) {
nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', block.start)
}
}
// find the contract and get the nodes of the contract and the base contracts and imports
if (isArray(nodesAtPosition) && nodesAtPosition.length) {
let contractNode: any = {}

@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import { isArray } from "lodash"
import Editor, { loader, Monaco } from '@monaco-editor/react'
import { AlertModal } from '@remix-ui/app'
import { reducerActions, reducerListener, initialState } from './actions/editor'
@ -8,15 +8,14 @@ import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo'
import { zokratesTokensProvider, zokratesLanguageConfig } from './syntaxes/zokrates'
import { moveTokenProvider, moveLanguageConfig } from './syntaxes/move'
import { monacoTypes } from '@remix-ui/editor';
import './remix-ui-editor.css'
import { loadTypes } from './web-types'
import { retrieveNodesAtPosition } from './helpers/retrieveNodesAtPosition'
import { RemixHoverProvider } from './providers/hoverProvider'
import { RemixReferenceProvider } from './providers/referenceProvider'
import { RemixCompletionProvider } from './providers/completionProvider'
import { RemixHighLightProvider } from './providers/highlightProvider'
import { RemixDefinitionProvider } from './providers/definitionProvider'
import './remix-ui-editor.css'
enum MarkerSeverity {
@ -93,6 +92,24 @@ export type DecorationsReturn = {
registeredDecorations?: Array<any>
}
export type PluginType = {
on: (plugin: string, event: string, listener: any) => void
call: (plugin: string, method: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any) => any
}
export type EditorAPIType = {
findMatches: (uri: string, value: string) => any
getFontSize: () => number,
getValue: (uri: string) => string
getCursorPosition: (offset?: boolean) => number | monacoTypes.IPosition
getHoverPosition: (position: monacoTypes.IPosition) => number
addDecoration: (marker: sourceMarker, filePath: string, typeOfDecoration: string) => DecorationsReturn
clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
addErrorMarker: (errors: errorMarker[], from: string) => void
clearErrorMarkers: (sources: string[] | {[fileName: string]: any}, from: string) => void
}
/* eslint-disable-next-line */
export interface EditorUIProps {
contextualListener: any
@ -105,22 +122,8 @@ export interface EditorUIProps {
onDidChangeContent: (file: string) => void
onEditorMounted: () => void
}
plugin: {
on: (plugin: string, event: string, listener: any) => void
call: (plugin: string, method: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any) => any
}
editorAPI: {
findMatches: (uri: string, value: string) => any
getFontSize: () => number,
getValue: (uri: string) => string
getCursorPosition: (offset?: boolean) => number | monacoTypes.IPosition
getHoverPosition: (position: monacoTypes.IPosition) => number
addDecoration: (marker: sourceMarker, filePath: string, typeOfDecoration: string) => DecorationsReturn
clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
addErrorMarker: (errors: errorMarker[], from: string) => void
clearErrorMarkers: (sources: string[] | {[fileName: string]: any}, from: string) => void
}
plugin: PluginType
editorAPI: EditorAPIType
}
export const EditorUI = (props: EditorUIProps) => {
const [, setCurrentBreakpoints] = useState({})
@ -630,9 +633,67 @@ export const EditorUI = (props: EditorUIProps) => {
await props.plugin.call('codeFormatter', 'format', file)
},
}
const freeFunctionCondition = editor.createContextKey('freeFunctionCondition', false);
let freeFunctionAction
const executeFreeFunctionAction = {
id: "executeFreeFunction",
label: "Run a free function in the Remix VM",
contextMenuOrder: 0, // choose the order
contextMenuGroupId: "execute", // create a new grouping
precondition: 'freeFunctionCondition',
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR,
],
run: async () => {
const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin)
// find the contract and get the nodes of the contract and the base contracts and imports
if (nodesAtPosition && isArray(nodesAtPosition) && nodesAtPosition.length) {
const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction')
if (freeFunctionNode) {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
props.plugin.call('solidity-script', 'execute', file, freeFunctionNode.name)
} else {
props.plugin.call('notification', 'toast', 'This can only execute free function')
}
} else {
props.plugin.call('notification', 'toast', 'Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.')
}
},
}
editor.addAction(formatAction)
editor.addAction(zoomOutAction)
editor.addAction(zoominAction)
editor.addAction(executeFreeFunctionAction)
// we have to add the command because the menu action isn't always available (see onContextMenuHandlerForFreeFunction)
editor.addCommand(monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, () => executeFreeFunctionAction.run())
const contextmenu = editor.getContribution('editor.contrib.contextmenu')
const orgContextMenuMethod = contextmenu._onContextMenu;
const onContextMenuHandlerForFreeFunction = async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
if (!file.endsWith('.sol')) {
freeFunctionCondition.set(false)
return
}
const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin)
const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction')
if (freeFunctionAction) freeFunctionAction.dispose()
if (freeFunctionNode) {
executeFreeFunctionAction.label = `Run the free function "${freeFunctionNode.name}" in the Remix VM`
freeFunctionAction = editor.addAction(executeFreeFunctionAction)
}
freeFunctionCondition.set(!!freeFunctionNode)
}
contextmenu._onContextMenu = (...args) => {
if (args[0]) args[0].event?.preventDefault()
onContextMenuHandlerForFreeFunction()
.then(() => orgContextMenuMethod.apply(contextmenu, args))
.catch(() => orgContextMenuMethod.apply(contextmenu, args))
}
const editorService = editor._codeEditorService;
const openEditorBase = editorService.openCodeEditor.bind(editorService);
editorService.openCodeEditor = async (input , source) => {

@ -8,44 +8,40 @@ import FileDecorationTooltip from './filedecorationicons/file-decoration-tooltip
import FileDecorationWarningIcon from './filedecorationicons/file-decoration-warning-icon'
export type fileDecorationProps = {
file: FileType,
fileDecorations: fileDecoration[]
file: FileType,
fileDecorations: fileDecoration[]
}
export const FileDecorationIcons = (props: fileDecorationProps) => {
const [states, setStates] = useState<fileDecoration[]>([])
useEffect(() => {
//console.log(props.file)
//console.log(props.fileState)
setStates(props.fileDecorations.filter((fileDecoration) => fileDecoration.path === props.file.path || `${fileDecoration.workspace.name}/${fileDecoration.path}` === props.file.path))
}, [props.fileDecorations])
const [states, setStates] = useState<fileDecoration[]>([])
useEffect(() => {
setStates(props.fileDecorations.filter((fileDecoration) => fileDecoration.path === props.file.path || `${fileDecoration.workspace.name}/${fileDecoration.path}` === props.file.path))
}, [props.fileDecorations])
const getTags = function () {
if (states && states.length) {
const elements: JSX.Element[] = []
const getTags = function () {
if (states && states.length) {
const elements: JSX.Element[] = []
for (const [index, state] of states.entries()) {
switch (state.fileStateType) {
case fileDecorationType.Error:
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationErrorIcon fileDecoration={state} key={index}/>}/>)
break
case fileDecorationType.Warning:
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationWarningIcon fileDecoration={state} key={index}/>}/>)
break
case fileDecorationType.Custom:
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationCustomIcon fileDecoration={state} key={index}/>}/>)
break
}
}
return elements
for (const [index, state] of states.entries()) {
switch (state.fileStateType) {
case fileDecorationType.Error:
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationErrorIcon fileDecoration={state} key={index}/>}/>)
break
case fileDecorationType.Warning:
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationWarningIcon fileDecoration={state} key={index}/>}/>)
break
case fileDecorationType.Custom:
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationCustomIcon fileDecoration={state} key={index}/>}/>)
break
}
}
return elements
}
}
return <>
{getTags()}
</>
return <>
{getTags()}
</>
}
export default FileDecorationIcons

@ -1,33 +1,35 @@
import React from "react";
import { OverlayTrigger, Tooltip } from "react-bootstrap";
import { OverlayTrigger, Popover } from "react-bootstrap";
import { fileDecoration } from "../../types";
const FileDecorationTooltip = (props: {
fileDecoration: fileDecoration,
icon: JSX.Element
index: number
fileDecoration: fileDecoration,
icon: JSX.Element
index: number
},
) => {
const getComments = function (fileDecoration: fileDecoration) {
if (fileDecoration.comment) {
const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment]
return comments.map((comment, index) => {
return <div key={index}>{comment}<br></br></div>
})
}
const getComments = function (fileDecoration: fileDecoration) {
if (fileDecoration.comment) {
const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment]
return comments.map((comment, index) => {
return <div className="bg-secondary text-left p-1 mx-1 my-0" key={index}>{comment}</div>
})
}
}
return <OverlayTrigger
key={`overlaytrigger-${props.fileDecoration.path}-${props.index}`}
placement='auto'
overlay={
<Tooltip id={`error-tooltip-${props.fileDecoration.path}`}>
<>{getComments(props.fileDecoration)}</>
</Tooltip>
}
><div>{props.icon}</div></OverlayTrigger>
return <OverlayTrigger
key={`overlaytrigger-${props.fileDecoration.path}-${props.index}`}
placement='auto'
overlay={
<Popover id={`popover-positioned-auto}`}>
<Popover.Content id={`error-tooltip-${props.fileDecoration.path}`} style={{minWidth: "fit-content"}} className={"text-wrap bg-secondary w-100 p-1 m-0"}>
<pre>{getComments(props.fileDecoration)}</pre>
</Popover.Content>
</Popover>
}
>
<div>{props.icon}</div>
</OverlayTrigger>
}
export default FileDecorationTooltip;

@ -1,19 +1,27 @@
import React from 'react';
import { Fragment } from 'react';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { OverlayTrigger, Popover } from 'react-bootstrap';
import { CustomTooltipType } from '../../types/customtooltip'
export function CustomTooltip ({ children, placement, tooltipId, tooltipClasses, tooltipText, tooltipTextClasses, delay }: CustomTooltipType) {
export function CustomTooltip({ children, placement, tooltipId, tooltipClasses, tooltipText, tooltipTextClasses, delay }: CustomTooltipType) {
if (typeof tooltipText !== 'string') {
const newTooltipText = React.cloneElement(tooltipText, {
className: " bg-secondary text-wrap p-1 px-2 "
})
tooltipText = newTooltipText
}
return (
<Fragment>
<OverlayTrigger
placement={placement}
overlay={
<Tooltip id={!tooltipId ? `${tooltipText}Tooltip` : tooltipId} className={tooltipClasses}>
{typeof tooltipText === 'string' ? (<span className={tooltipTextClasses}>{tooltipText}</span>) : (tooltipText)}
</Tooltip>
<Popover id={`popover-positioned-${placement}`}>
<Popover.Content id={!tooltipId ? `${tooltipText}Tooltip` : tooltipId} style={{minWidth: "fit-content"}} className={"text-wrap p-1 px-2 bg-secondary w-100" + tooltipClasses}>
{typeof tooltipText === 'string' ? (<span className={"text-wrap p-1 px-2 bg-secondary " + {tooltipTextClasses}}>{tooltipText}</span>) : (tooltipText)}
</Popover.Content>
</Popover>
}
delay={delay}
>

@ -6,7 +6,7 @@ import { CustomTooltip } from '@remix-ui/helper'
const _paq = window._paq = window._paq || [] // eslint-disable-line
function HomeTabTitle() {
function HomeTabTitle () {
useEffect(() => {
document.addEventListener("keyup", (e) => handleSearchKeyDown(e))
return () => {
@ -184,4 +184,4 @@ function HomeTabTitle() {
)
}
export default HomeTabTitle
export default HomeTabTitle

@ -32,7 +32,7 @@ function PluginButton ({ imgPath, envID, envText, callback, l2, description, rem
{ remixMaintained &&
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-run-script"
tooltipId="overlay-tooltip-by-remix"
tooltipText={'Maintained by Remix'}
>
<i className="bg-light text-success mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel fas fa-check"></i>

@ -87,7 +87,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
</h6>
{!props.showCancelIcon &&
<span className="modal-close" onClick={() => handleHide()}>
<i title="Close" className="fas fa-times" aria-hidden="true"></i>
<i className="fas fa-times" aria-hidden="true"></i>
</span>
}
</div>

@ -3,6 +3,6 @@
flex-direction : column;
height : 100%;
width : 100%;
position: relative;
position : relative;
}

@ -2,7 +2,6 @@ import React, { useEffect, useRef, useState } from 'react' // eslint-disable-lin
import { FormattedMessage } from 'react-intl'
import { PluginRecord } from '../types'
import './panel.css'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { CustomTooltip } from '@remix-ui/helper'
export interface RemixPanelProps {
@ -39,16 +38,14 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
<div className="d-flex flex-row">
<div className="d-flex flex-row">
{plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && (
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="maintainedByTooltip" className="text-nowrap">
<span>{"Maintained by Remix"}</span>
</Tooltip>
}
>
<CustomTooltip
placement="right-end"
tooltipId="maintainedByTooltip"
tooltipClasses="text-nowrap"
tooltipText="Maintained by Remix"
>
<i aria-hidden="true" className="text-success mt-1 px-1 fas fa-check"></i>
</OverlayTrigger>
</CustomTooltip>
)}
</div>
<div className="swapitHeaderInfoSection d-flex justify-content-between" data-id='swapitHeaderInfoSectionId' onClick={toggleClass}>
@ -63,7 +60,6 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
</div>
</div>
</div>
<div className="d-flex w-100 flex-row py-2"></div>
<div className={`bg-light mx-3 mb-2 p-3 pt-1 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
{plugin?.profile?.author && <span className="d-flex flex-row align-items-center">
<label className="mb-0 pr-2"><FormattedMessage id='panel.author' />:</label>
@ -76,7 +72,14 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
{plugin?.profile?.documentation && <span className="d-flex flex-row align-items-center">
<label className="mb-0 pr-2"><FormattedMessage id='panel.documentation' />:</label>
<span>
<a href={plugin?.profile?.documentation} className="titleInfo p-0 mb-2" title="link to documentation" target="_blank" rel="noreferrer"><i aria-hidden="true" className="fas fa-book"></i></a>
<CustomTooltip
placement="right-end"
tooltipId="linkToDocsTooltip"
tooltipClasses=" text-nowrap "
tooltipText="Link to documentation"
>
<a href={plugin?.profile?.documentation} className="titleInfo p-0 mb-2" target="_blank" rel="noreferrer"><i aria-hidden="true" className="fas fa-book"></i></a>
</CustomTooltip>
</span>
</span>}
{plugin?.profile?.description && <span className="d-flex flex-row align-items-baseline">

@ -1,6 +1,7 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-use-before-define
import React from 'react'
import '../remix-ui-plugin-manager.css'
import { CustomTooltip } from '@remix-ui/helper'
interface PluginCardProps {
profile: any
buttonText: string
@ -14,35 +15,72 @@ function ActivePluginCard ({
}: PluginCardProps) {
return (
<div className="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentActiveTile">
<article className="list-group-item py-1 mb-1 plugins-list-group-item" title={profile.displayName || profile.name}>
<article className="list-group-item py-1 mb-1 plugins-list-group-item">
<div className="remixui_row justify-content-between align-items-center mb-2">
<h6 className="remixui_displayName plugin-name">
<div>
{ profile.displayName || profile.name }
{ profile?.maintainedBy?.toLowerCase() == "remix" &&
<i aria-hidden="true" className="px-1 text-success fas fa-check" title="Maintained by Remix"></i>
<CustomTooltip
placement="right"
tooltipId="pluginManagerActiveTitleByRemix"
tooltipClasses="text-nowrap"
tooltipText="Maintained by Remix"
>
<i aria-hidden="true" className="px-1 text-success fas fa-check"></i>
</CustomTooltip>
}
{ profile.documentation &&
<a href={profile.documentation} className="px-1" title="link to documentation" target="_blank" rel="noreferrer">
<i aria-hidden="true" className="fas fa-book"/>
</a>
<CustomTooltip
placement="right"
tooltipId="pluginManagerActiveLinkToDoc"
tooltipClasses="text-nowrap"
tooltipText="Link to documentation"
>
<a href={profile.documentation} className="px-1" target="_blank" rel="noreferrer">
<i aria-hidden="true" className="fas fa-book"/>
</a>
</CustomTooltip>
}
{ profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)
? <small title="Version Alpha" className="remixui_versionWarning plugin-version">alpha</small>
? <CustomTooltip
placement="right"
tooltipId="pluginManagerActiveVersionAlpha"
tooltipClasses="text-nowrap"
tooltipText="Version Alpha"
>
<small className="remixui_versionWarning plugin-version">alpha</small>
</CustomTooltip>
: profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)
? <small title="Version Beta" className="remixui_versionWarning plugin-version">beta</small>
? <CustomTooltip
placement="right"
tooltipId="pluginManagerActiveVersionBeta"
tooltipClasses="text-nowrap"
tooltipText="Version Beta"
>
<small className="remixui_versionWarning plugin-version">beta</small>
</CustomTooltip>
: null
}
</div>
{<button
onClick={() => {
deactivatePlugin(profile.name)
} }
className="btn btn-secondary btn-sm"
data-id={`pluginManagerComponentDeactivateButton${profile.name}`}
>
{buttonText}
</button>}
{
<CustomTooltip
placement="right"
tooltipId={`pluginManagerInactiveActiveBtn${profile.name}`}
tooltipClasses="text-nowrap"
tooltipText={`Dectivate ${profile.displayName || profile.name}`}
>
<button
onClick={() => {
deactivatePlugin(profile.name)
} }
className="btn btn-secondary btn-sm"
data-id={`pluginManagerComponentDeactivateButton${profile.name}`}
>
{buttonText}
</button>
</CustomTooltip>
}
</h6>
</div>
<div className="remixui_description d-flex text-body plugin-text mb-2">

@ -2,6 +2,7 @@ import { Profile } from '@remixproject/plugin-utils'
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-use-before-define
import React from 'react'
import '../remix-ui-plugin-manager.css'
import { CustomTooltip } from '@remix-ui/helper'
interface PluginCardProps {
profile: Profile & {
icon?: string
@ -17,36 +18,71 @@ function InactivePluginCard ({
}: PluginCardProps) {
return (
<div className="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentActiveTile">
<article className="list-group-item py-1 mb-1 plugins-list-group-item" title={profile.displayName || profile.name}>
<article className="list-group-item py-1 mb-1 plugins-list-group-item">
<div className="remixui_row justify-content-between align-items-center mb-2">
<h6 className="remixui_displayName plugin-name">
<div>
{ profile.displayName || profile.name }
{ profile?.maintainedBy?.toLowerCase() == "remix" &&
<i aria-hidden="true" className="px-1 text-success fas fa-check" title="Verified by Remix"></i>
<CustomTooltip
placement="right"
tooltipId="pluginManagerInactiveTitleByRemix"
tooltipClasses="text-nowrap"
tooltipText="Maintained by Remix"
>
<i aria-hidden="true" className="px-1 text-success fas fa-check"></i>
</CustomTooltip>
}
{ profile.documentation &&
<a href={profile.documentation} className="px-1" title="link to documentation" target="_blank" rel="noreferrer">
<i aria-hidden="true" className="fas fa-book"/>
</a>
<CustomTooltip
placement="right"
tooltipId="pluginManagerInactiveTitleLinkToDoc"
tooltipClasses="text-nowrap"
tooltipText="Link to documentation"
>
<a href={profile.documentation} className="px-1" target="_blank" rel="noreferrer">
<i aria-hidden="true" className="fas fa-book"/>
</a>
</CustomTooltip>
}
{ profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)
? <small title="Version Alpha" className="remixui_versionWarning plugin-version">alpha</small>
? <CustomTooltip
placement="right"
tooltipId="pluginManagerActiveVersionAlpha"
tooltipClasses="text-nowrap"
tooltipText="Version Alpha"
>
<small className="remixui_versionWarning plugin-version">alpha</small>
</CustomTooltip>
: profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)
? <small title="Version Beta" className="remixui_versionWarning plugin-version">beta</small>
? <CustomTooltip
placement="right"
tooltipId="pluginManagerActiveVersionBeta"
tooltipClasses="text-nowrap"
tooltipText="Version Beta"
>
<small className="remixui_versionWarning plugin-version">beta</small>
</CustomTooltip>
: null
}
</div>
{
<button
onClick={() => {
activatePlugin(profile.name)
}}
className="btn btn-success btn-sm"
data-id={`pluginManagerComponentActivateButton${profile.name}`}
<CustomTooltip
placement="right"
tooltipId={`pluginManagerInactiveActiveBtn${profile.name}`}
tooltipClasses="text-nowrap"
tooltipText={`Activate ${profile.displayName || profile.name}`}
>
{buttonText}
</button>
<button
onClick={() => {
activatePlugin(profile.name)
}}
className="btn btn-success btn-sm"
data-id={`pluginManagerComponentActivateButton${profile.name}`}
>
{buttonText}
</button>
</CustomTooltip>
}
</h6>
</div>

@ -37,6 +37,20 @@ export function AccountUI (props: AccountProps) {
})
break
case 'vm-london':
setPlusOpt({
classList: '',
title: 'Create a new account'
})
break
case 'vm-berlin':
setPlusOpt({
classList: '',
title: 'Create a new account'
})
break
case 'web3':
if (!props.personalMode) {
setPlusOpt({

@ -274,7 +274,13 @@ export function ContractGUI (props: ContractGUIProps) {
<div
className="udapp_contractActionsContainerSingle pt-2"
style={{ display: toggleContainer ? "none" : "flex" }}
>
>
<CustomTooltip
placement={"right"}
tooltipClasses="text-wrap"
tooltipId="remixUdappInstanceButtonTooltip"
tooltipText={toggleUpgradeImp && !proxyAddress ? 'Proxy address cannot be empty' : buttonOptions.title}
>
<button
onClick={handleActionClick}
className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`}
@ -282,61 +288,47 @@ export function ContractGUI (props: ContractGUIProps) {
data-title={buttonOptions.title}
disabled={(toggleUpgradeImp && !proxyAddress) || props.disabled}
>
<CustomTooltip
placement={"right-start"}
tooltipClasses="text-wrap"
tooltipId="remixUdappInstanceButtonTooltip"
tooltipText={toggleUpgradeImp && !proxyAddress ? 'Proxy address cannot be empty' : buttonOptions.title}
>
<div>{title}</div>
</CustomTooltip>
<div>{title}</div>
</button>
<CustomTooltip
placement={"right"}
tooltipClasses="text-nowrap"
tooltipId="remixContractGuiTooltip"
tooltipText={props.funcABI.type === "fallback" ||props.funcABI.type === "receive" ? `'(${props.funcABI.type}')`
: props.inputs}
>
<input
className="form-control"
data-id={
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
? `'(${props.funcABI.type}')`
: "multiParamManagerBasicInputField"
}
placeholder={props.inputs}
onChange={handleBasicInput}
data-title={
</CustomTooltip>
<input
className="form-control"
data-id={
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
? `'(${props.funcABI.type}')`
: "multiParamManagerBasicInputField"
}
placeholder={props.inputs}
onChange={handleBasicInput}
data-title={
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
? `'(${props.funcABI.type}')`
: props.inputs
}
ref={basicInputRef}
style={{
visibility: !(
(props.funcABI.inputs && props.funcABI.inputs.length > 0) ||
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
? `'(${props.funcABI.type}')`
: props.inputs
}
ref={basicInputRef}
style={{
visibility: !(
(props.funcABI.inputs && props.funcABI.inputs.length > 0) ||
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
)
? "hidden"
: "visible",
}}
/>
</CustomTooltip>
<i
className="fas fa-angle-down udapp_methCaret"
onClick={switchMethodViewOn}
style={{
visibility: !(
props.funcABI.inputs && props.funcABI.inputs.length > 0
)
? "hidden"
: "visible",
}}
></i>
)
? "hidden"
: "visible",
}}
/>
<i
className="fas fa-angle-down udapp_methCaret"
onClick={switchMethodViewOn}
style={{
visibility: !(
props.funcABI.inputs && props.funcABI.inputs.length > 0
)
? "hidden"
: "visible",
}}
></i>
</div>
<div
className="udapp_contractActionsContainerMulti"
@ -477,14 +469,14 @@ export function ContractGUI (props: ContractGUIProps) {
{" "}
{inp.name}:{" "}
</label>
<input
ref={(el) => {
initializeFields.current[index] = el;
}}
style={{ height: 32 }}
className="form-control udapp_input"
placeholder={inp.type}
/>
<input
ref={(el) => {
initializeFields.current[index] = el;
}}
style={{ height: 32 }}
className="form-control udapp_input"
placeholder={inp.type}
/>
</div>
);
})}
@ -540,26 +532,26 @@ export function ContractGUI (props: ContractGUIProps) {
tooltipText={'Deployed ' + shortenDate(deployment.date)}
key={index}
>
<Dropdown.Item
key={index}
onClick={() => {
switchProxyAddress(deployment.address)
}}
data-id={`proxyAddress${index}`}
>
<span>{ proxyAddress === deployment.address ? <span>&#10003; { deployment.contractName + ' ' + shortenProxyAddress(deployment.address) } </span> : <span className="pl-3">{ deployment.contractName + ' ' + shortenProxyAddress(deployment.address) }</span> }</span>
</Dropdown.Item>
</CustomTooltip>
))
}
</Dropdown.Menu>
}
</Dropdown>
<Dropdown.Item
key={index}
onClick={() => {
switchProxyAddress(deployment.address)
}}
data-id={`proxyAddress${index}`}
>
<span>{ proxyAddress === deployment.address ? <span>&#10003; { deployment.contractName + ' ' + shortenProxyAddress(deployment.address) } </span> : <span className="pl-3">{ deployment.contractName + ' ' + shortenProxyAddress(deployment.address) }</span> }</span>
</Dropdown.Item>
</CustomTooltip>
))
}
</Dropdown.Menu>
}
</Dropdown>
</div>
<div className='d-flex'>
<div className="mb-2">
{ proxyAddressError && <span className='text-lowercase text-danger' data-id="errorMsgProxyAddress" style={{ fontSize: '.8em' }}>{ proxyAddressError }</span> }
</div>
<div className="mb-2">
{ proxyAddressError && <span className='text-lowercase text-danger' data-id="errorMsgProxyAddress" style={{ fontSize: '.8em' }}>{ proxyAddressError }</span> }
</div>
</div>
</div>
</>

@ -27,7 +27,7 @@ export function DeployButton (props: DeployButtonProps) {
</Dropdown.Menu>
</Dropdown> :
<CustomTooltip
placement="right-start"
placement="top"
tooltipId="deployButtonTooltip"
tooltipClasses="text-nowrap"
tooltipText={props.buttonOptions.title}

@ -8,7 +8,7 @@ export function DeployInput (props: DeployInputProps) {
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: 'flex' }}>
<DeployButton buttonOptions={props.buttonOptions} selectedIndex={props.selectedIndex} setSelectedIndex={props.setSelectedIndex} handleActionClick={props.handleActionClick} deployOptions={props.deployOptions} />
<CustomTooltip
placement="right-start"
placement="right"
tooltipId="deployInputTooltip"
tooltipClasses="text-nowrap"
tooltipText={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs}

@ -43,12 +43,12 @@ export function RecorderUI (props: RecorderProps) {
tooltipText={<FormattedMessage id='udapp.transactionsCountTooltip' />}
>
<div className="ml-2 badge badge-pill badge-primary text-center" data-title="The number of recorded transactions">{props.count}</div>
</CustomTooltip>
<CustomTooltip
placement={'right'}
tooltipClasses="text-wrap"
tooltipId="info-recorder"
tooltipText={<span><FormattedMessage id='udapp.infoRecorderTooltip' values={{ br: <br /> }} /></span>}
</CustomTooltip>
<CustomTooltip
placement={'right'}
tooltipClasses="text-wrap"
tooltipId="info-recorder"
tooltipText={<span><FormattedMessage id='udapp.infoRecorderTooltip' values={{ br: <br /> }} /></span>}
>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
</CustomTooltip>

@ -237,10 +237,8 @@ export function UniversalDappUI (props: UdappProps) {
{props.context})
</span>
</div>
<div className="btn-group">
<button className="btn p-1 btn-secondary">
<CopyToClipboard content={address} direction={"top"} />
</button>
<div className="btn">
<CopyToClipboard content={address} direction={"top"} />
</div>
</div>
<CustomTooltip
@ -249,13 +247,12 @@ export function UniversalDappUI (props: UdappProps) {
tooltipId="udapp_udappCloseTooltip"
tooltipText="Remove from the list"
>
<button
className="udapp_udappClose mr-1 p-1 btn btn-secondary align-items-center"
<i
className="udapp_closeIcon m-1 fas fa-times align-self-center"
aria-hidden="true"
data-id="universalDappUiUdappClose"
onClick={remove}
>
<i className="udapp_closeIcon fas fa-times" aria-hidden="true"></i>
</button>
></i>
</CustomTooltip>
</div>
<div

@ -273,7 +273,7 @@
color: var(--primary);
}
.udapp_titleExpander {
margin-top: 2px;
align-self: center;
}
.udapp_nameNbuts {
display: contents;

@ -150,32 +150,25 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const displayErrorsChecked = props.config.get('settings/display-errors') || false
return (
<div className="$border-top">
<CustomTooltip
tooltipText="Reset to Default settings"
tooltipId="resetDefaultTooltip"
tooltipClasses="text-nowrap"
placement="top-start"
>
<div className='d-flex justify-content-end pr-4'>
<button className="btn btn-sm btn-secondary ml-2" onClick={() => {
try {
if ((window as any).remixFileSystem.name === 'indexedDB') {
props.config.clear()
try {
localStorage.clear() // remove the whole storage
} catch (e) {
console.log(e)
}
} else {
props.config.clear() // remove only the remix settings
<div className='d-flex justify-content-end pr-4'>
<button className="btn btn-sm btn-secondary ml-2" onClick={() => {
try {
if ((window as any).remixFileSystem.name === 'indexedDB') {
props.config.clear()
try {
localStorage.clear() // remove the whole storage
} catch (e) {
console.log(e)
}
refresh(resetState + 1)
} catch (e) {
console.log(e)
} else {
props.config.clear() // remove only the remix settings
}
}}><FormattedMessage id='settings.reset' /></button>
</div>
</CustomTooltip>
refresh(resetState + 1)
} catch (e) {
console.log(e)
}
}}><FormattedMessage id='settings.reset' /></button>
</div>
<div className="card-body pt-3 pb-2">
<h6 className="card-title"><FormattedMessage id='settings.general' /></h6>
<div className="mt-2 custom-control custom-checkbox mb-1">

@ -212,14 +212,14 @@ export const CompilerApiMixin = (Base) => class extends Base {
listenToEvents () {
this.on('editor', 'contentChanged', () => {
this.statusChanged({ key: 'edited', title: 'the content has changed, needs recompilation', type: 'info' })
this.statusChanged({ key: 'edited', title: 'The content has changed, needs recompilation', type: 'info' })
if (this.onContentChanged) this.onContentChanged()
})
this.data.eventHandlers.onLoadingCompiler = (url) => {
this.data.loading = true
this.data.loadingUrl = url
this.statusChanged({ key: 'loading', title: 'loading compiler...', type: 'info' })
this.statusChanged({ key: 'loading', title: 'Loading compiler...', type: 'info' })
this.emit('loadingCompiler', url)
}
this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler)
@ -232,7 +232,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded)
this.data.eventHandlers.onStartingCompilation = () => {
this.statusChanged({ key: 'loading', title: 'compiling...', type: 'info' })
this.statusChanged({ key: 'loading', title: 'Compiling...', type: 'info' })
}
this.data.eventHandlers.onRemoveAnnotations = () => {
@ -288,13 +288,13 @@ export const CompilerApiMixin = (Base) => class extends Base {
const warningsCount = data.errors.length
this.statusChanged({
key: warningsCount,
title: `compilation successful with ${warningsCount} warning${warningsCount > 1 ? 's' : ''}`,
title: `Compilation successful with ${warningsCount} warning${warningsCount > 1 ? 's' : ''}`,
type: 'warning'
})
} else this.statusChanged({ key: 'succeed', title: 'compilation successful', type: 'success' })
} else this.statusChanged({ key: 'succeed', title: 'Compilation successful', type: 'success' })
} else {
const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + (data.error ? 1 : 0))
this.statusChanged({ key: count, title: `compilation failed with ${count} error${count > 1 ? 's' : ''}`, type: 'error' })
this.statusChanged({ key: count, title: `Compilation failed with ${count} error${count > 1 ? 's' : ''}`, type: 'error' })
}
// Store the contracts and Update contract Selection
if (success) {

@ -855,7 +855,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<FormattedMessage id='solidity.language' />
</label>
<CustomTooltip
placement="right-start"
placement="right"
tooltipId="compilerLabelTooltip"
tooltipClasses="text-nowrap"
tooltipText={<span>{'Language specification available from Compiler >= v0.5.7'}</span>}
@ -970,7 +970,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<button
id="compileAndRunBtn"
data-id="compilerContainerCompileAndRunBtn"
className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-3"
className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-1"
onClick={compileAndRun}
disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton}
>
@ -978,9 +978,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
placement="right"
tooltipId="overlay-tooltip-compile-run"
tooltipText={<div className="text-left">
{!(configFilePath === '' && state.useFileConfiguration) && <div><b>Ctrl+Shift+S</b> for compiling and script execution</div>}
{(configFilePath === '' && state.useFileConfiguration) && <div> No config file selected</div>}
</div>}
{!(configFilePath === '' && state.useFileConfiguration) && <div><b>Ctrl+Shift+S</b> for compiling and script execution</div>}
{(configFilePath === '' && state.useFileConfiguration) && <div> No config file selected</div>}
</div>}
>
<span>
<FormattedMessage id='solidity.compileAndRunScript' />
@ -988,27 +988,28 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</CustomTooltip>
</button>
<CustomTooltip
placement="right"
placement="top"
tooltipId="overlay-tooltip-compile-run-doc"
tooltipText={<div className="text-left p-2">
<div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div>
<pre>
<code>
/**<br />
* @title ContractName<br />
* @dev ContractDescription<br />
* @custom:dev-run-script file_path<br />
*/<br />
contract ContractName {'{}'}<br />
</code>
</pre>
Click the i icon to learn more
</div>}
<div>Choose the script to execute right after compilation
by adding the `dev-run-script` natspec tag, as in:</div>
<pre>
<code>
/**<br />
* @title ContractName<br />
* @dev ContractDescription<br />
* @custom:dev-run-script file_path<br />
*/<br />
contract ContractName {'{}'}<br />
</code>
</pre>
Click the "i" icon to learn more
</div>}
>
<a href="https://remix-ide.readthedocs.io/en/latest/running_js_scripts.html#compile-a-contract-and-run-a-script-on-the-fly" target="_blank" ><i className="pl-2 ml-2 mt-3 mb-1 fas fa-info text-dark"></i></a>
<a href="https://remix-ide.readthedocs.io/en/latest/running_js_scripts.html#compile-a-contract-and-run-a-script-on-the-fly" target="_blank" ><i className="pl-2 ml-2 fas fa-info text-dark"></i></a>
</CustomTooltip>
<CopyToClipboard tip="Click to copy the custom NatSpec tag" getContent={() => '@custom:dev-run-script file_path'} direction='top'>
<button className="btn remixui_copyButton ml-2 mt-3 mb-1 text-dark">
<button className="btn remixui_copyButton ml-2 my-1 text-dark">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
</button>
</CopyToClipboard>

@ -213,7 +213,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
<article className="mt-2 pb-0">
<button id="publishOnIpfs" className="btn btn-secondary btn-block" onClick={() => { handlePublishToStorage('ipfs') }}>
<CustomTooltip
placement="right-start"
placement="right"
tooltipId="publishOnIpfsTooltip"
tooltipClasses="text-nowrap"
tooltipText={`${intl.formatMessage({ id: 'solidity.publishOn' })} Ipfs`}
@ -226,7 +226,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
</button>
<button id="publishOnSwarm" className="btn btn-secondary btn-block" onClick={() => { handlePublishToStorage('swarm') }}>
<CustomTooltip
placement="right-start"
placement="right"
tooltipId="publishOnSwarmTooltip"
tooltipClasses="text-nowrap"
tooltipText={`${intl.formatMessage({ id: 'solidity.publishOn' })} Swarm`}
@ -239,7 +239,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
</button>
<button data-id="compilation-details" className="btn btn-secondary btn-block" onClick={() => { details() }}>
<CustomTooltip
placement="right-start"
placement="right"
tooltipId="CompilationDetailsTooltip"
tooltipClasses="text-nowrap"
tooltipText="Display Contract Details"

@ -1,58 +1,58 @@
.remix-ui-tabs {
display: -webkit-box;
max-height: 2.15rem;
display: -webkit-box;
max-height: 2.15rem;
}
.remix-ui-tabs li {
display: inline-block;
display: inline-block;
}
.title-tabs {
padding: inherit;
align-items: center;
padding-right: 8px;
padding-left: 2px;
cursor: default;
/*to make it unselectable*/
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by any browser but < IE9 */
vertical-align: middle;
padding: inherit;
align-items: center;
padding-right: 8px;
padding-left: 2px;
cursor: default;
/*to make it unselectable*/
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by any browser but < IE9 */
vertical-align: middle;
}
.tab:hover .close-tabs{
visibility: visible
visibility: visible
}
.active .close-tabs {
visibility: visible
visibility: visible
}
.close-tabs {
visibility: hidden;
font-size: medium;
visibility: hidden;
font-size: medium;
}
.iconImage {
width: 1rem;
height: 1rem;
align-self: start;
width: 1rem;
height: 1rem;
align-self: start;
}
.active {
border: 1px solid transparent;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
border: 1px solid transparent;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.tab-scroll {
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
}
/* Hide scrollbar for Chrome, Safari and Opera */
.tab-scroll::-webkit-scrollbar {
display: none;
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
/* Hide scrollbar for IE, Edge and Firefox */
.tab-scroll {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}

@ -7,6 +7,7 @@ import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
import './remix-ui-tabs.css'
const _paq = window._paq = window._paq || []
/* eslint-disable-next-line */
export interface TabsUIProps {
tabs: Array<any>
@ -89,14 +90,25 @@ export const TabsUI = (props: TabsUIProps) => {
const invert = props.themeQuality === 'dark' ? 'invert(1)' : 'invert(0)'
return (
<div ref={el => { tabsRef.current[index] = el }} className={classNameTab} data-id={index === currentIndexRef.current ? 'tab-active' : ''} title={tab.tooltip}>
{tab.icon ? (<img className="my-1 mr-1 iconImage" style={{ filter: invert }} src={tab.icon} />) : (<i className={classNameImg}></i>)}
<span className={`title-tabs ${getFileDecorationClasses(tab)}`}>{tab.title}</span>
{getFileDecorationIcons(tab)}
<span className="close-tabs" onClick={(event) => { props.onClose(index); event.stopPropagation() }}>
<i className="text-dark fas fa-times"></i>
</span>
</div>
<CustomTooltip
tooltipId="tabsActive"
tooltipText={tab.tooltip}
placement="bottom-start"
>
<div
ref={el => { tabsRef.current[index] = el }}
className={classNameTab}
data-id={index === currentIndexRef.current ? 'tab-active' : ''}
data-path={tab.name}
>
{tab.icon ? (<img className="my-1 mr-1 iconImage" style={{ filter: invert }} src={tab.icon} />) : (<i className={classNameImg}></i>)}
<span className={`title-tabs ${getFileDecorationClasses(tab)}`}>{tab.title}</span>
{getFileDecorationIcons(tab)}
<span className="close-tabs" onClick={(event) => { props.onClose(index); event.stopPropagation() }}>
<i className="text-dark fas fa-times"></i>
</span>
</div>
</CustomTooltip>
)
}
@ -143,7 +155,7 @@ export const TabsUI = (props: TabsUIProps) => {
return (
<div className="remix-ui-tabs d-flex justify-content-between border-0 header nav-tabs" data-id="tabs-component">
<div className="d-flex flex-row" style={{ maxWidth: 'fit-content', width: '97%' }}>
<div className="d-flex flex-row" style={{ maxWidth: 'fit-content', width: '99%' }}>
<div className="d-flex flex-row justify-content-center align-items-center m-1 mt-1">
<button
data-id='play-editor'
@ -172,8 +184,20 @@ export const TabsUI = (props: TabsUIProps) => {
<i className="fad fa-play"></i>
</CustomTooltip>
</button>
<span data-id="tabProxyZoomOut" className="btn btn-sm px-2 fas fa-search-minus text-dark" title="Zoom out" onClick={() => props.onZoomOut()}></span>
<span data-id="tabProxyZoomIn" className="btn btn-sm px-2 fas fa-search-plus text-dark" title="Zoom in" onClick={() => props.onZoomIn()}></span>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-zoom-out"
tooltipText="Zoom out"
>
<span data-id="tabProxyZoomOut" className="btn btn-sm px-2 fas fa-search-minus text-dark" onClick={() => props.onZoomOut()}></span>
</CustomTooltip>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-run-zoom-in"
tooltipText="Zoom in"
>
<span data-id="tabProxyZoomIn" className="btn btn-sm px-2 fas fa-search-plus text-dark" onClick={() => props.onZoomIn()}></span>
</CustomTooltip>
</div>
<Tabs
className="tab-scroll"
@ -191,11 +215,11 @@ export const TabsUI = (props: TabsUIProps) => {
>
<TabList className="d-flex flex-row align-items-center">
{props.tabs.map((tab, i) => <Tab className="" key={tab.name}>{renderTab(tab, i)}</Tab>)}
<div style={{minWidth: '4rem', height: "1rem"}} id="dummyElForLastXVisibility"></div>
</TabList>
{props.tabs.map((tab) => <TabPanel key={tab.name} ></TabPanel>)}
</Tabs>
</div>
<i className="mt-2 mr-2 fas fa-arrows-alt-h" title="Scroll to see all tabs"></i>
</div>
)
}

@ -8,6 +8,7 @@ import {allCommands, allPrograms} from './commands' // eslint-disable-line
import TerminalWelcomeMessage from './terminalWelcome' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { CustomTooltip } from '@remix-ui/helper'
import './remix-ui-terminal.css'
import vm from 'vm'
@ -439,8 +440,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setIsOpen(!panels.terminal.minimized)
})
return () => {
props.plugin.off('layout', 'change')
}
@ -452,28 +451,60 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
<div style={{ flexGrow: 1 }} className='remix_ui_terminal_panel' ref={panelRef}>
<div className="remix_ui_terminal_bar d-flex">
<div className="remix_ui_terminal_menu d-flex w-100 align-items-center position-relative border-top border-dark bg-light" ref={terminalMenu} data-id="terminalToggleMenu">
<i className={`mx-2 remix_ui_terminal_toggleTerminal fas ${isOpen ? 'fa-angle-double-down' : 'fa-angle-double-up'}`} data-id="terminalToggleIcon" onClick={handleToggleTerminal}></i>
<CustomTooltip
placement="top"
tooltipId="terminalToggle"
tooltipClasses="text-nowrap"
tooltipText={isOpen ? "Hide Terminal" : "Show Terminal"}
>
<i className={`mx-2 remix_ui_terminal_toggleTerminal fas ${isOpen ? 'fa-angle-double-down' : 'fa-angle-double-up'}`} data-id="terminalToggleIcon" onClick={handleToggleTerminal}></i>
</CustomTooltip>
<div className="mx-2 remix_ui_terminal_console" id="clearConsole" data-id="terminalClearConsole" onClick={handleClearConsole} >
<i className="fas fa-ban" aria-hidden="true" title="Clear console"
></i>
<CustomTooltip
placement="top"
tooltipId="terminalClear"
tooltipClasses="text-nowrap"
tooltipText="Clear console"
>
<i className="fas fa-ban" aria-hidden="true"></i>
</CustomTooltip>
</div>
<div className="mx-2" title='Pending Transactions'>0</div>
<CustomTooltip
placement="top"
tooltipId="terminalClear"
tooltipClasses="text-nowrap"
tooltipText="Pending Transactions"
>
<div className="mx-2">0</div>
</CustomTooltip>
<div className="pt-1 h-80 mx-3 align-items-center remix_ui_terminal_listenOnNetwork custom-control custom-checkbox">
<input
className="custom-control-input"
id="listenNetworkCheck"
onChange={listenOnNetwork}
type="checkbox"
title={intl.formatMessage({ id: 'terminal.listenTitle' })}
/>
<label
className="pt-1 form-check-label custom-control-label text-nowrap"
title={intl.formatMessage({ id: 'terminal.listenTitle' })}
htmlFor="listenNetworkCheck"
data-id="listenNetworkCheckInput"
<CustomTooltip
placement="top"
tooltipId="terminalClear"
tooltipClasses="text-nowrap"
tooltipText={intl.formatMessage({ id: 'terminal.listenTitle' })}
>
<input
className="custom-control-input"
id="listenNetworkCheck"
onChange={listenOnNetwork}
type="checkbox"
/>
</CustomTooltip>
<CustomTooltip
placement="top"
tooltipId="terminalClear"
tooltipClasses="text-nowrap"
tooltipText={intl.formatMessage({ id: 'terminal.listenTitle' })}
>
<FormattedMessage id='terminal.listen' />
</label>
<label
className="pt-1 form-check-label custom-control-label text-nowrap"
htmlFor="listenNetworkCheck"
data-id="listenNetworkCheckInput"
>
<FormattedMessage id='terminal.listen' />
</label>
</CustomTooltip>
</div>
<div className="remix_ui_terminal_search d-flex align-items-center h-100">
<i

@ -4,9 +4,9 @@ import { TooltipPopupProps } from '../types'
import './tooltip-popup.module.css'
const popover = (title?: string, content?: string | React.ReactNode) => (
<Popover id="popover-basic" className='bg-light border-secondary'>
<Popover.Title as="h3" className='bg-dark border-0'>{ title || 'Tooltip' }</Popover.Title>
<Popover.Content>
<Popover id="popover-basic" className='bg-danger border-danger'>
<Popover.Title as="h3" className='bg-warning border-0'>{ title || 'Tooltip' }</Popover.Title>
<Popover.Content className='bg-danger border-info'>
{ content }
</Popover.Content>
</Popover>

@ -14,7 +14,11 @@ export const TreeViewItem = (props: TreeViewItemProps) => {
return (
<li ref={innerRef} key={`treeViewLi${id}`} data-id={`treeViewLi${id}`} className='li_tv' {...otherProps}>
<div key={`treeViewDiv${id}`} data-id={`treeViewDiv${id}`} className={`d-flex flex-row align-items-center ${labelClass}`} onClick={() => !controlBehaviour && setIsExpanded(!isExpanded)}>
{ children && showIcon ? <div className={isExpanded ? `px-1 ${iconY} caret caret_tv` : `px-1 ${iconX} caret caret_tv`} style={{ visibility: children ? 'visible' : 'hidden' }}></div> : icon ? <div className={`pr-3 pl-1 ${icon} caret caret_tv`}></div> : null }
{ children && showIcon ? <div
className={isExpanded ? `px-1 ${iconY} caret caret_tv` : `px-1 ${iconX} caret caret_tv`}
style={{ visibility: children ? 'visible' : 'hidden' }}
></div> : icon ? <div className={`pr-3 pl-1 ${icon} caret caret_tv`}></div> : null
}
<span className='w-100 pl-1'>
{ label }
</span>

@ -1,6 +1,7 @@
import React from 'react'
import { BadgeStatus } from './Icon'
import { CustomTooltip } from '@remix-ui/helper'
import { FormattedMessage } from 'react-intl'
interface BadgeProps {
badgeStatus?: BadgeStatus
}
@ -47,13 +48,20 @@ function Badge ({ badgeStatus }: BadgeProps) {
<>
{
badgeStatus && checkStatusKeyValue(badgeStatus.key, badgeStatus.type) ? (
<i
title={badgeStatus.title}
className={resolveClasses(badgeStatus.key, badgeStatus.type!)}
aria-hidden="true"
<CustomTooltip
placement={'right'}
tooltipClasses="text-nowrap"
tooltipId="recordedTransactionsCounttooltip"
tooltipText={<FormattedMessage id='udapp.transactionsCountTooltip' />}
>
{badgeStatus.text}
</i>
<i
title={badgeStatus.title}
className={resolveClasses(badgeStatus.key, badgeStatus.type!)}
aria-hidden="true"
>
{badgeStatus.text}
</i>
</CustomTooltip>
) : null
}
</>

@ -86,8 +86,7 @@ const Icon = ({
return (
<>
<CustomTooltip
placement={name === 'settings' ? 'right' : name === 'search' ? 'top' :
name === 'udapp' ? 'bottom' : "top"}
placement={name === 'settings' ? 'right' : name === 'search' ? 'top' : name === 'udapp' ? 'bottom' : "top"}
tooltipText={title}
delay={{ show: 1000, hide: 0 }}
>

@ -62,7 +62,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
return (
<>
<CustomTooltip
placement="top-start"
placement="top"
tooltipId="remixuilabelTooltip"
tooltipClasses="text-nowrap"
tooltipText={props.title}
@ -83,7 +83,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
<label
id={action}
data-id={'fileExplorerUploadFile' + action }
className={icon + ' mb-0 remixui_newFile'}
className={icon + ' mb-0 px-1 remixui_newFile'}
key={`index-${action}-${placement}-${icon}`}
>
<input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onChange={(e) => {
@ -108,7 +108,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
<label
id={action}
data-id={'fileExplorerUploadFolder' + action }
className={icon + ' mb-0 remixui_newFile'}
className={icon + ' mb-0 px-1 remixui_newFile'}
key={`index-${action}-${placement}-${icon}`}
>
<input id="folderUpload" data-id="fileExplorerFolderUpload" type="file" onChange={(e) => {
@ -146,7 +146,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
state.actions[action]()
}
}}
className={'newFile ' + icon + ' remixui_newFile'}
className={'newFile ' + icon + ' px-1 remixui_newFile'}
key={`${action}-${title}-${index}`}
>
</span>

@ -454,76 +454,76 @@ export const FileExplorer = (props: FileExplorerProps) => {
return (
<Drag onFileMoved={handleFileMove} onFolderMoved={handleFolderMove}>
<div ref={treeRef} tabIndex={0} style={{ outline: "none" }}>
<TreeView id='treeView'>
<TreeViewItem id="treeViewItem"
controlBehaviour={true}
label={
<div onClick={handleFileExplorerMenuClick}>
<FileExplorerMenu
title={''}
menuItems={props.menuItems}
createNewFile={handleNewFileInput}
createNewFolder={handleNewFolderInput}
publishToGist={publishToGist}
uploadFile={uploadFile}
uploadFolder={uploadFolder}
/>
<div ref={treeRef} tabIndex={0} style={{ outline: "none" }}>
<TreeView id='treeView'>
<TreeViewItem id="treeViewItem"
controlBehaviour={true}
label={
<div onClick={handleFileExplorerMenuClick}>
<FileExplorerMenu
title={''}
menuItems={props.menuItems}
createNewFile={handleNewFileInput}
createNewFolder={handleNewFolderInput}
publishToGist={publishToGist}
uploadFile={uploadFile}
uploadFolder={uploadFolder}
/>
</div>
}
expand={true}>
<div className='pb-4 mb-4'>
<TreeView id='treeViewMenu'>
{
files[ROOT_PATH] && Object.keys(files[ROOT_PATH]).map((key, index) => <FileRender
file={files[ROOT_PATH][key]}
fileDecorations={fileState}
index={index}
focusContext={state.focusContext}
focusEdit={state.focusEdit}
focusElement={props.focusElement}
ctrlKey={state.ctrlKey}
expandPath={props.expandPath}
editModeOff={editModeOff}
handleClickFile={handleClickFile}
handleClickFolder={handleClickFolder}
handleContextMenu={handleContextMenu}
key={index}
showIconsMenu={props.showIconsMenu}
hideIconsMenu={props.hideIconsMenu}
/>)
}
</TreeView>
</div>
}
expand={true}>
<div className='pb-4 mb-4'>
<TreeView id='treeViewMenu'>
{
files[ROOT_PATH] && Object.keys(files[ROOT_PATH]).map((key, index) => <FileRender
file={files[ROOT_PATH][key]}
fileDecorations={fileState}
index={index}
focusContext={state.focusContext}
focusEdit={state.focusEdit}
focusElement={props.focusElement}
ctrlKey={state.ctrlKey}
expandPath={props.expandPath}
editModeOff={editModeOff}
handleClickFile={handleClickFile}
handleClickFolder={handleClickFolder}
handleContextMenu={handleContextMenu}
key={index}
showIconsMenu={props.showIconsMenu}
hideIconsMenu={props.hideIconsMenu}
/>)
}
</TreeView>
</div>
</TreeViewItem>
</TreeView>
{ state.showContextMenu &&
<FileExplorerContextMenu
actions={props.focusElement.length > 1 ? state.actions.filter(item => item.multiselect) : state.actions.filter(item => !item.multiselect)}
hideContextMenu={hideContextMenu}
createNewFile={handleNewFileInput}
createNewFolder={handleNewFolderInput}
deletePath={deletePath}
downloadPath={downloadPath}
renamePath={editModeOn}
runScript={runScript}
copy={handleCopyClick}
paste={handlePasteClick}
copyFileName={handleCopyFileNameClick}
copyPath={handleCopyFilePathClick}
emit={emitContextMenuEvent}
pageX={state.focusContext.x}
pageY={state.focusContext.y}
path={state.focusContext.element}
type={state.focusContext.type}
focus={props.focusElement}
pushChangesToGist={pushChangesToGist}
publishFolderToGist={publishFolderToGist}
publishFileToGist={publishFileToGist}
/>
}
</div>
</TreeViewItem>
</TreeView>
{ state.showContextMenu &&
<FileExplorerContextMenu
actions={props.focusElement.length > 1 ? state.actions.filter(item => item.multiselect) : state.actions.filter(item => !item.multiselect)}
hideContextMenu={hideContextMenu}
createNewFile={handleNewFileInput}
createNewFolder={handleNewFolderInput}
deletePath={deletePath}
downloadPath={downloadPath}
renamePath={editModeOn}
runScript={runScript}
copy={handleCopyClick}
paste={handlePasteClick}
copyFileName={handleCopyFileNameClick}
copyPath={handleCopyFilePathClick}
emit={emitContextMenuEvent}
pageX={state.focusContext.x}
pageY={state.focusContext.y}
path={state.focusContext.element}
type={state.focusContext.type}
focus={props.focusElement}
pushChangesToGist={pushChangesToGist}
publishFolderToGist={publishFolderToGist}
publishFileToGist={publishFileToGist}
/>
}
</div>
</Drag>
)
}

@ -2,7 +2,7 @@
import { fileDecoration } from '@remix-ui/file-decorators'
import React, { useEffect, useRef, useState } from 'react'
import { FileType } from '../types'
import { CustomTooltip } from '@remix-ui/helper'
export interface FileLabelProps {
file: FileType,
focusEdit: {
@ -70,12 +70,19 @@ export const FileLabel = (props: FileLabelProps) => {
onKeyDown={handleEditInput}
onBlur={handleEditBlur}
>
<CustomTooltip
placement={"right"}
tooltipClasses="text-wrap"
tooltipId={`remixFEItemTooltip${file.path}`}
tooltipText={`${file.path}`}
>
<span
className={`text-nowrap remixui_label ${fileStateClasses} ` + (file.isDirectory ? 'folder' : 'remixui_leaf')}
data-path={file.path} title={file.path}
data-path={file.path}
>
{file.name}
</span>
</CustomTooltip>
</div>
)
}

@ -19,7 +19,7 @@ export function HamburgerMenuItem (props: HamburgerMenuItemProps) {
<>
<Dropdown.Item>
<CustomTooltip
placement="right-start"
placement="right"
tooltipId={uid + "Tooltip"}
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id={'filePanel.workspace.' + props.kind} />}
@ -29,7 +29,7 @@ export function HamburgerMenuItem (props: HamburgerMenuItemProps) {
key={uid + '-fe-ws'}
onClick={() => {
props.actionOnClick()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', {uid}])
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', uid])
}}
>
<span

@ -23,9 +23,6 @@ input[type="file"] {
.remixui_file {
padding : 4px;
}
.remixui_newFile {
padding-right : 10px;
}
.remixui_newFile i {
cursor : pointer;
}

@ -71,14 +71,6 @@
padding: .25rem;
}
.remixui_menuicon .bs-popover-auto[x-placement^="bottom"] .popover-header::before, .bs-popover-bottom .popover-header::before {
border-bottom-color: var(--dark) !important
}
.remixui_menuicon .bs-popover-auto[x-placement^="bottom"] > .arrow::after, .bs-popover-bottom > .arrow::after {
border-bottom-color: var(--dark) !important
}
.custom-dropdown-items {
padding: 0.25rem 0.25rem;
border-radius: .25rem;
@ -107,10 +99,6 @@
padding-bottom: 0.1rem;
}
.remixui_menuwidth {
/* width: 8rem; */
}
#workspacesMenuDropdown > div.custom-dropdown-items {
min-width: 8rem;
}

@ -435,7 +435,7 @@ export function Workspace () {
</span>
{currentWorkspace !== LOCALHOST ? (<span className="remixui_menu remixui_topmenu d-flex justify-content-between align-items-end w-75">
<CustomTooltip
placement="top-end"
placement="top"
tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='filePanel.create' />}

Loading…
Cancel
Save