Merge branch 'master' into allow-url-language-selection

pull/1504/head
David Disu 3 years ago committed by GitHub
commit 2df27fe6b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      apps/debugger/src/app/debugger-api.ts
  2. 8
      apps/remix-ide-e2e/src/tests/defaultLayout.test.ts
  3. 2
      apps/remix-ide-e2e/src/tests/generalSettings.test.ts
  4. 12
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  5. 107
      apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
  6. 4
      apps/remix-ide/src/app.js
  7. 3
      apps/remix-ide/src/app/components/panel.ts
  8. 37
      apps/remix-ide/src/app/components/side-panel.tsx
  9. 110
      apps/remix-ide/src/app/components/vertical-icons.js
  10. 116
      apps/remix-ide/src/app/components/vertical-icons.tsx
  11. 2
      apps/remix-ide/src/app/editor/examples.js
  12. 3
      apps/remix-ide/src/app/panels/layout.ts
  13. 6
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  14. 2
      apps/remix-ide/src/app/tabs/runTab/model/recorder.js
  15. 7
      apps/remix-ide/src/app/tabs/theme-module.js
  16. 8543
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  17. 8671
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  18. 7213
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  19. 8645
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  20. 51
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  21. 3
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  22. 51
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  23. 20
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  24. 3
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  25. BIN
      apps/remix-ide/src/assets/img/cairoLogo.webp
  26. 2
      apps/remix-ide/src/blockchain/blockchain.js
  27. 14
      apps/remix-ide/src/remixAppManager.js
  28. 2
      apps/remix-ide/src/walkthroughService.js
  29. 5
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  30. 3
      libs/remix-core-plugin/src/lib/compiler-content-imports.ts
  31. 2
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  32. 43
      libs/remix-tests/src/compiler.ts
  33. 22
      libs/remix-tests/src/runTestSources.ts
  34. 4
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.css
  35. 5
      libs/remix-ui/editor/src/lib/remix-ui-editor.css
  36. 7
      libs/remix-ui/helper/src/lib/helper-components.tsx
  37. 4
      libs/remix-ui/helper/src/lib/remix-ui-helper.ts
  38. 25
      libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx
  39. 48
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css
  40. 71
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  41. 2
      libs/remix-ui/panel/src/lib/dragbar/dragbar.css
  42. 1
      libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx
  43. 12
      libs/remix-ui/panel/src/lib/main/main-panel.tsx
  44. 2
      libs/remix-ui/panel/src/lib/plugins/panel-header.tsx
  45. 2
      libs/remix-ui/panel/src/lib/types/index.ts
  46. 2
      libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx
  47. 46
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  48. 2
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  49. 9
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  50. 6
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  51. 4
      libs/remix-ui/run-tab/src/lib/types/index.ts
  52. 9
      libs/remix-ui/solidity-unit-testing/src/lib/logic/testTabLogic.ts
  53. 52
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  54. 7
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  55. 10
      libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
  56. 8
      libs/remix-ui/terminal/src/lib/components/ChechTxStatus.tsx
  57. 55
      libs/remix-ui/terminal/src/lib/components/Context.tsx
  58. 19
      libs/remix-ui/terminal/src/lib/components/RenderCall.tsx
  59. 17
      libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx
  60. 17
      libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx
  61. 84
      libs/remix-ui/terminal/src/lib/components/Table.tsx
  62. 20
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  63. 581
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
  64. 136
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  65. 6
      libs/remix-ui/terminal/src/lib/terminalWelcome.tsx
  66. 51
      libs/remix-ui/theme-module/src/lib/remix-ui-theme-module.tsx
  67. 20
      libs/remix-ui/vertical-icons-panel/.eslintrc.json
  68. 3
      libs/remix-ui/vertical-icons-panel/src/index.ts
  69. 11
      libs/remix-ui/vertical-icons-panel/src/lib/components/Badge.tsx
  70. 2
      libs/remix-ui/vertical-icons-panel/src/lib/components/BasicLogo.tsx
  71. 13
      libs/remix-ui/vertical-icons-panel/src/lib/components/Chevron.tsx
  72. 41
      libs/remix-ui/vertical-icons-panel/src/lib/components/Debugger.tsx
  73. 59
      libs/remix-ui/vertical-icons-panel/src/lib/components/FilePanel.tsx
  74. 14
      libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx
  75. 66
      libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx
  76. 33
      libs/remix-ui/vertical-icons-panel/src/lib/components/IconList.tsx
  77. 43
      libs/remix-ui/vertical-icons-panel/src/lib/components/OtherIcons.tsx
  78. 36
      libs/remix-ui/vertical-icons-panel/src/lib/components/PluginManager.tsx
  79. 72
      libs/remix-ui/vertical-icons-panel/src/lib/components/RequiredSection.tsx
  80. 60
      libs/remix-ui/vertical-icons-panel/src/lib/components/Settings.tsx
  81. 41
      libs/remix-ui/vertical-icons-panel/src/lib/components/Solidity.tsx
  82. 41
      libs/remix-ui/vertical-icons-panel/src/lib/components/SolidityStaticAnalysis.tsx
  83. 42
      libs/remix-ui/vertical-icons-panel/src/lib/components/Udapp.tsx
  84. 29
      libs/remix-ui/vertical-icons-panel/src/lib/reducers/iconBadgeReducer.ts
  85. 3
      libs/remix-ui/vertical-icons-panel/src/lib/reducers/verticalScrollReducer.ts
  86. 26
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css
  87. 167
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx
  88. 10
      libs/remix-ui/vertical-icons-panel/src/lib/types/index.ts
  89. 46
      libs/remix-ui/vertical-icons-panel/src/lib/vertical-icons-context-menu.tsx
  90. 12
      libs/remix-ui/vertical-icons-panel/tsconfig.json
  91. 3
      libs/remix-ui/vertical-icons-panel/tsconfig.lib.json
  92. 111
      libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel.d.ts
  93. 2
      libs/remix-url-resolver/src/resolve.ts
  94. 2
      libs/remix-url-resolver/tests/test.ts

@ -83,7 +83,7 @@ export const DebuggerApiMixin = (Base) => class extends Base {
const target = (address && remixDebug.traceHelper.isContractCreation(address)) ? receipt.contractAddress : address const target = (address && remixDebug.traceHelper.isContractCreation(address)) ? receipt.contractAddress : address
const targetAddress = target || receipt.contractAddress || receipt.to const targetAddress = target || receipt.contractAddress || receipt.to
const codeAtAddress = await this._web3.eth.getCode(targetAddress) const codeAtAddress = await this._web3.eth.getCode(targetAddress)
const output = await this.call('fetchAndCompile', 'resolve', targetAddress, codeAtAddress, 'browser/.debug') const output = await this.call('fetchAndCompile', 'resolve', targetAddress, codeAtAddress, '.debug')
if (output) { if (output) {
return new CompilerAbstract(output.languageversion, output.data, output.source) return new CompilerAbstract(output.languageversion, output.data, output.source)
} }

@ -50,11 +50,15 @@ module.exports = {
'Toggles Terminal': function (browser: NightwatchBrowser) { 'Toggles Terminal': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="terminalContainer"]') browser.waitForElementVisible('div[data-id="terminalContainer"]')
.assert.elementPresent('div[data-id="terminalContainerDisplay"]') .assert.elementPresent('div[data-id="terminalCli"]')
.assert.elementPresent('div[data-id="terminalContainer"]')
.waitForElementVisible('div[data-id="terminalContainer"]')
.waitForElementVisible('div[data-id="terminalCli"]')
.click('i[data-id="terminalToggleIcon"]') .click('i[data-id="terminalToggleIcon"]')
.checkElementStyle('div[data-id="terminalToggleMenu"]', 'height', '35px') .checkElementStyle('div[data-id="terminalToggleMenu"]', 'height', '35px')
.assert.not.elementPresent('div[data-id="terminalCli"]')
.click('i[data-id="terminalToggleIcon"]') .click('i[data-id="terminalToggleIcon"]')
.assert.elementPresent('div[data-id="terminalContainerDisplay"]') .waitForElementVisible('div[data-id="terminalCli"]')
}, },
'Switch Tabs using tabs icon': function (browser: NightwatchBrowser) { 'Switch Tabs using tabs icon': function (browser: NightwatchBrowser) {

@ -19,7 +19,7 @@ module.exports = {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000) browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.waitForElementVisible('*[data-id="settingsTabGenerateContractMetadataLabel"]', 5000) .waitForElementVisible('*[data-id="settingsTabGenerateContractMetadataLabel"]', 5000)
.verify.elementPresent('[data-id="settingsTabGenerateContractMetadata"]:checked') .verify.elementPresent('[data-id="settingsTabGenerateContractMetadata"]:checked')
.click('*[data-id="verticalIconsFileExplorerIcons"]') .click('*[data-id="verticalIconsKindfilePanel"]')
.click('[data-id="treeViewLitreeViewItemcontracts"]') .click('[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/3_Ballot.sol') .openFile('contracts/3_Ballot.sol')
.click('*[data-id="verticalIconsKindsolidity"]') .click('*[data-id="verticalIconsKindsolidity"]')

@ -149,6 +149,18 @@ module.exports = {
await clickAndCheckLog(browser, 'udapp:getAccounts', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', null, null) await clickAndCheckLog(browser, 'udapp:getAccounts', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', null, null)
}, },
'Should select another provider #group1': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'udapp:setEnvironmentMode', null, null, { context: 'vm', fork: 'berlin' })
await browser
.frameParent()
.useCss()
.clickLaunchIcon('udapp')
.waitForElementContainsText('#selectExEnvOptions option:checked', 'JavaScript VM (Berlin)')
.clickLaunchIcon('localPlugin')
.useXpath()
// @ts-ignore
.frame(0)
},
// context menu item // context menu item
'Should create context menu item #group1': async function (browser: NightwatchBrowser) { 'Should create context menu item #group1': async function (browser: NightwatchBrowser) {

@ -195,6 +195,26 @@ module.exports = {
.journalLastChildIncludes('"documentation": "param2 from library"') .journalLastChildIncludes('"documentation": "param2 from library"')
.journalLastChildIncludes('"documentation": "param3 from library"') .journalLastChildIncludes('"documentation": "param3 from library"')
.journalLastChildIncludes('Debug the transaction to get more information.') .journalLastChildIncludes('Debug the transaction to get more information.')
},
'Should compile and deploy 2 simple contracts, the contract creation component state should be correctly reset for the deployment of the second contract #group4': function (browser: NightwatchBrowser) {
browser
.addFile('Storage.sol', sources[6]['Storage.sol'])
.addFile('Owner.sol', sources[6]['Owner.sol'])
.clickLaunchIcon('udapp')
.createContract('42')
.openFile('Storage.sol')
.clickLaunchIcon('udapp')
.createContract('') // this creation will fail if the component hasn't been properly reset.
.clickInstance(1)
.clickFunction('store - transact (not payable)', { types: 'uint256 num', values: '24' })
.testFunction('last', // we check if the contract is actually reachable.
{
status: 'true Transaction mined and execution succeed',
'decoded input': {
'uint256 num': '24'
}
})
.end() .end()
} }
} }
@ -322,5 +342,92 @@ contract C {
} }
}` }`
} }
},
{
'Owner.sol': {
content: `
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Owner
* @dev Set & change owner
*/
contract Owner {
address private owner;
// event for EVM logging
event OwnerSet(address indexed oldOwner, address indexed newOwner);
// modifier to check if caller is owner
modifier isOwner() {
// If the first argument of 'require' evaluates to 'false', execution terminates and all
// changes to the state and to Ether balances are reverted.
// This used to consume all gas in old EVM versions, but not anymore.
// It is often a good idea to use 'require' to check if functions are called correctly.
// As a second argument, you can also provide an explanation about what went wrong.
require(msg.sender == owner, "Caller is not owner");
_;
}
/**
* @dev Set contract deployer as owner
*/
constructor(uint p) {
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
emit OwnerSet(address(0), owner);
}
/**
* @dev Change owner
* @param newOwner address of new owner
*/
function changeOwner(address newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
/**
* @dev Return owner address
* @return address of owner
*/
function getOwner() external view returns (address) {
return owner;
}
}`
},
'Storage.sol': {
content: `
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}`
}
} }
] ]

@ -225,8 +225,8 @@ class AppComponent {
self.engine.register([appPanel, tabProxy]) self.engine.register([appPanel, tabProxy])
// those views depend on app_manager // those views depend on app_manager
self.menuicons = new VerticalIcons(appManager) self.menuicons = new VerticalIcons()
self.sidePanel = new SidePanel(appManager, self.menuicons) self.sidePanel = new SidePanel()
self.hiddenPanel = new HiddenPanel() self.hiddenPanel = new HiddenPanel()
const pluginManagerComponent = new PluginManagerComponent( const pluginManagerComponent = new PluginManagerComponent(

@ -49,8 +49,7 @@ export class AbstractPanel extends HostPlugin {
* @param {String} name The name of the plugin to display the content * @param {String} name The name of the plugin to display the content
*/ */
showContent (name) { showContent (name) {
if (!this.plugins[name]) throw new Error(`Plugin ${name} is not yet activated`) if (!this.plugins[name]) throw new Error(`Plugin ${name} is not yet activated`)
Object.values(this.plugins).forEach(plugin => { Object.values(this.plugins).forEach(plugin => {
plugin.active = false plugin.active = false
}) })

@ -4,8 +4,6 @@ import ReactDOM from 'react-dom'
import { AbstractPanel } from './panel' import { AbstractPanel } from './panel'
import { RemixPluginPanel } from '@remix-ui/panel' import { RemixPluginPanel } from '@remix-ui/panel'
import packageJson from '../../../../../package.json' import packageJson from '../../../../../package.json'
import { RemixAppManager } from '../../remixAppManager'
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
import RemixUIPanelHeader from 'libs/remix-ui/panel/src/lib/plugins/panel-header' import RemixUIPanelHeader from 'libs/remix-ui/panel/src/lib/plugins/panel-header'
// const csjs = require('csjs-inject') // const csjs = require('csjs-inject')
@ -17,20 +15,18 @@ const sidePanel = {
methods: ['addView', 'removeView'] methods: ['addView', 'removeView']
} }
// TODO merge with vertical-icons.js
export class SidePanel extends AbstractPanel { export class SidePanel extends AbstractPanel {
appManager: RemixAppManager
sideelement: any sideelement: any
verticalIcons: VerticalIcons; constructor() {
constructor (appManager: RemixAppManager, verticalIcons: VerticalIcons) {
super(sidePanel) super(sidePanel)
this.appManager = appManager
this.sideelement = document.createElement('section') this.sideelement = document.createElement('section')
this.sideelement.setAttribute('class', 'panel plugin-manager') this.sideelement.setAttribute('class', 'panel plugin-manager')
this.verticalIcons = verticalIcons }
onActivation() {
this.renderComponent()
// Toggle content // Toggle content
verticalIcons.events.on('toggleContent', (name) => { this.on('menuicons', 'toggleContent', (name) => {
if (!this.plugins[name]) return if (!this.plugins[name]) return
if (this.plugins[name].active) { if (this.plugins[name].active) {
// TODO: Only keep `this.emit` (issue#2210) // TODO: Only keep `this.emit` (issue#2210)
@ -44,7 +40,7 @@ export class SidePanel extends AbstractPanel {
this.events.emit('showing', name) this.events.emit('showing', name)
}) })
// Force opening // Force opening
verticalIcons.events.on('showContent', (name) => { this.on('menuicons', 'showContent', (name) => {
if (!this.plugins[name]) return if (!this.plugins[name]) return
this.showContent(name) this.showContent(name)
// TODO: Only keep `this.emit` (issue#2210) // TODO: Only keep `this.emit` (issue#2210)
@ -53,25 +49,22 @@ export class SidePanel extends AbstractPanel {
}) })
} }
onActivation () { focus(name) {
this.renderComponent()
}
focus (name) {
this.emit('focusChanged', name) this.emit('focusChanged', name)
super.focus(name) super.focus(name)
} }
removeView (profile) { removeView(profile) {
if (this.plugins[profile.name].active) this.call('menuicons', 'select', 'filePanel')
super.removeView(profile) super.removeView(profile)
this.emit('pluginDisabled', profile.name) this.emit('pluginDisabled', profile.name)
this.call('menuicons', 'unlinkContent', profile) this.call('menuicons', 'unlinkContent', profile)
this.renderComponent() this.renderComponent()
} }
addView (profile, view) { addView(profile, view) {
super.addView(profile, view) super.addView(profile, view)
this.verticalIcons.linkContent(profile) this.call('menuicons', 'linkContent', profile)
this.renderComponent() this.renderComponent()
} }
@ -79,17 +72,17 @@ export class SidePanel extends AbstractPanel {
* Display content and update the header * Display content and update the header
* @param {String} name The name of the plugin to display * @param {String} name The name of the plugin to display
*/ */
async showContent (name) { async showContent(name) {
super.showContent(name) super.showContent(name)
this.emit('focusChanged', name) this.emit('focusChanged', name)
this.renderComponent() this.renderComponent()
} }
render () { render() {
return this.sideelement return this.sideelement
} }
renderComponent () { renderComponent() {
ReactDOM.render(<RemixPluginPanel header={<RemixUIPanelHeader plugins={this.plugins}></RemixUIPanelHeader>} plugins={this.plugins}/>, this.sideelement) ReactDOM.render(<RemixPluginPanel header={<RemixUIPanelHeader plugins={this.plugins}></RemixUIPanelHeader>} plugins={this.plugins} />, this.sideelement)
} }
} }

@ -1,110 +0,0 @@
import * as packageJson from '../../../../../package.json'
import ReactDOM from 'react-dom'
import React from 'react' // eslint-disable-line
// eslint-disable-next-line no-unused-vars
import { RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel'
import Registry from '../state/registry'
const { Plugin } = require('@remixproject/engine')
const EventEmitter = require('events')
const profile = {
name: 'menuicons',
displayName: 'Vertical Icons',
description: '',
version: packageJson.version,
methods: ['select', 'unlinkContent']
}
// TODO merge with side-panel.js. VerticalIcons should not be a plugin
export class VerticalIcons extends Plugin {
constructor (appManager) {
super(profile)
this.events = new EventEmitter()
this.appManager = appManager
this.htmlElement = document.createElement('div')
this.htmlElement.setAttribute('id', 'icon-panel')
this.icons = {}
this.iconKind = {}
this.iconStatus = {}
this.defaultProfile = profile
this.targetProfileForChange = {}
this.targetProfileForRemoval = {}
this.registry = Registry.getInstance()
this.keys = ['succeed', 'edited', 'none', 'loading', 'failed']
this.types = ['error', 'warning', 'success', 'info', '']
}
renderComponent () {
ReactDOM.render(
<RemixUiVerticalIconsPanel
verticalIconsPlugin={this}
/>,
this.htmlElement)
}
onActivation () {
this.renderComponent()
}
linkContent (profile) {
if (!profile.icon) return
if (!profile.kind) profile.kind = 'none'
this.targetProfileForChange[profile.name] = profile
this.listenOnStatus(profile)
this.renderComponent()
}
unlinkContent (profile) {
this.targetProfileForRemoval = profile
this.removeIcon(profile)
this.renderComponent()
}
listenOnStatus (profile) {
}
/**
* Remove an icon from the map
* @param {ModuleProfile} profile The profile of the module
*/
removeIcon ({ name }) {
if (this.targetProfileForChange[name]) delete this.targetProfileForChange[name]
setTimeout(() => {
this.renderComponent()
}, 150)
}
/**
* Set an icon as active
* @param {string} name Name of profile of the module to activate
*/
select (name) {
// TODO: Only keep `this.emit` (issue#2210)
this.emit('showContent', name)
this.events.emit('showContent', name)
}
onThemeChanged (themeType) {
const invert = themeType === 'dark' ? 1 : 0
const active = this.view.querySelector('.active')
if (active) {
const image = active.querySelector('.image')
image.style.setProperty('filter', `invert(${invert})`)
}
}
/**
* Toggles the side panel for plugin
* @param {string} name Name of profile of the module to activate
*/
toggle (name) {
// TODO: Only keep `this.emit` (issue#2210)
this.emit('toggleContent', name)
this.events.emit('toggleContent', name)
}
render () {
return this.htmlElement
}
}

@ -0,0 +1,116 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import ReactDOM from 'react-dom'
import Registry from '../state/registry'
import packageJson from '../../../../../package.json'
import { Plugin } from '@remixproject/engine'
import { EventEmitter } from 'events'
import { IconRecord, RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel'
import { Profile } from '@remixproject/plugin-utils'
import { timeStamp } from 'console'
const profile = {
name: 'menuicons',
displayName: 'Vertical Icons',
description: '',
version: packageJson.version,
methods: ['select', 'unlinkContent', 'linkContent'],
events: ['toggleContent', 'showContent']
}
export class VerticalIcons extends Plugin {
events: EventEmitter
htmlElement: HTMLDivElement
icons: Record<string, IconRecord> = {}
constructor () {
super(profile)
this.events = new EventEmitter()
this.htmlElement = document.createElement('div')
this.htmlElement.setAttribute('id', 'icon-panel')
}
renderComponent () {
const fixedOrder = ['filePanel', 'solidity','udapp', 'debugger', 'solidityStaticAnalysis', 'solidityUnitTesting', 'pluginManager']
const divived = Object.values(this.icons).map((value) => { return {
...value,
isRequired: fixedOrder.indexOf(value.profile.name) > -1
}}).sort((a,b) => {
return a.timestamp - b.timestamp
})
const required = divived.filter((value) => value.isRequired).sort((a,b) => {
return fixedOrder.indexOf(a.profile.name) - fixedOrder.indexOf(b.profile.name)
})
const sorted: IconRecord[] = [
...required,
...divived.filter((value) => { return !value.isRequired })
]
ReactDOM.render(
<RemixUiVerticalIconsPanel
verticalIconsPlugin={this}
icons={sorted}
/>,
this.htmlElement)
}
onActivation () {
this.renderComponent()
this.on('sidePanel', 'focusChanged', (name: string) => {
Object.keys(this.icons).map((o) => {
this.icons[o].active = false
})
this.icons[name].active = true
this.renderComponent()
})
}
async linkContent (profile: Profile) {
if (!profile.icon) return
if (!profile.kind) profile.kind = 'none'
this.icons[profile.name] = {
profile: profile,
active: false,
canbeDeactivated: await this.call('manager', 'canDeactivate', this.profile, profile),
timestamp: Date.now()
}
this.renderComponent()
}
unlinkContent (profile: Profile) {
delete this.icons[profile.name]
this.renderComponent()
}
async activateHome() {
await this.call('manager', 'activatePlugin', 'home')
await this.call('tabs', 'focus', 'home')
}
/**
* Set an icon as active
* @param {string} name Name of profile of the module to activate
*/
select (name: string) {
// TODO: Only keep `this.emit` (issue#2210)
console.log(name, this)
this.emit('showContent', name)
this.events.emit('showContent', name)
}
/**
* Toggles the side panel for plugin
* @param {string} name Name of profile of the module to activate
*/
toggle (name: string) {
// TODO: Only keep `this.emit` (issue#2210)
this.emit('toggleContent', name)
this.events.emit('toggleContent', name)
}
render () {
return this.htmlElement
}
}

@ -313,7 +313,7 @@ const deployWithEthers = `// Right click on the script name and hit "Run" to exe
const readme = `REMIX EXAMPLE PROJECT const readme = `REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads very first time or there are no files existing in the File Explorer. Remix example project is present when Remix loads for the very first time or there are no files existing in the File Explorer.
It contains 3 directories: It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name. 1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.

@ -80,7 +80,8 @@ export class Layout extends Plugin {
const params = queryParams.get() const params = queryParams.get()
if (params.minimizeterminal || params.embed) { if (params.minimizeterminal || params.embed) {
this.panels.terminal.minimized = true this.panels.terminal.minimized = true
this.event.emit('change', null) this.event.emit('change', this.panels)
this.emit('change', this.panels)
} }
if (params.minimizesidepanel || params.embed) { if (params.minimizesidepanel || params.embed) {
this.event.emit('minimizesidepanel') this.event.emit('minimizesidepanel')

@ -142,11 +142,11 @@ function remixdDialog () {
</div> </div>
<div className='mb-2 text-break'> <div className='mb-2 text-break'>
If you are just looking for the remixd command, here it is: If you are just looking for the remixd command, here it is:
<br></br><br></br><b>${commandText}</b> <br></br><br></br><b>{commandText}</b>
<CopyToClipboard data-id='remixdCopyCommand' content={commandText}></CopyToClipboard> <CopyToClipboard data-id='remixdCopyCommand' content={commandText}></CopyToClipboard>
</div> </div>
<div className='mb-2 text-break'> <div className='mb-2 text-break'>
When connected, a session will be started between <em>${window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>. When connected, a session will be started between <em>{window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>.
The shared folder will be in the "File Explorers" workspace named "localhost". The shared folder will be in the "File Explorers" workspace named "localhost".
<br/>Read more about other <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a> <br/>Read more about other <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a>
</div> </div>
@ -155,7 +155,7 @@ function remixdDialog () {
</div> </div>
<div className='mb-2 text-break'> <div className='mb-2 text-break'>
<h6 className="text-danger"> <h6 className="text-danger">
Before using, make sure remixd version is latest i.e. <b>${remixdVersion}</b> Before using, make sure remixd version is latest i.e. <b>v{remixdVersion}</b>
<br></br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a> <br></br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a>
</h6> </h6>
</div> </div>

@ -259,7 +259,7 @@ class Recorder {
cb(err) cb(err)
} }
) )
}, () => { self.setListen(true); self.clearAll() }) }, () => { self.setListen(true) })
} }
runScenario (json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) { runScenario (json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) {

@ -35,15 +35,17 @@ export class ThemeModule extends Plugin {
} }
this.themes = themes.reduce((acc, theme) => { this.themes = themes.reduce((acc, theme) => {
theme.url = window.location.origin + window.location.pathname + theme.url theme.url = window.location.origin + window.location.pathname + theme.url
return { ...acc, [theme.name]: theme } return { ...acc, [theme.name.toLocaleLowerCase()]: theme }
}, {}) }, {})
this._paq = _paq this._paq = _paq
let queryTheme = (new QueryParams()).get().theme let queryTheme = (new QueryParams()).get().theme
queryTheme = queryTheme && queryTheme.toLocaleLowerCase()
queryTheme = this.themes[queryTheme] ? queryTheme : null queryTheme = this.themes[queryTheme] ? queryTheme : null
let currentTheme = this._deps.config.get('settings/theme') let currentTheme = this._deps.config.get('settings/theme')
currentTheme = currentTheme && currentTheme.toLocaleLowerCase()
currentTheme = this.themes[currentTheme] ? currentTheme : null currentTheme = this.themes[currentTheme] ? currentTheme : null
this.currentThemeState = { queryTheme, currentTheme } this.currentThemeState = { queryTheme, currentTheme }
this.active = queryTheme || currentTheme || 'Dark' this.active = queryTheme || currentTheme || 'dark'
this.forced = !!queryTheme this.forced = !!queryTheme
} }
@ -82,6 +84,7 @@ export class ThemeModule extends Plugin {
* @param {string} [themeName] - The name of the theme * @param {string} [themeName] - The name of the theme
*/ */
switchTheme (themeName) { switchTheme (themeName) {
themeName = themeName && themeName.toLocaleLowerCase()
if (themeName && !Object.keys(this.themes).includes(themeName)) { if (themeName && !Object.keys(this.themes).includes(themeName)) {
throw new Error(`Theme ${themeName} doesn't exist`) throw new Error(`Theme ${themeName} doesn't exist`)
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -22,6 +22,7 @@
--light: #1f2020; --light: #1f2020;
--dark: #1a1a1a; --dark: #1a1a1a;
--text: #babbcc; --text: #babbcc;
--body-bg: #1a1a1a;
--breakpoint-xs: 0; --breakpoint-xs: 0;
--breakpoint-sm: 576px; --breakpoint-sm: 576px;
--breakpoint-md: 768px; --breakpoint-md: 768px;
@ -66,7 +67,7 @@ body {
line-height: 1.5; line-height: 1.5;
color: #b3b3b3; color: #b3b3b3;
text-align: left; text-align: left;
background-color: #1a1a1a; background-color: var(--body-bg);
} }
*::-webkit-scrollbar { *::-webkit-scrollbar {
width: 8px; width: 8px;
@ -1638,7 +1639,7 @@ pre code {
} }
.form-control:focus { .form-control:focus {
color: #aaaaaa; color: #aaaaaa;
background-color: #1a1a1a; background-color: var(--body-bg);
border-color: #6d7172; border-color: #6d7172;
outline: 0; outline: 0;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
@ -1655,7 +1656,7 @@ pre code {
} }
select.form-control:focus::-ms-value { select.form-control:focus::-ms-value {
color: #aaaaaa; color: #aaaaaa;
background-color: #1a1a1a; background-color: var(--body-bg);
} }
.form-control-file, .form-control-file,
.form-control-range { .form-control-range {
@ -2361,8 +2362,8 @@ fieldset:disabled a.btn {
} }
.btn-dark { .btn-dark {
color: #d5d5d5; color: #d5d5d5;
background-color: #1a1a1a; background-color: var(--body-bg);
border-color: #1a1a1a; border-color: var(--body-bg);
} }
.btn-dark:hover { .btn-dark:hover {
color: #d5d5d5; color: #d5d5d5;
@ -2376,8 +2377,8 @@ fieldset:disabled a.btn {
.btn-dark.disabled, .btn-dark.disabled,
.btn-dark:disabled { .btn-dark:disabled {
color: #d5d5d5; color: #d5d5d5;
background-color: #1a1a1a; background-color: var(--body-bg);
border-color: #1a1a1a; border-color: var(--body-bg);
} }
.btn-dark:not(:disabled):not(.disabled).active, .btn-dark:not(:disabled):not(.disabled).active,
.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled):active,
@ -2602,13 +2603,13 @@ fieldset:disabled a.btn {
box-shadow: 0 0 0 0.2rem rgba(42, 44, 63, 0.5); box-shadow: 0 0 0 0.2rem rgba(42, 44, 63, 0.5);
} }
.btn-outline-dark { .btn-outline-dark {
color: #1a1a1a; color: var(--body-bg);
border-color: #1a1a1a; border-color: var(--body-bg);
} }
.btn-outline-dark:hover { .btn-outline-dark:hover {
color: #d5d5d5; color: #d5d5d5;
background-color: #1a1a1a; background-color: var(--body-bg);
border-color: #1a1a1a; border-color: var(--body-bg);
} }
.btn-outline-dark.focus, .btn-outline-dark.focus,
.btn-outline-dark:focus { .btn-outline-dark:focus {
@ -2616,15 +2617,15 @@ fieldset:disabled a.btn {
} }
.btn-outline-dark.disabled, .btn-outline-dark.disabled,
.btn-outline-dark:disabled { .btn-outline-dark:disabled {
color: #1a1a1a; color: var(--body-bg);
background-color: transparent; background-color: transparent;
} }
.btn-outline-dark:not(:disabled):not(.disabled).active, .btn-outline-dark:not(:disabled):not(.disabled).active,
.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled):active,
.show > .btn-outline-dark.dropdown-toggle { .show > .btn-outline-dark.dropdown-toggle {
color: #d5d5d5; color: #d5d5d5;
background-color: #1a1a1a; background-color: var(--body-bg);
border-color: #1a1a1a; border-color: var(--body-bg);
} }
.btn-outline-dark:not(:disabled):not(.disabled).active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,
.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled):active:focus,
@ -3359,7 +3360,7 @@ input[type="submit"].btn-block {
} }
.custom-select:focus::-ms-value { .custom-select:focus::-ms-value {
color: #aaaaaa; color: #aaaaaa;
background-color: #1a1a1a; background-color: var(--body-bg);
} }
.custom-select[multiple], .custom-select[multiple],
.custom-select[size]:not([size="1"]) { .custom-select[size]:not([size="1"]) {
@ -3427,7 +3428,7 @@ input[type="submit"].btn-block {
font-weight: 400; font-weight: 400;
line-height: 1.25; line-height: 1.25;
color: #aaaaaa; color: #aaaaaa;
background-color: #1a1a1a; background-color: var(--body-bg);
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 2px; border-radius: 2px;
} }
@ -3458,15 +3459,15 @@ input[type="submit"].btn-block {
outline: 0; outline: 0;
} }
.custom-range:focus::-webkit-slider-thumb { .custom-range:focus::-webkit-slider-thumb {
box-shadow: 0 0 0 1px #1a1a1a, inset 0 1px 1px rgba(0, 0, 0, 0.075), box-shadow: 0 0 0 1px var(--body-bg), inset 0 1px 1px rgba(0, 0, 0, 0.075),
0 0 0 3px rgba(0, 122, 166, 0.25); 0 0 0 3px rgba(0, 122, 166, 0.25);
} }
.custom-range:focus::-moz-range-thumb { .custom-range:focus::-moz-range-thumb {
box-shadow: 0 0 0 1px #1a1a1a, inset 0 1px 1px rgba(0, 0, 0, 0.075), box-shadow: 0 0 0 1px var(--body-bg), inset 0 1px 1px rgba(0, 0, 0, 0.075),
0 0 0 3px rgba(0, 122, 166, 0.25); 0 0 0 3px rgba(0, 122, 166, 0.25);
} }
.custom-range:focus::-ms-thumb { .custom-range:focus::-ms-thumb {
box-shadow: 0 0 0 1px #1a1a1a, inset 0 1px 1px rgba(0, 0, 0, 0.075), box-shadow: 0 0 0 1px var(--body-bg), inset 0 1px 1px rgba(0, 0, 0, 0.075),
0 0 0 3px rgba(0, 122, 166, 0.25); 0 0 0 3px rgba(0, 122, 166, 0.25);
} }
.custom-range::-moz-focus-outer { .custom-range::-moz-focus-outer {
@ -4423,7 +4424,7 @@ a.badge-light:focus {
} }
.badge-dark { .badge-dark {
color: #d5d5d5; color: #d5d5d5;
background-color: #1a1a1a; background-color: var(--body-bg);
} }
a.badge-dark:focus, a.badge-dark:focus,
a.badge-dark:hover { a.badge-dark:hover {
@ -5040,7 +5041,7 @@ a.close.disabled {
width: 100%; width: 100%;
color: #d5d5d5; color: #d5d5d5;
pointer-events: auto; pointer-events: auto;
background-color: #1a1a1a; background-color: var(--body-bg);
background-clip: padding-box; background-clip: padding-box;
border: 1px solid #000000; border: 1px solid #000000;
border-radius: 2px; border-radius: 2px;
@ -5691,7 +5692,7 @@ button.bg-light:hover {
background-color: #161720 !important; background-color: #161720 !important;
} }
.bg-dark { .bg-dark {
background-color: #1a1a1a !important; background-color: var(--body-bg) !important;
} }
a.bg-dark:focus, a.bg-dark:focus,
a.bg-dark:hover, a.bg-dark:hover,
@ -8711,16 +8712,16 @@ a.text-dark:hover {
/* Plugins manager */ /* Plugins manager */
.plugin-manager { .plugin-manager {
background: #1a1a1a; background: var(--body-bg);
} }
.plugins-header { .plugins-header {
background-color: #1a1a1a !important; background-color: var(--body-bg) !important;
border-bottom: 1px solid #000000 !important; border-bottom: 1px solid #000000 !important;
} }
.plugins-list-header { .plugins-list-header {
justify-content: flex-start !important; justify-content: flex-start !important;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
background-color: #1a1a1a !important background-color: var(--body-bg) !important
} }
.plugins-list-title { .plugins-list-title {
margin: 0 0.5rem 0 0 !important; margin: 0 0.5rem 0 0 !important;

@ -21,6 +21,7 @@
--light: #fff; --light: #fff;
--dark: #645fb5; --dark: #645fb5;
--text: #11556c; --text: #11556c;
--body-bg: #d5efff;
--breakpoint-xs: 0; --breakpoint-xs: 0;
--breakpoint-sm: 576px; --breakpoint-sm: 576px;
--breakpoint-md: 768px; --breakpoint-md: 768px;
@ -69,7 +70,7 @@ body {
line-height: 1.5; line-height: 1.5;
color: #0f7292; color: #0f7292;
text-align: left; text-align: left;
background-color: #d5efff; background-color: var(--body-bg);
} }
[tabindex="-1"]:focus { [tabindex="-1"]:focus {

@ -23,6 +23,7 @@
--dark: #222336; --dark: #222336;
--text: #babbcc; --text: #babbcc;
--text-background: #222336; --text-background: #222336;
--body-bg: #222336;
--breakpoint-xs: 0; --breakpoint-xs: 0;
--breakpoint-sm: 576px; --breakpoint-sm: 576px;
--breakpoint-md: 768px; --breakpoint-md: 768px;
@ -67,7 +68,7 @@ body {
line-height: 1.5; line-height: 1.5;
color: #A2A3BD; color: #A2A3BD;
text-align: left; text-align: left;
background-color: #222336; background-color: var(--body-bg);
} }
*::-webkit-scrollbar { *::-webkit-scrollbar {
width: 8px; width: 8px;
@ -448,7 +449,7 @@ mark {
} }
.img-thumbnail { .img-thumbnail {
padding: 0.25rem; padding: 0.25rem;
background-color: #222336; background-color: var(--body-bg);
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 2px; border-radius: 2px;
max-width: 100%; max-width: 100%;
@ -1364,7 +1365,7 @@ pre code {
border: 0; border: 0;
} }
.table-striped tbody tr:nth-of-type(odd) { .table-striped tbody tr:nth-of-type(odd) {
background-color: #222336; background-color: var(--body-bg);
} }
.table-hover tbody tr:hover { .table-hover tbody tr:hover {
color: #fff; color: #fff;
@ -1533,7 +1534,7 @@ pre code {
} }
.table .thead-light th { .table .thead-light th {
color: #fff; color: #fff;
background-color: #222336; background-color: var(--body-bg);
border-color: #51536b; border-color: #51536b;
} }
.table-dark { .table-dark {
@ -2353,8 +2354,8 @@ fieldset:disabled a.btn {
} }
.btn-dark { .btn-dark {
color: #fff; color: #fff;
background-color: #222336; background-color: var(--body-bg);
border-color: #222336; border-color: var(--body-bg);
} }
.btn-dark:hover { .btn-dark:hover {
color: #fff; color: #fff;
@ -2368,8 +2369,8 @@ fieldset:disabled a.btn {
.btn-dark.disabled, .btn-dark.disabled,
.btn-dark:disabled { .btn-dark:disabled {
color: #fff; color: #fff;
background-color: #222336; background-color: var(--body-bg);
border-color: #222336; border-color: var(--body-bg);
} }
.btn-dark:not(:disabled):not(.disabled).active, .btn-dark:not(:disabled):not(.disabled).active,
.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled):active,
@ -2594,13 +2595,13 @@ fieldset:disabled a.btn {
box-shadow: 0 0 0 0.2rem rgba(42, 44, 63, 0.5); box-shadow: 0 0 0 0.2rem rgba(42, 44, 63, 0.5);
} }
.btn-outline-dark { .btn-outline-dark {
color: #222336; color: var(--body-bg);
border-color: #222336; border-color: var(--body-bg);
} }
.btn-outline-dark:hover { .btn-outline-dark:hover {
color: #fff; color: #fff;
background-color: #222336; background-color: var(--body-bg);
border-color: #222336; border-color: var(--body-bg);
} }
.btn-outline-dark.focus, .btn-outline-dark.focus,
.btn-outline-dark:focus { .btn-outline-dark:focus {
@ -2608,15 +2609,15 @@ fieldset:disabled a.btn {
} }
.btn-outline-dark.disabled, .btn-outline-dark.disabled,
.btn-outline-dark:disabled { .btn-outline-dark:disabled {
color: #222336; color: var(--body-bg);
background-color: transparent; background-color: transparent;
} }
.btn-outline-dark:not(:disabled):not(.disabled).active, .btn-outline-dark:not(:disabled):not(.disabled).active,
.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled):active,
.show > .btn-outline-dark.dropdown-toggle { .show > .btn-outline-dark.dropdown-toggle {
color: #fff; color: #fff;
background-color: #222336; background-color: var(--body-bg);
border-color: #222336; border-color: var(--body-bg);
} }
.btn-outline-dark:not(:disabled):not(.disabled).active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,
.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled):active:focus,
@ -3454,15 +3455,15 @@ input[type="submit"].btn-block {
outline: 0; outline: 0;
} }
.custom-range:focus::-webkit-slider-thumb { .custom-range:focus::-webkit-slider-thumb {
box-shadow: 0 0 0 1px #222336, inset 0 1px 1px rgba(0, 0, 0, 0.075), box-shadow: 0 0 0 1px var(--body-bg), inset 0 1px 1px rgba(0, 0, 0, 0.075),
0 0 0 3px rgba(0, 122, 166, 0.25); 0 0 0 3px rgba(0, 122, 166, 0.25);
} }
.custom-range:focus::-moz-range-thumb { .custom-range:focus::-moz-range-thumb {
box-shadow: 0 0 0 1px #222336, inset 0 1px 1px rgba(0, 0, 0, 0.075), box-shadow: 0 0 0 1px var(--body-bg), inset 0 1px 1px rgba(0, 0, 0, 0.075),
0 0 0 3px rgba(0, 122, 166, 0.25); 0 0 0 3px rgba(0, 122, 166, 0.25);
} }
.custom-range:focus::-ms-thumb { .custom-range:focus::-ms-thumb {
box-shadow: 0 0 0 1px #222336, inset 0 1px 1px rgba(0, 0, 0, 0.075), box-shadow: 0 0 0 1px var(--body-bg), inset 0 1px 1px rgba(0, 0, 0, 0.075),
0 0 0 3px rgba(0, 122, 166, 0.25); 0 0 0 3px rgba(0, 122, 166, 0.25);
} }
.custom-range::-moz-focus-outer { .custom-range::-moz-focus-outer {
@ -3633,7 +3634,7 @@ input[type="submit"].btn-block {
.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-item.show .nav-link,
.nav-tabs .nav-link.active { .nav-tabs .nav-link.active {
color: #fff; color: #fff;
background-color: #222336; background-color: var(--body-bg);
border-color: #3f4455; border-color: #3f4455;
} }
.nav-tabs .dropdown-menu { .nav-tabs .dropdown-menu {
@ -4419,7 +4420,7 @@ a.badge-light:focus {
} }
.badge-dark { .badge-dark {
color: #fff; color: #fff;
background-color: #222336; background-color: var(--body-bg);
} }
a.badge-dark:focus, a.badge-dark:focus,
a.badge-dark:hover { a.badge-dark:hover {
@ -5036,7 +5037,7 @@ a.close.disabled {
width: 100%; width: 100%;
color: #fff; color: #fff;
pointer-events: auto; pointer-events: auto;
background-color: #222336; background-color: var(--body-bg);
background-clip: padding-box; background-clip: padding-box;
border: 1px solid #2c3244; border: 1px solid #2c3244;
border-radius: 2px; border-radius: 2px;
@ -5687,7 +5688,7 @@ button.bg-light:hover {
background-color: #161720 !important; background-color: #161720 !important;
} }
.bg-dark { .bg-dark {
background-color: #222336 !important; background-color: var(--body-bg) !important;
} }
a.bg-dark:focus, a.bg-dark:focus,
a.bg-dark:hover, a.bg-dark:hover,
@ -8707,16 +8708,16 @@ a.text-dark:hover {
/* Plugins manager */ /* Plugins manager */
.plugin-manager { .plugin-manager {
background: #222336; background: var(--body-bg);
} }
.plugins-header { .plugins-header {
background-color: #222336 !important; background-color: var(--body-bg) !important;
border-bottom: 1px solid #2C3244 !important; border-bottom: 1px solid #2C3244 !important;
} }
.plugins-list-header { .plugins-list-header {
justify-content: flex-start !important; justify-content: flex-start !important;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
background-color: #222336 !important background-color: var(--body-bg) !important
} }
.plugins-list-title { .plugins-list-title {
margin: 0 0.5rem 0 0 !important; margin: 0 0.5rem 0 0 !important;

@ -21,6 +21,7 @@
--light: #fff; --light: #fff;
--dark: #f8fafe; --dark: #f8fafe;
--text: #3b445e; --text: #3b445e;
--body-bg: #eef1f6;
--breakpoint-xs: 0; --breakpoint-xs: 0;
--breakpoint-sm: 576px; --breakpoint-sm: 576px;
--breakpoint-md: 768px; --breakpoint-md: 768px;
@ -69,7 +70,7 @@ body {
line-height: 1.5; line-height: 1.5;
color: #2e3145; color: #2e3145;
text-align: left; text-align: left;
background-color: #eef1f6; background-color: var(--body-bg);
} }
[tabindex="-1"]:focus { [tabindex="-1"]:focus {
@ -517,7 +518,7 @@ mark,
.img-thumbnail { .img-thumbnail {
padding: 0.25rem; padding: 0.25rem;
background-color: #eef1f6; background-color: var(--body-bg);
border: 1px solid #dee2e6; border: 1px solid #dee2e6;
border-radius: 0.25rem; border-radius: 0.25rem;
max-width: 100%; max-width: 100%;
@ -1673,7 +1674,7 @@ pre code {
.table-active, .table-active,
.table-active > th, .table-active > th,
.table-active > td { .table-active > td {
background-color: #eef1f6; background-color: var(--body-bg);
} }
.table-hover .table-active:hover { .table-hover .table-active:hover {
@ -3767,13 +3768,13 @@ input[type="button"].btn-block {
outline: none; outline: none;
} }
.custom-range:focus::-webkit-slider-thumb { .custom-range:focus::-webkit-slider-thumb {
box-shadow: 0 0 0 1px #eef1f6, 0px 0px 5px #00bbff; box-shadow: 0 0 0 1px var(--body-bg), 0px 0px 5px #00bbff;
} }
.custom-range:focus::-moz-range-thumb { .custom-range:focus::-moz-range-thumb {
box-shadow: 0 0 0 1px #eef1f6, 0px 0px 5px #00bbff; box-shadow: 0 0 0 1px var(--body-bg), 0px 0px 5px #00bbff;
} }
.custom-range:focus::-ms-thumb { .custom-range:focus::-ms-thumb {
box-shadow: 0 0 0 1px #eef1f6, 0px 0px 5px #00bbff; box-shadow: 0 0 0 1px var(--body-bg), 0px 0px 5px #00bbff;
} }
.custom-range::-moz-focus-outer { .custom-range::-moz-focus-outer {
border: 0; border: 0;
@ -5482,7 +5483,7 @@ a.close.disabled {
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
pointer-events: auto; pointer-events: auto;
background-color: #eef1f6; background-color: var(--body-bg);
background-clip: padding-box; background-clip: padding-box;
border: 1px solid #dfe1ea; border: 1px solid #dfe1ea;
border-radius: 0.3rem; border-radius: 0.3rem;
@ -5496,7 +5497,7 @@ a.close.disabled {
z-index: 1040; z-index: 1040;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: #eef1f6; background-color: var(--body-bg);
} }
.modal-backdrop.fade { .modal-backdrop.fade {
opacity: 0; opacity: 0;
@ -9494,8 +9495,9 @@ a.text-dark:focus {
border: none !important; border: none !important;
} }
.remix-bg-opacity { .remix-bg-opacity {
background: rgba(248, 250, 254, 0.80) !important; background: rgba(248, 250, 254, 0.80) !important;
} }
.plugins-list-header { .plugins-list-header {
background: transparent !important; background: transparent !important;
} }

@ -21,6 +21,7 @@
--light: #eeede9; --light: #eeede9;
--dark: #01414E; --dark: #01414E;
--text: #11556c; --text: #11556c;
--body-bg: #DBE2E0;
--breakpoint-xs: 0; --breakpoint-xs: 0;
--breakpoint-sm: 576px; --breakpoint-sm: 576px;
--breakpoint-md: 768px; --breakpoint-md: 768px;
@ -69,7 +70,7 @@ body {
line-height: 1.5; line-height: 1.5;
color: #062809; color: #062809;
text-align: left; text-align: left;
background-color: #DBE2E0; background-color: var(--body-bg);
} }
[tabindex="-1"]:focus { [tabindex="-1"]:focus {

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

@ -353,7 +353,7 @@ export class Blockchain extends Plugin {
if (network.name === 'VM') return if (network.name === 'VM') return
this.call('terminal', 'logHtml', this.call('terminal', 'logHtml',
(<a href={etherScanLink(network.name, txhash)} target="_blank"> (<a href={etherScanLink(network.name, txhash)} target="_blank">
open in etherscan view on etherscan
</a>)) </a>))
}) })
}) })

@ -50,6 +50,10 @@ export class RemixAppManager extends PluginManager {
return isNative(from.name) return isNative(from.name)
} }
async canDeactivate(from,to) {
return this.canDeactivatePlugin(from, to)
}
async deactivatePlugin (name) { async deactivatePlugin (name) {
const [to, from] = [ const [to, from] = [
await this.getProfile(name), await this.getProfile(name),
@ -145,16 +149,6 @@ export class RemixAppManager extends PluginManager {
pattern: [], pattern: [],
sticky: true sticky: true
}) })
await this.call('filePanel', 'registerContextMenuItem', {
id: 'optimism-compiler',
name: 'compileCustomAction',
label: 'Compile with Optimism',
type: [],
extension: ['.sol'],
path: [],
pattern: [],
sticky: true
})
} }
} }

@ -31,7 +31,7 @@ export class WalkthroughService extends Plugin {
position: 'right' position: 'right'
}, },
{ {
element: document.querySelector('#compileIcons'), element: document.querySelector('#verticalIconsKindsolidity'),
title: 'Solidity Compiler', title: 'Solidity Compiler',
intro: 'Having selected a .sol file in the File Explorers (the icon above), compile it with the Solidity Compiler.', intro: 'Having selected a .sol file in the File Explorers (the icon above), compile it with the Solidity Compiler.',
tooltipClass: 'bg-light text-dark', tooltipClass: 'bg-light text-dark',

@ -48,11 +48,6 @@ export class CompilerArtefacts extends Plugin {
saveCompilationPerFileResult(file, source, languageVersion, data) saveCompilationPerFileResult(file, source, languageVersion, data)
}) })
this.on('optimism-compiler', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
})
this.on('solidityUnitTesting', 'compilationFinished', (file, source, languageVersion, data) => { this.on('solidityUnitTesting', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source) this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data) saveCompilationPerFileResult(file, source, languageVersion, data)

@ -35,7 +35,8 @@ export class CompilerImports extends Plugin {
isExternalUrl (url) { isExternalUrl (url) {
const handlers = this.urlResolver.getHandlers() const handlers = this.urlResolver.getHandlers()
return handlers.some(handler => handler.match(url)) // we filter out "npm" because this will be recognized as internal url although it's not the case.
return handlers.filter((handler) => handler.type !== 'npm').some(handler => handler.match(url))
} }
/** /**

@ -153,7 +153,7 @@ async function tryTillReceiptAvailable (txhash, web3) {
async function tryTillTxAvailable (txhash, web3) { async function tryTillTxAvailable (txhash, web3) {
try { try {
const tx = await web3.eth.getTransaction(txhash) const tx = await web3.eth.getTransaction(txhash)
if (tx) return tx if (tx && tx.blockHash) return tx
} catch (e) {} } catch (e) {}
return await tryTillTxAvailable(txhash, web3) return await tryTillTxAvailable(txhash, web3)
} }

@ -1,6 +1,7 @@
import fs from './fileSystem' import fs from './fileSystem'
import async from 'async' import async from 'async'
import path from 'path' import path from 'path'
import deepequal from 'deep-equal'
import Log from './logger' import Log from './logger'
import { Compiler as RemixCompiler } from '@remix-project/remix-solidity' import { Compiler as RemixCompiler } from '@remix-project/remix-solidity'
import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types' import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types'
@ -46,7 +47,6 @@ function isRemixTestFile (path: string) {
function processFile (filePath: string, sources: SrcIfc, isRoot = false) { function processFile (filePath: string, sources: SrcIfc, isRoot = false) {
const importRegEx = /import ['"](.+?)['"];/g const importRegEx = /import ['"](.+?)['"];/g
let group: RegExpExecArray| null = null
const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath) const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath)
// Return if file is a remix test file or already processed // Return if file is a remix test file or already processed
@ -61,14 +61,6 @@ function processFile (filePath: string, sources: SrcIfc, isRoot = false) {
content = includeTestLibs.concat(content) content = includeTestLibs.concat(content)
} }
sources[filePath] = { content } sources[filePath] = { content }
importRegEx.exec('') // Resetting state of RegEx
// Process each 'import' in file content
while ((group = importRegEx.exec(content))) {
const importedFile: string = group[1]
const importedFilePath: string = path.join(path.dirname(filePath), importedFile)
processFile(importedFilePath, sources)
}
} }
const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-'
@ -122,7 +114,13 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
} finally { } finally {
async.waterfall([ async.waterfall([
function loadCompiler (next) { function loadCompiler (next) {
compiler = new RemixCompiler() compiler = new RemixCompiler((url, cb) => {
try {
cb(null, fs.readFileSync(url, 'utf-8'))
} catch (e) {
cb(e.message)
}
})
if (compilerConfig) { if (compilerConfig) {
const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig
if (evmVersion) compiler.set('evmVersion', evmVersion) if (evmVersion) compiler.set('evmVersion', evmVersion)
@ -170,7 +168,8 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
* @param opts Options * @param opts Options
* @param cb Callback * @param cb Callback
*/ */
export function compileContractSources (sources: SrcIfc, compiler: any, opts: any, cb): void { export function compileContractSources (sources: SrcIfc, newCompConfig: any, importFileCb, UTRunner, opts: any, cb): void {
let compiler
const filepath = opts.testFilePath || '' const filepath = opts.testFilePath || ''
const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm
@ -183,8 +182,28 @@ export function compileContractSources (sources: SrcIfc, compiler: any, opts: an
} }
async.waterfall([ async.waterfall([
function doCompilation (next) { (next) => {
if (!deepequal(UTRunner.compilerConfig, newCompConfig)) {
UTRunner.compilerConfig = newCompConfig
const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = newCompConfig
compiler = new RemixCompiler(importFileCb)
compiler.set('evmVersion', evmVersion)
compiler.set('optimize', optimize)
compiler.set('runs', runs)
compiler.loadVersion(usingWorker, currentCompilerUrl)
// @ts-ignore
compiler.event.register('compilerLoaded', this, (version) => {
next()
})
} else {
compiler = UTRunner.compiler
next()
}
},
(next) => {
const compilationFinishedCb = (success, data, source) => { const compilationFinishedCb = (success, data, source) => {
// data.error usually exists for exceptions like worker error etc.
if (!data.error) UTRunner.compiler = compiler
if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source) if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source)
next(null, data) next(null, data)
} }

@ -1,6 +1,4 @@
import async, { ErrorCallback } from 'async' import async, { ErrorCallback } from 'async'
import deepequal from 'deep-equal'
import { Compiler as RemixCompiler } from '@remix-project/remix-solidity'
import { compileContractSources, writeTestAccountsContract } from './compiler' import { compileContractSources, writeTestAccountsContract } from './compiler'
import { deployAll } from './deployer' import { deployAll } from './deployer'
import { runTest } from './testRunner' import { runTest } from './testRunner'
@ -50,29 +48,13 @@ export class UnitTestRunner {
* @param importFileCb Import file callback * @param importFileCb Import file callback
* @param opts Options * @param opts Options
*/ */
async runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, deployCb:any, finalCallback: any, importFileCb, opts: Options) { async runTestSources (contractSources: SrcIfc, newCompilerConfig: CompilerConfiguration, testCallback, resultCallback, deployCb:any, finalCallback: any, importFileCb, opts: Options) {
opts = opts || {} opts = opts || {}
const sourceASTs: any = {} const sourceASTs: any = {}
if (opts.web3 || opts.accounts) this.init(opts.web3, opts.accounts) if (opts.web3 || opts.accounts) this.init(opts.web3, opts.accounts)
async.waterfall([ async.waterfall([
(next) => { (next) => {
if (!deepequal(this.compilerConfig, compilerConfig)) { compileContractSources(contractSources, newCompilerConfig, importFileCb, this, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next)
this.compilerConfig = compilerConfig
const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = compilerConfig
this.compiler = new RemixCompiler(importFileCb)
this.compiler.set('evmVersion', evmVersion)
this.compiler.set('optimize', optimize)
this.compiler.set('runs', runs)
this.compiler.loadVersion(usingWorker, currentCompilerUrl)
// @ts-ignore
this.compiler.event.register('compilerLoaded', this, (version) => {
next()
})
} else next()
},
(next) => {
compileContractSources(contractSources, this.compiler, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next)
}, },
(compilationResult: compilationInterface, asts: ASTInterface, next) => { (compilationResult: compilationInterface, asts: ASTInterface, next) => {
for (const filename in asts) { for (const filename in asts) {

@ -7,7 +7,7 @@
left: 0px; left: 0px;
top: 0px; top: 0px;
width: 0.3em; width: 0.3em;
z-index: 9999; z-index: 1000;
} }
.overlay { .overlay {
@ -17,7 +17,7 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
display: block; display: block;
z-index: 9998; z-index: 1000;
} }
.dragbar:hover, .dragbar:hover,

@ -10,6 +10,11 @@
width: auto; width: auto;
} }
.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) {
max-width: none !important;
word-wrap: break-word;
}
.contextview { .contextview {
opacity: 1; opacity: 1;
position: absolute; position: absolute;

@ -3,10 +3,9 @@ import React from 'react'
export const fileChangedToastMsg = (from: string, path: string) => ( export const fileChangedToastMsg = (from: string, path: string) => (
<div><i className="fas fa-exclamation-triangle text-danger mr-1"></i> <div><i className="fas fa-exclamation-triangle text-danger mr-1"></i>
<span> <span>
{from} {from} <span className="font-weight-bold text-warning">
<span className="font-weight-bold text-warning">
is modifying is modifying
</span>{path} </span> {path}
</span> </span>
</div> </div>
) )
@ -52,4 +51,4 @@ export const sourceVerificationNotAvailableToastMsg = () => (
<div> <div>
<b>Source verification plugin not activated or not available.</b> continuing <i>without</i> source code debugging. <b>Source verification plugin not activated or not available.</b> continuing <i>without</i> source code debugging.
</div> </div>
) )

@ -37,9 +37,9 @@ export const createNonClashingNameAsync = async (name: string, fileManager, pref
let exist = true let exist = true
do { do {
const isDuplicate = await fileManager.exists(name + _counter + prefix + '.' + ext) const isDuplicate = await fileManager.exists(name + (_counter || '') + prefix + '.' + ext)
if (isDuplicate) _counter = (_counter | 0) + 1 if (isDuplicate) _counter = (_counter || 0) + 1
else exist = false else exist = false
} while (exist) } while (exist)
const counter = _counter || '' const counter = _counter || ''

@ -6,21 +6,26 @@ interface PluginButtonProps {
imgPath: string, imgPath: string,
envID: string, envID: string,
envText: string, envText: string,
callback: any callback: any,
l2?: boolean
} }
function PluginButton ({ imgPath, envID, envText, callback }: PluginButtonProps) { function PluginButton ({ imgPath, envID, envText, callback, l2 }: PluginButtonProps) {
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
return ( return (
<button <div>
className="btn border-secondary d-flex mr-3 text-nowrap justify-content-center flex-column align-items-center remixui_envButton" <button
data-id={'landingPageStart' + envText} className="btn border-secondary d-flex mr-3 text-nowrap justify-content-center flex-column align-items-center remixui_home_envButton"
onClick={() => callback()} data-id={'landingPageStart' + envText}
> onClick={() => callback()}
<img className="m-2 align-self-center remixui_envLogo" id={envID} src={imgPath} alt="" style={ { filter: themeFilter.filter } } /> >
<label className="text-uppercase text-dark remixui_cursorStyle">{envText}</label> <img className="m-2 align-self-center remixui_home_envLogo" id={envID} src={imgPath} alt="" style={ { filter: themeFilter.filter } } />
</button> <label className="text-uppercase text-dark remixui_home_cursorStyle">{envText}</label>
</button>
{ l2 && <label className="bg-light mx-1 px-1 mb-0 mx-2 position-relative remixui_home_l2Label">L2</label> }
</div>
) )
} }

@ -1,82 +1,86 @@
.remixui_text { .remixui_home_text {
cursor: pointer; cursor: pointer;
font-size: 0.8rem;
font-weight: normal; font-weight: normal;
max-width: 300px; max-width: 300px;
} }
.remixui_text:hover { .remixui_home_text:hover {
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
} }
.remixui_homeContainer { .remixui_home_homeContainer {
overflow-y: hidden; overflow-y: hidden;
overflow-y: auto; overflow-y: auto;
flex-grow: 3; flex-grow: 3;
} }
.remixui_hpLogoContainer { .remixui_home_hpLogoContainer {
margin: 30px; margin: 30px;
padding-right: 90px; padding-right: 90px;
} }
.remixui_mediaBadge { .remixui_home_mediaBadge {
font-size: 2em; font-size: 2em;
height: 2em; height: 2em;
width: 2em; width: 2em;
} }
.remixui_mediaBadge:focus { .remixui_home_mediaBadge:focus {
outline: none; outline: none;
} }
.remixui_image { .remixui_home_image {
height: 1em; height: 1em;
width: 1em; width: 1em;
text-align: center; text-align: center;
} }
.remixui_logoImg { .remixui_home_logoImg {
height: 10em; height: 10em;
} }
.remixui_rightPanel { .remixui_home_rightPanel {
right: 0; right: 0;
position: absolute; position: absolute;
z-index: 3; z-index: 3000;
} }
.remixui_remixHomeMedia { .remixui_home_remixHomeMedia {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
.remixui_panels { .remixui_home_panels {
box-shadow: 0px 0px 13px -7px; box-shadow: 0px 0px 13px -7px;
} }
.remixui_labelIt { .remixui_home_labelIt {
margin-bottom: 0; margin-bottom: 0;
} }
.remixui_bigLabelSize { .remixui_home_bigLabelSize {
font-size: 13px; font-size: 13px;
} }
.remixui_seeAll { .remixui_home_seeAll {
margin-top: 7px; margin-top: 7px;
white-space: nowrap; white-space: nowrap;
} }
.remixui_importFrom p { .remixui_home_importFrom p {
margin-right: 10px; margin-right: 10px;
} }
.remixui_logoContainer img{ .remixui_home_logoContainer img{
height: 150px; height: 150px;
opacity: 0.7; opacity: 0.7;
} }
.remixui_envLogo { .remixui_home_envLogo {
height: 16px; height: 16px;
} }
.remixui_cursorStyle { .remixui_home_cursorStyle {
cursor: pointer; cursor: pointer;
} }
.remixui_envButton { .remixui_home_envButton {
width: 120px; width: 120px;
height: 70px; height: 70px;
} }
.remixui_media { .remixui_home_media {
overflow: hidden; overflow: hidden;
max-width: 400px; max-width: 400px;
transition: .5s ease-out; transition: .5s ease-out;
z-index: 1000; z-index: 1000;
} }
.remixui_migrationBtn { .remixui_home_migrationBtn {
width: 100px; width: 100px;
} }
.remixui_home_l2Label {
bottom: 10px;
}

@ -80,6 +80,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
const remiAudioEl = useRef(null) const remiAudioEl = useRef(null)
const inputValue = useRef(null) const inputValue = useRef(null)
const rightPanel = useRef(null)
useEffect(() => { useEffect(() => {
plugin.call('theme', 'currentTheme').then((theme) => { plugin.call('theme', 'currentTheme').then((theme) => {
@ -97,7 +98,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
window.addEventListener('click', (event) => { window.addEventListener('click', (event) => {
const target = event.target as Element const target = event.target as Element
const id = target.id const id = target.id
if (id !== 'remixIDEHomeTwitterbtn' && id !== 'remixIDEHomeMediumbtn') { if (id !== 'remixIDEHomeTwitterbtn' && id !== 'remixIDEHomeMediumbtn' && !rightPanel.current.contains(event.target)) {
// todo check event.target // todo check event.target
setState(prevState => { return { ...prevState, showMediaPanel: 'none' } }) setState(prevState => { return { ...prevState, showMediaPanel: 'none' } })
} }
@ -125,6 +126,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
} }
const createNewFile = async () => { const createNewFile = async () => {
plugin.verticalIcons.select('filePanel')
await plugin.call('filePanel', 'createNewFile') await plugin.call('filePanel', 'createNewFile')
} }
@ -150,10 +152,10 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
plugin.verticalIcons.select('solidity') plugin.verticalIcons.select('solidity')
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity']) _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity'])
} }
const startOptimism = async () => { const startCairo = async () => {
await plugin.appManager.activatePlugin('optimism-compiler') await plugin.appManager.activatePlugin('cairo_compiler')
plugin.verticalIcons.select('optimism-compiler') plugin.verticalIcons.select('cairo_compiler')
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'optimism-compiler']) _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'cairo_compiler'])
} }
const startSolhint = async () => { const startSolhint = async () => {
await plugin.appManager.activatePlugin(['solidity', 'solhint']) await plugin.appManager.activatePlugin(['solidity', 'solhint'])
@ -171,7 +173,6 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'sourcify']) _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'sourcify'])
} }
const startPluginManager = async () => { const startPluginManager = async () => {
await plugin.appManager.activatePlugin('pluginManager')
plugin.verticalIcons.select('pluginManager') plugin.verticalIcons.select('pluginManager')
} }
@ -232,7 +233,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<label style={ { fontSize: 'xxx-large', height: 'auto', alignSelf: 'flex-end' } }>Remix IDE</label> <label style={ { fontSize: 'xxx-large', height: 'auto', alignSelf: 'flex-end' } }>Remix IDE</label>
</div> </div>
<div className="mr-4 d-flex"> <div className="mr-4 d-flex">
<img className="mt-4 mb-2 remixui_logoImg" src="assets/img/guitarRemiCroped.webp" onClick={ () => playRemi() } alt=""></img> <img className="mt-4 mb-2 remixui_home_logoImg" src="assets/img/guitarRemiCroped.webp" onClick={ () => playRemi() } alt=""></img>
<audio <audio
id="remiAudio" id="remiAudio"
muted={false} muted={false}
@ -241,14 +242,14 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
></audio> ></audio>
</div> </div>
</div> </div>
<div className="row remixui_hpSections mx-2 mr-4" data-id="landingPageHpSections"> <div className="row mx-2 mr-4" data-id="landingPageHpSections">
<div className="ml-3"> <div className="ml-3">
<div className="mb-5"> <div className="mb-5">
<h4>Featured Plugins</h4> <h4>Featured Plugins</h4>
<div className="d-flex flex-row pt-2"> <div className="d-flex flex-row pt-2">
<ThemeContext.Provider value={ state.themeQuality }> <ThemeContext.Provider value={ state.themeQuality }>
<PluginButton imgPath="assets/img/solidityLogo.webp" envID="solidityLogo" envText="Solidity" callback={() => startSolidity()} /> <PluginButton imgPath="assets/img/solidityLogo.webp" envID="solidityLogo" envText="Solidity" callback={() => startSolidity()} />
<PluginButton imgPath="assets/img/optimismLogo.webp" envID="optimismLogo" envText="Optimism" callback={() => startOptimism()} /> <PluginButton imgPath="assets/img/cairoLogo.webp" envID="CairoLogo" envText="Cairo compiler" l2={true} callback={() => startCairo()} />
<PluginButton imgPath="assets/img/solhintLogo.webp" envID="solhintLogo" envText="Solhint linter" callback={() => startSolhint()} /> <PluginButton imgPath="assets/img/solhintLogo.webp" envID="solhintLogo" envText="Solhint linter" callback={() => startSolhint()} />
<PluginButton imgPath="assets/img/learnEthLogo.webp" envID="learnEthLogo" envText="LearnEth" callback={() => startLearnEth()} /> <PluginButton imgPath="assets/img/learnEthLogo.webp" envID="learnEthLogo" envText="LearnEth" callback={() => startLearnEth()} />
<PluginButton imgPath="assets/img/sourcifyLogo.webp" envID="sourcifyLogo" envText="Sourcify" callback={() => startSourceVerify()} /> <PluginButton imgPath="assets/img/sourcifyLogo.webp" envID="sourcifyLogo" envText="Sourcify" callback={() => startSourceVerify()} />
@ -261,21 +262,22 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<h4>File</h4> <h4>File</h4>
<p className="mb-1"> <p className="mb-1">
<i className="mr-2 far fa-file"></i> <i className="mr-2 far fa-file"></i>
<span className="ml-1 mb-1 remixui_text" onClick={() => createNewFile()}>New File</span> <label className="ml-1 mb-1 remixui_home_text" onClick={() => createNewFile()}>New File</label>
</p> </p>
<p className="mb-1"> <p className="mb-1">
<i className="mr-2 far fa-file-alt"></i> <i className="mr-2 far fa-file-alt"></i>
<span className="ml-1 remixui_labelIt remixui_bigLabelSize} remixui_text"> <label className="ml-1 remixui_home_labelIt remixui_home_bigLabelSize remixui_home_text" htmlFor="openFileInput">
Open Files Open Files
<input title="open file" type="file" onChange={(event) => { </label>
event.stopPropagation() <input title="open file" type="file" id="openFileInput" onChange={(event) => {
uploadFile(event.target) event.stopPropagation()
}} multiple /> plugin.verticalIcons.select('filePanel')
</span> uploadFile(event.target)
}} multiple />
</p> </p>
<p className="mb-1"> <p className="mb-1">
<i className="mr-1 far fa-hdd"></i> <i className="mr-1 far fa-hdd"></i>
<span className="ml-1 remixui_text" onClick={() => connectToLocalhost()}>Connect to Localhost</span> <label className="ml-1 remixui_home_text" onClick={() => connectToLocalhost()}>Connect to Localhost</label>
</p> </p>
<p className="mt-3 mb-0"><label>LOAD FROM:</label></p> <p className="mt-3 mb-0"><label>LOAD FROM:</label></p>
<div className="btn-group"> <div className="btn-group">
@ -289,28 +291,28 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<h4>Resources</h4> <h4>Resources</h4>
<p className="mb-1"> <p className="mb-1">
<i className="mr-2 fas fa-book"></i> <i className="mr-2 fas fa-book"></i>
<a className="remixui_text" target="__blank" href="https://remix-ide.readthedocs.io/en/latest/#">Documentation</a> <a className="remixui_home_text" target="__blank" href="https://remix-ide.readthedocs.io/en/latest/#">Documentation</a>
</p> </p>
<p className="mb-1"> <p className="mb-1">
<i className="mr-2 fab fa-gitter"></i> <i className="mr-2 fab fa-gitter"></i>
<a className="remixui_text" target="__blank" href="https://gitter.im/ethereum/remix">Gitter channel</a> <a className="remixui_home_text" target="__blank" href="https://gitter.im/ethereum/remix">Gitter channel</a>
</p> </p>
<p className="mb-1"> <p className="mb-1">
<img id='remixHhomeWebsite' className="mr-2 remixui_image" src={ plugin.profile.icon } style={ { filter: state.themeQuality.filter } } alt=''></img> <img id='remixHhomeWebsite' className="mr-2 remixui_home_image" src={ plugin.profile.icon } style={ { filter: state.themeQuality.filter } } alt=''></img>
<a className="remixui_text" target="__blank" href="https://remix-project.org">Featuring website</a> <a className="remixui_home_text" target="__blank" href="https://remix-project.org">Featuring website</a>
</p> </p>
<p className="mb-1"> <p className="mb-1">
<i className="mr-2 fab fa-ethereum remixui_image"></i> <i className="mr-2 fab fa-ethereum remixui_home_image"></i>
<span className="remixui_text" onClick={() => switchToPreviousVersion()}>Old experience</span> <label className="remixui_home_text" onClick={() => switchToPreviousVersion()}>Old experience</label>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="d-flex flex-column remixui_rightPanel"> <div className="d-flex flex-column remixui_home_rightPanel">
<div className="d-flex pr-3 py-2 align-self-end" id="remixIDEMediaPanelsTitle"> <div className="d-flex pr-3 py-2 align-self-end" id="remixIDEMediaPanelsTitle">
<button <button
className="btn-info p-2 m-1 border rounded-circle remixui_mediaBadge fab fa-twitter" className="btn-info p-2 m-1 border rounded-circle remixui_home_mediaBadge fab fa-twitter"
id="remixIDEHomeTwitterbtn" id="remixIDEHomeTwitterbtn"
title="Twitter" title="Twitter"
onClick={(e) => { onClick={(e) => {
@ -321,7 +323,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}} }}
></button> ></button>
<button <button
className="btn-danger p-2 m-1 border rounded-circle remixui_mediaBadge fab fa-medium" className="btn-danger p-2 m-1 border rounded-circle remixui_home_mediaBadge fab fa-medium"
id="remixIDEHomeMediumbtn" id="remixIDEHomeMediumbtn"
title="Medium blogs" title="Medium blogs"
onClick={(e) => { onClick={(e) => {
@ -332,9 +334,14 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}} }}
></button> ></button>
</div> </div>
<div className="mr-3 d-flex bg-light remixui_panels" style={ { visibility: state.showMediaPanel === 'none' ? 'hidden' : 'visible' } } id="remixIDEMediaPanels"> <div
<div id="remixIDE_MediumBlock" className="p-2 mx-1 mt-3 mb-0 remixui_remixHomeMedia" style={ { maxHeight: maxHeight } }> className="mr-3 d-flex bg-light remixui_home_panels"
<div id="medium-widget" className="px-3 remixui_media" hidden={state.showMediaPanel !== 'medium'} style={ { maxHeight: elHeight } }> style={ { visibility: state.showMediaPanel === 'none' ? 'hidden' : 'visible' } }
id="remixIDEMediaPanels"
ref={rightPanel}
>
<div id="remixIDE_MediumBlock" className="p-2 mx-1 mt-3 mb-0 remixui_home_remixHomeMedia" style={ { maxHeight: maxHeight } }>
<div id="medium-widget" className="px-3 remixui_home_media" hidden={state.showMediaPanel !== 'medium'} style={ { maxHeight: '10000px' } }>
<div <div
id="retainable-rss-embed" id="retainable-rss-embed"
data-rss="https://medium.com/feed/remix-ide" data-rss="https://medium.com/feed/remix-ide"
@ -348,10 +355,10 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
</div> </div>
</div> </div>
</div> </div>
<div id="remixIDE_TwitterBlock" className="p-2 mx-1 mt-3 mb-0 remixui_remixHomeMedia" hidden={state.showMediaPanel !== 'twitter'} style={ { maxHeight: maxHeight, marginRight: '28px' } } > <div id="remixIDE_TwitterBlock" className="p-2 mx-1 mt-3 mb-0 remixui_home_remixHomeMedia" hidden={state.showMediaPanel !== 'twitter'} style={ { maxHeight: maxHeight, marginRight: '28px' } } >
<div className="remixui_media" style={ { minHeight: elHeight } } > <div className="remixui_home_media" style={ { minHeight: elHeight } } >
<a className="twitter-timeline" <a className="twitter-timeline"
data-width="330" data-width="375"
data-theme={ state.themeQuality.name } data-theme={ state.themeQuality.name }
data-chrome="nofooter noheader transparent" data-chrome="nofooter noheader transparent"
data-tweet-limit="18" data-tweet-limit="18"

@ -7,7 +7,7 @@
left: 0px; left: 0px;
top: 0px; top: 0px;
height: 0.3em; height: 0.3em;
z-index: 9999; z-index: 1000;
} }
.overlay { .overlay {

@ -20,6 +20,7 @@ const DragBar = (props: IRemixDragBarUi) => {
props.refObject.current.setAttribute('style', `height: ${h}px;`) props.refObject.current.setAttribute('style', `height: ${h}px;`)
setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight)
setDragState(false) setDragState(false)
props.setHideStatus(false)
} }
const handleResize = () => { const handleResize = () => {
setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight)

@ -37,14 +37,24 @@ const RemixUIMainPanel = () => {
appContext.layout.event.on('change', () => { appContext.layout.event.on('change', () => {
renderPanels() renderPanels()
}) })
return () => {
appContext.layout.event.off('change')
}
}, []) }, [])
const showTerminal = (hide: boolean) => {
appContext.layout.panels.terminal.minimized = hide
appContext.layout.event.emit('change', appContext.layout.panels)
appContext.layout.emit('change', appContext.layout.panels)
}
return ( return (
<div className="mainview"> <div className="mainview">
{Object.values(plugins).map((pluginRecord, i) => { {Object.values(plugins).map((pluginRecord, i) => {
return ( return (
<React.Fragment key={`mainView${i}`}> <React.Fragment key={`mainView${i}`}>
{(pluginRecord.profile.name === 'terminal') ? <DragBar key='dragbar-terminal' hidden={pluginRecord.minimized || false} setHideStatus={() => {}} refObject={terminalRef}></DragBar> : null} {(pluginRecord.profile.name === 'terminal') ? <DragBar key='dragbar-terminal' hidden={pluginRecord.minimized || false} setHideStatus={showTerminal} refObject={terminalRef}></DragBar> : null}
<RemixUIPanelPlugin <RemixUIPanelPlugin
ref={refs[i]} ref={refs[i]}
key={pluginRecord.profile.name} key={pluginRecord.profile.name}

@ -20,7 +20,7 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
return ( return (
<header className='swapitHeader'><h6 data-id='sidePanelSwapitTitle'>{plugin?.profile.displayName || plugin?.profile.name}</h6> <header className='swapitHeader'><h6 data-id='sidePanelSwapitTitle'>{plugin?.profile.displayName || plugin?.profile.name}</h6>
{plugin?.profile.documentation ? (<a href={plugin.profile.documentation} className="titleInfo" title="link to documentation" target="_blank" rel="noreferrer"><i aria-hidden="true" className="fas fa-book"></i></a>) : ''} {plugin?.profile.documentation ? (<a href={plugin.profile.documentation} className="titleInfo mb-2" title="link to documentation" target="_blank" rel="noreferrer"><i aria-hidden="true" className="fas fa-book"></i></a>) : ''}
</header>) </header>)
} }

@ -6,4 +6,4 @@ export type PluginRecord = {
active: boolean active: boolean
class?: string class?: string
minimized?: boolean minimized?: boolean
} }

@ -56,7 +56,7 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
<article className='remember'> <article className='remember'>
<div className='form-check'> <div className='form-check'>
{rememberSwitch()} {rememberSwitch()}
<label className="form-check-label" data-id="permissionHandlerRememberChoice">Remember this choice</label> <label htmlFor='remember' className="form-check-label" data-id="permissionHandlerRememberChoice">Remember this choice</label>
</div> </div>
<button className="btn btn-sm" onClick={reset}>Reset all Permissions</button> <button className="btn btn-sm" onClick={reset}>Reset all Permissions</button>
</article> </article>

@ -2,7 +2,7 @@
import React from 'react' import React from 'react'
import * as ethJSUtil from 'ethereumjs-util' import * as ethJSUtil from 'ethereumjs-util'
import Web3 from 'web3' import Web3 from 'web3'
import { addressToString, createNonClashingNameAsync, shortenAddress } from '@remix-ui/helper' import { addressToString, createNonClashingNameAsync, extractNameFromKey, shortenAddress } from '@remix-ui/helper'
import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeExistingInstance, removeProvider, resetUdapp, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setDecodedResponse, setEnvToasterContent, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setRecorderCount, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent, setWeb3Dialog } from './payload' import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeExistingInstance, removeProvider, resetUdapp, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setDecodedResponse, setEnvToasterContent, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setRecorderCount, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent, setWeb3Dialog } from './payload'
import { RunTab } from '../types/run-tab' import { RunTab } from '../types/run-tab'
import { CompilerAbstract } from '@remix-project/remix-solidity' import { CompilerAbstract } from '@remix-project/remix-solidity'
@ -74,8 +74,6 @@ const setupEvents = () => {
plugin.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data)) plugin.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
plugin.on('optimism-compiler', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
plugin.on('udapp', 'setEnvironmentModeReducer', (env: { context: string, fork: string }, from: string) => { plugin.on('udapp', 'setEnvironmentModeReducer', (env: { context: string, fork: string }, from: string) => {
dispatch(displayPopUp(plugin.REACT_API.envToasterContent(env, from))) dispatch(displayPopUp(plugin.REACT_API.envToasterContent(env, from)))
setExecutionContext(env, plugin.REACT_API.web3Dialog()) setExecutionContext(env, plugin.REACT_API.web3Dialog())
@ -548,7 +546,6 @@ export const clearInstances = () => {
export const loadAddress = (contract: ContractData, address: string) => { export const loadAddress = (contract: ContractData, address: string) => {
if (!contract) return dispatch(displayPopUp('No compiled contracts found.')) if (!contract) return dispatch(displayPopUp('No compiled contracts found.'))
clearInstances()
loadContractFromAddress(address, loadContractFromAddress(address,
(cb) => { (cb) => {
dispatch(displayNotification('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, 'OK', 'Cancel', cb, null)) dispatch(displayNotification('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, 'OK', 'Cancel', cb, null))
@ -625,38 +622,39 @@ export const runTransactions = (
) )
} }
const saveScenario = (promptCb, cb) => { const saveScenario = (newPath: string, provider, promptCb, cb) => {
const txJSON = JSON.stringify(plugin.recorder.getAll(), null, 2) const txJSON = JSON.stringify(plugin.recorder.getAll(), null, 2)
const path = plugin.fileManager.currentPath()
promptCb(path, async () => {
const fileProvider = plugin.fileManager.fileProviderOf(path)
if (!fileProvider) return promptCb(() => {
const newFile = path + '/' + plugin.REACT_API.recorder.pathToScenario
try { try {
const newPath = await createNonClashingNameAsync(newFile, plugin.fileManager) if (!provider.set(newPath, txJSON)) return cb('Failed to create file ' + newPath)
if (!fileProvider.set(newPath, txJSON)) return cb('Failed to create file ' + newFile) plugin.fileManager.open(newPath)
plugin.fileManager.open(newFile)
} catch (error) { } catch (error) {
if (error) return cb('Failed to create file. ' + newFile + ' ' + error) if (error) return cb('Failed to create file. ' + newPath + ' ' + error)
} }
}) })
} }
export const storeScenario = (prompt: (msg: string) => JSX.Element) => { export const storeScenario = async (prompt: (msg: string, defaultValue: string) => JSX.Element) => {
saveScenario( const path = plugin.fileManager.currentPath()
(path, cb) => { const fileProvider = plugin.fileManager.fileProviderOf(path)
dispatch(displayNotification('Save transactions as scenario', prompt('Transactions will be saved in a file under ' + path), 'Ok', 'Cancel', cb, null))
if (!fileProvider) return displayNotification('Alert', 'Invalid File Provider', 'OK', null)
const newPath = await createNonClashingNameAsync(path + '/' + plugin.REACT_API.recorder.pathToScenario, plugin.fileManager)
const newName = extractNameFromKey(newPath)
saveScenario(newPath, fileProvider,
(cb) => {
dispatch(displayNotification('Save transactions as scenario', prompt('Transactions will be saved in a file under ' + path, newName), 'OK', 'Cancel', cb, null))
}, },
(error) => { (error) => {
if (error) return dispatch(displayNotification('Alert', error, 'Ok', null)) if (error) return dispatch(displayNotification('Alert', error, 'OK', null))
} }
) )
} }
const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => { const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {
if (!file) return dispatch(displayNotification('Alert', 'Unable to run scenerio, no specified scenario file', 'Ok', null)) if (!file) return dispatch(displayNotification('Alert', 'Unable to run scenerio, no specified scenario file', 'OK', null))
plugin.fileManager.readFile(file).then((json) => { plugin.fileManager.readFile(file).then((json) => {
// TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily // TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily
@ -667,7 +665,7 @@ const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Ele
}, (okCb, cancelCb) => { }, (okCb, cancelCb) => {
promptHandler(passphrasePrompt, okCb, cancelCb) promptHandler(passphrasePrompt, okCb, cancelCb)
}, (msg) => { }, (msg) => {
dispatch(displayNotification('Alert', msg, 'Ok', null)) dispatch(displayNotification('Alert', msg, 'OK', null))
}, (network, tx, gasEstimation, continueTxExecution, cancelCb) => { }, (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(confirmDialogContent, network, tx, gasEstimation, continueTxExecution, cancelCb) confirmationHandler(confirmDialogContent, network, tx, gasEstimation, continueTxExecution, cancelCb)
}, (msg: string) => { }, (msg: string) => {
@ -676,11 +674,11 @@ const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Ele
return terminalLogger(log) return terminalLogger(log)
}, (error, abi, address, contractName) => { }, (error, abi, address, contractName) => {
if (error) { if (error) {
return dispatch(displayNotification('Alert', error, 'Ok', null)) return dispatch(displayNotification('Alert', error, 'OK', null))
} }
addInstance({ name: contractName, address, abi }) addInstance({ name: contractName, address, abi })
}) })
}).catch((error) => dispatch(displayNotification('Alert', error, 'Ok', null))) }).catch((error) => dispatch(displayNotification('Alert', error, 'OK', null)))
} }
export const runCurrentScenario = (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => { export const runCurrentScenario = (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {

@ -237,7 +237,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
</div> </div>
<div className="udapp_orLabel mt-2" style={{ display: loadType === 'abi' ? 'none' : 'block' }}>or</div> <div className="udapp_orLabel mt-2" style={{ display: loadType === 'abi' ? 'none' : 'block' }}>or</div>
<div className="udapp_button udapp_atAddressSect"> <div className="udapp_button udapp_atAddressSect">
<button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} onClick={loadFromAddress}>At Address</button> <button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} title={atAddressOptions.title} onClick={loadFromAddress}>At Address</button>
<input <input
className="udapp_input udapp_ataddressinput ataddressinput form-control" className="udapp_input udapp_ataddressinput ataddressinput form-control"
placeholder="Load contract from Address" placeholder="Load contract from Address"

@ -25,6 +25,7 @@ export function ContractGUI (props: ContractGUIProps) {
} else { } else {
setTitle(props.funcABI.type === 'receive' ? '(receive)' : '(fallback)') setTitle(props.funcABI.type === 'receive' ? '(receive)' : '(fallback)')
} }
setBasicInput('')
}, [props.title, props.funcABI]) }, [props.title, props.funcABI])
useEffect(() => { useEffect(() => {
@ -62,13 +63,7 @@ export function ContractGUI (props: ContractGUIProps) {
return 'cannot encode empty arguments' return 'cannot encode empty arguments'
} }
const multiJSON = JSON.parse('[' + multiString + ']') const multiJSON = JSON.parse('[' + multiString + ']')
let encodeObj const encodeObj = txFormat.encodeData(props.funcABI, multiJSON, null)
if (props.evmBC) {
encodeObj = txFormat.encodeData(props.funcABI, multiJSON, props.evmBC)
} else {
encodeObj = txFormat.encodeData(props.funcABI, multiJSON, null)
}
if (encodeObj.error) { if (encodeObj.error) {
console.error(encodeObj.error) console.error(encodeObj.error)
return encodeObj.error return encodeObj.error

@ -172,8 +172,8 @@ export function RunTabUI (props: RunTabProps) {
return <PassphrasePrompt message={message} setPassphrase={setPassphrasePrompt} defaultValue={runTab.passphrase} /> return <PassphrasePrompt message={message} setPassphrase={setPassphrasePrompt} defaultValue={runTab.passphrase} />
} }
const scenarioPrompt = (message: string) => { const scenarioPrompt = (message: string, defaultValue) => {
return <ScenarioPrompt message={message} setScenarioPath={updateScenarioPath} defaultValue={runTab.recorder.pathToScenario} /> return <ScenarioPrompt message={message} setScenarioPath={updateScenarioPath} defaultValue={defaultValue} />
} }
const mainnetPrompt = (tx: Tx, network: Network, amount: string, gasEstimation: string, gasFees: (maxFee: string, cb: (txFeeText: string, priceStatus: boolean) => void) => void, determineGasPrice: (cb: (txFeeText: string, gasPriceValue: string, gasPriceStatus: boolean) => void) => void) => { const mainnetPrompt = (tx: Tx, network: Network, amount: string, gasEstimation: string, gasFees: (maxFee: string, cb: (txFeeText: string, priceStatus: boolean) => void) => void, determineGasPrice: (cb: (txFeeText: string, gasPriceValue: string, gasPriceStatus: boolean) => void) => void) => {
@ -205,7 +205,7 @@ export function RunTabUI (props: RunTabProps) {
{from} {from}
<span className="font-weight-bold text-warning"> <span className="font-weight-bold text-warning">
is changing your environment to is changing your environment to
</span> {env} </span> {env && env.context}
</span> </span>
</div> </div>
) )

@ -189,13 +189,13 @@ export interface ContractDropdownProps {
} }
export interface RecorderProps { export interface RecorderProps {
storeScenario: (prompt: (msg: string) => JSX.Element) => void, storeScenario: (prompt: (msg: string, defaultValue: string) => JSX.Element) => void,
runCurrentScenario: (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => void, runCurrentScenario: (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => void,
logBuilder: (msg: string) => JSX.Element, logBuilder: (msg: string) => JSX.Element,
mainnetPrompt: MainnetPrompt, mainnetPrompt: MainnetPrompt,
gasEstimationPrompt: (msg: string) => JSX.Element, gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element,
scenarioPrompt: (msg: string) => JSX.Element, scenarioPrompt: (msg: string, defaultValue: string) => JSX.Element,
count: number count: number
} }

@ -21,23 +21,20 @@ export class TestTabLogic {
this.currentPath = this.helper.removeMultipleSlashes(this.helper.removeTrailingSlashes(path)) this.currentPath = this.helper.removeMultipleSlashes(this.helper.removeTrailingSlashes(path))
} }
generateTestFolder (path:string) { async generateTestFolder (path:string) {
// Todo move this check to File Manager after refactoring // Todo move this check to File Manager after refactoring
// Checking to ignore the value which contains only whitespaces // Checking to ignore the value which contains only whitespaces
if (!path || !(/\S/.test(path))) return if (!path || !(/\S/.test(path))) return
path = this.helper.removeMultipleSlashes(path) path = this.helper.removeMultipleSlashes(path)
const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0]) const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0])
fileProvider.exists(path).then((res: boolean) => { if(!await fileProvider.exists(path)) fileProvider.createDir(path)
if (!res) fileProvider.createDir(path)
})
} }
async pathExists (path: string) { async pathExists (path: string) {
// Checking to ignore the value which contains only whitespaces // Checking to ignore the value which contains only whitespaces
if (!path || !(/\S/.test(path))) return if (!path || !(/\S/.test(path))) return
const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0]) const fileProvider = this.fileManager.fileProviderOf(path.split('/')[0])
const res = await fileProvider.exists(path, (e: Error, res: boolean) => { return res }) return await fileProvider.exists(path)
return res
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

@ -67,6 +67,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
let [readyTestsNumber, setReadyTestsNumber] = useState<number>(0) // eslint-disable-line let [readyTestsNumber, setReadyTestsNumber] = useState<number>(0) // eslint-disable-line
let [runningTestsNumber, setRunningTestsNumber] = useState<number>(0) // eslint-disable-line let [runningTestsNumber, setRunningTestsNumber] = useState<number>(0) // eslint-disable-line
const areTestsRunning = useRef<boolean>(false)
const hasBeenStopped = useRef<boolean>(false) const hasBeenStopped = useRef<boolean>(false)
const isDebugging = useRef<boolean>(false) const isDebugging = useRef<boolean>(false)
const allTests = useRef<string[]>([]) const allTests = useRef<string[]>([])
@ -74,7 +75,6 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const currentErrors:any = useRef([]) // eslint-disable-line @typescript-eslint/no-explicit-any const currentErrors:any = useRef([]) // eslint-disable-line @typescript-eslint/no-explicit-any
const defaultPath = 'tests' const defaultPath = 'tests'
let areTestsRunning = false
let runningTestFileName: string let runningTestFileName: string
const filesContent: Record<string, Record<string, string>> = {} const filesContent: Record<string, Record<string, string>> = {}
@ -113,7 +113,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
allTests.current = tests allTests.current = tests
selectedTests.current = [...allTests.current] selectedTests.current = [...allTests.current]
updateTestFileList() updateTestFileList()
if (!areTestsRunning) await updateRunAction(file) if (!areTestsRunning.current) await updateRunAction(file)
} catch (e: any) { // eslint-disable-line @typescript-eslint/no-explicit-any } catch (e: any) { // eslint-disable-line @typescript-eslint/no-explicit-any
console.log(e) console.log(e)
setToasterMsg(e) setToasterMsg(e)
@ -153,7 +153,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
await setCurrentPath(defaultPath) await setCurrentPath(defaultPath)
}) })
testTab.fileManager.events.on('noFileSelected', () => { }) // eslint-disable-line testTab.fileManager.events.on('noFileSelected', async () => { await updateForNewCurrent() })
testTab.fileManager.events.on('currentFileChanged', async (file: string) => await updateForNewCurrent(file)) testTab.fileManager.events.on('currentFileChanged', async (file: string) => await updateForNewCurrent(file))
}, []) // eslint-disable-line }, []) // eslint-disable-line
@ -167,15 +167,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const handleTestDirInput = async (e: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any const handleTestDirInput = async (e: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
let testDirInput = trimTestDirInput(e.target.value) let testDirInput = trimTestDirInput(e.target.value)
testDirInput = helper.removeMultipleSlashes(testDirInput) testDirInput = helper.removeMultipleSlashes(testDirInput)
if (testDirInput !== '/') testDirInput = helper.removeTrailingSlashes(testDirInput)
setInputPathValue(testDirInput) setInputPathValue(testDirInput)
if (e.key === 'Enter') {
if (await testTabLogic.pathExists(testDirInput)) {
testTabLogic.setCurrentPath(testDirInput)
await updateForNewCurrent()
return
}
}
if (testDirInput) { if (testDirInput) {
if (testDirInput.endsWith('/') && testDirInput !== '/') { if (testDirInput.endsWith('/') && testDirInput !== '/') {
testDirInput = helper.removeTrailingSlashes(testDirInput) testDirInput = helper.removeTrailingSlashes(testDirInput)
@ -205,24 +197,14 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
} }
} }
const handleEnter = async (e: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
let inputPath = e.target.value
inputPath = helper.removeMultipleSlashes(trimTestDirInput(inputPath))
setInputPathValue(inputPath)
if (disableCreateButton) {
if (await testTabLogic.pathExists(inputPath)) {
await setCurrentPath(inputPath)
}
}
}
const handleCreateFolder = async () => { const handleCreateFolder = async () => {
let inputPath = trimTestDirInput(inputPathValue) let inputPath = trimTestDirInput(inputPathValue)
let path = helper.removeMultipleSlashes(inputPath) let path = helper.removeMultipleSlashes(inputPath)
if (path !== '/') path = helper.removeTrailingSlashes(path) if (path !== '/') path = helper.removeTrailingSlashes(path)
if (inputPath === '') inputPath = defaultPath if (inputPath === '') inputPath = defaultPath
setInputPathValue(path) setInputPathValue(path)
testTabLogic.generateTestFolder(inputPath) await testTabLogic.generateTestFolder(inputPath)
setToasterMsg('Folder created successfully')
setDisableCreateButton(true) setDisableCreateButton(true)
setDisableGenerateButton(false) setDisableGenerateButton(false)
testTabLogic.setCurrentPath(inputPath) testTabLogic.setCurrentPath(inputPath)
@ -518,7 +500,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (selectedTests.current?.length !== 0) { if (selectedTests.current?.length !== 0) {
setDisableRunButton(false) setDisableRunButton(false)
} }
areTestsRunning = false areTestsRunning.current = false
} }
} }
@ -565,7 +547,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
} }
const runTests = () => { const runTests = () => {
areTestsRunning = true areTestsRunning.current = true
hasBeenStopped.current = false hasBeenStopped.current = false
readyTestsNumber = 0 readyTestsNumber = 0
setReadyTestsNumber(readyTestsNumber) setReadyTestsNumber(readyTestsNumber)
@ -586,14 +568,14 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const updateRunAction = async (currentFile: any = null) => { // eslint-disable-line @typescript-eslint/no-explicit-any const updateRunAction = async (currentFile: any = null) => { // eslint-disable-line @typescript-eslint/no-explicit-any
const isSolidityActive = await testTab.appManager.isActive('solidity') const isSolidityActive = await testTab.appManager.isActive('solidity')
if (!isSolidityActive || !selectedTests.current?.length) { if (!isSolidityActive || !selectedTests.current.length) {
// setDisableRunButton(true) setDisableRunButton(true)
if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) { if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) {
setRunButtonTitle('No solidity file selected') setRunButtonTitle('No solidity file selected')
} else { } else {
setRunButtonTitle('The "Solidity Plugin" should be activated') setRunButtonTitle('The "Solidity Plugin" should be activated')
} }
} } else setDisableRunButton(false)
} }
const stopTests = () => { const stopTests = () => {
@ -610,7 +592,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const toggleCheckbox = (eChecked: boolean, index: number) => { const toggleCheckbox = (eChecked: boolean, index: number) => {
testFiles[index].checked = eChecked testFiles[index].checked = eChecked
setTestFiles(testFiles) setTestFiles([...testFiles])
selectedTests.current = getCurrentSelectedTests() selectedTests.current = getCurrentSelectedTests()
if (eChecked) { if (eChecked) {
setCheckSelectAll(true) setCheckSelectAll(true)
@ -627,7 +609,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const checkAll = (event: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any const checkAll = (event: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
testFiles.forEach((testFileObj) => testFileObj.checked = event.target.checked) testFiles.forEach((testFileObj) => testFileObj.checked = event.target.checked)
setTestFiles(testFiles) setTestFiles([...testFiles])
setCheckSelectAll(event.target.checked) setCheckSelectAll(event.target.checked)
if (event.target.checked) { if (event.target.checked) {
selectedTests.current = getCurrentSelectedTests() selectedTests.current = getCurrentSelectedTests()
@ -645,7 +627,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
} }
else else
testFiles = [] testFiles = []
setTestFiles(testFiles) setTestFiles([...testFiles])
} }
return ( return (
@ -672,8 +654,8 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
value={inputPathValue} value={inputPathValue}
title="Press 'Enter' to change the path for test files." title="Press 'Enter' to change the path for test files."
style={{ backgroundImage: "var(--primary)" }} style={{ backgroundImage: "var(--primary)" }}
onKeyUp={handleTestDirInput} onKeyDown={() => { if (inputPathValue === '/') setInputPathValue('')} }
onChange={handleEnter} onChange={handleTestDirInput}
onClick = {() => { if (inputPathValue === '/') setInputPathValue('')} } onClick = {() => { if (inputPathValue === '/') setInputPathValue('')} }
/> />
<button <button
@ -726,7 +708,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
/> />
<label className="text-nowrap pl-2 mb-0" htmlFor="checkAllTests"> Select all </label> <label className="text-nowrap pl-2 mb-0" htmlFor="checkAllTests"> Select all </label>
</div> </div>
<div className="testList py-2 mt-0 border-bottom">{testFiles?.length ? testFiles.map((testFileObj: TestObject, index) => { <div className="testList py-2 mt-0 border-bottom">{testFiles.length ? testFiles.map((testFileObj: TestObject, index) => {
const elemId = `singleTest${testFileObj.fileName}` const elemId = `singleTest${testFileObj.fileName}`
return ( return (
<div className="d-flex align-items-center py-1" key={index}> <div className="d-flex align-items-center py-1" key={index}>
@ -741,7 +723,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
<label className="text-warning h6" data-id="testTabTestsExecutionStopped" hidden={testsExecutionStoppedHidden}>The test execution has been stopped</label> <label className="text-warning h6" data-id="testTabTestsExecutionStopped" hidden={testsExecutionStoppedHidden}>The test execution has been stopped</label>
<label className="text-danger h6" data-id="testTabTestsExecutionStoppedError" hidden={testsExecutionStoppedErrorHidden}>The test execution has been stopped because of error(s) in your test file</label> <label className="text-danger h6" data-id="testTabTestsExecutionStoppedError" hidden={testsExecutionStoppedErrorHidden}>The test execution has been stopped because of error(s) in your test file</label>
</div> </div>
<div id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput">{testsOutput}</div> <div className="mx-3 mb-2 pb-4 border-primary" id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput">{testsOutput}</div>
</div> </div>
</div> </div>
) )

@ -36,7 +36,7 @@ export const TabsUI = (props: TabsUIProps) => {
const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass
const classNameTab = 'nav-item nav-link d-flex justify-content-center align-items-center px-2 py-1 tab' + (index === currentIndexRef.current ? ' active' : '') const classNameTab = 'nav-item nav-link d-flex justify-content-center align-items-center px-2 py-1 tab' + (index === currentIndexRef.current ? ' active' : '')
return ( return (
<div onClick={() => { props.onSelect(index); currentIndexRef.current = index; setSelectedIndex(index) }} ref={el => { tabsRef.current[index] = el }} className={classNameTab} title={tab.tooltip}> <div ref={el => { tabsRef.current[index] = el }} className={classNameTab} title={tab.tooltip}>
{tab.icon ? (<img className="my-1 mr-1 iconImage" src={tab.icon} />) : (<i className={classNameImg}></i>)} {tab.icon ? (<img className="my-1 mr-1 iconImage" src={tab.icon} />) : (<i className={classNameImg}></i>)}
<span className="title-tabs">{tab.title}</span> <span className="title-tabs">{tab.title}</span>
<span className="close-tabs" onClick={(event) => { props.onClose(index); event.stopPropagation() }}> <span className="close-tabs" onClick={(event) => { props.onClose(index); event.stopPropagation() }}>
@ -88,6 +88,11 @@ export const TabsUI = (props: TabsUIProps) => {
tabsElement.current = domEl tabsElement.current = domEl
tabsElement.current.addEventListener('wheel', transformScroll) tabsElement.current.addEventListener('wheel', transformScroll)
}} }}
onSelect={(index) => {
props.onSelect(index)
currentIndexRef.current = index
setSelectedIndex(index)
}}
> >
<TabList className="d-flex flex-row align-items-center"> <TabList className="d-flex flex-row align-items-center">
{props.tabs.map((tab, i) => <Tab className="py-1" key={tab.name}>{renderTab(tab, i)}</Tab>)} {props.tabs.map((tab, i) => <Tab className="py-1" key={tab.name}>{renderTab(tab, i)}</Tab>)}

@ -111,9 +111,11 @@ export const listenOnNetworkAction = async (plugins, isListening) => {
} }
export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch<any>) => { export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch<any>) => {
const provider = plugins.blockchain.getProvider()
plugins.txListener.event.register(NEW_BLOCK, (block) => { plugins.txListener.event.register(NEW_BLOCK, (block) => {
if (!block.transactions || (block.transactions && !block.transactions.length)) { if (!block.transactions || (block.transactions && !block.transactions.length)) {
dispatch({ type: EMPTY_BLOCK, payload: { message: 0 } }) dispatch({ type: EMPTY_BLOCK, payload: { message: 0, provider } })
} }
}) })
plugins.txListener.event.register(KNOWN_TRANSACTION, () => { plugins.txListener.event.register(KNOWN_TRANSACTION, () => {
@ -128,6 +130,8 @@ export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch<any>) =
const log = async (plugins, tx, receipt, dispatch: React.Dispatch<any>) => { const log = async (plugins, tx, receipt, dispatch: React.Dispatch<any>) => {
const resolvedTransaction = await plugins.txListener.resolvedTransaction(tx.hash) const resolvedTransaction = await plugins.txListener.resolvedTransaction(tx.hash)
const provider = plugins.blockchain.getProvider()
if (resolvedTransaction) { if (resolvedTransaction) {
let compiledContracts = null let compiledContracts = null
if (plugins._deps.compilersArtefacts.__last) { if (plugins._deps.compilersArtefacts.__last) {
@ -135,11 +139,11 @@ export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch<any>) =
} }
await plugins.eventsDecoder.parseLogs(tx, resolvedTransaction.contractName, compiledContracts, async (error, logs) => { await plugins.eventsDecoder.parseLogs(tx, resolvedTransaction.contractName, compiledContracts, async (error, logs) => {
if (!error) { if (!error) {
await dispatch({ type: KNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs }] } }) await dispatch({ type: KNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs }], provider } })
} }
}) })
} else { } else {
await dispatch({ type: UNKNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt }] } }) await dispatch({ type: UNKNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt }], provider } })
} }
} }

@ -2,14 +2,14 @@ import React from 'react' // eslint-disable-line
const CheckTxStatus = ({ tx, type }) => { const CheckTxStatus = ({ tx, type }) => {
if (tx.status === '0x1' || tx.status === true) { if (tx.status === '0x1' || tx.status === true) {
return (<i className='txStatus succeeded fas fa-check-circle'></i>) return (<i className='remix_ui_terminal_txStatus remix_ui_terminal_succeeded fas fa-check-circle'></i>)
} }
if (type === 'call' || type === 'unknownCall' || type === 'unknown') { if (type === 'call' || type === 'unknownCall' || type === 'unknown') {
return (<i className='txStatus call'>call</i>) return (<i className='remix_ui_terminal_txStatus remix_ui_terminal_call'>call</i>)
} else if (tx.status === '0x0' || tx.status === false) { } else if (tx.status === '0x0' || tx.status === false) {
return (<i className='txStatus failed fas fa-times-circle'></i>) return (<i className='remix_ui_terminal_txStatus remix_ui_terminal_failed fas fa-times-circle'></i>)
} else { } else {
return (<i className='txStatus notavailable fas fa-circle-thin' title='Status not available' ></i>) return (<i className='remix_ui_terminal_txStatus fas fa-circle-thin' title='Status not available' ></i>)
} }
} }

@ -4,7 +4,7 @@ import helper from 'apps/remix-ide/src/lib/helper'
const remixLib = require('@remix-project/remix-lib') const remixLib = require('@remix-project/remix-lib')
const typeConversion = remixLib.execution.typeConversion const typeConversion = remixLib.execution.typeConversion
const Context = ({ opts, blockchain }) => { const Context = ({ opts, provider }: { opts, provider: string }) => {
const data = opts.tx || '' const data = opts.tx || ''
const from = opts.from ? helper.shortenHexData(opts.from) : '' const from = opts.from ? helper.shortenHexData(opts.from) : ''
let to = opts.to let to = opts.to
@ -16,44 +16,45 @@ const Context = ({ opts, blockchain }) => {
const block = data.receipt ? data.receipt.blockNumber : data.blockNumber || '' const block = data.receipt ? data.receipt.blockNumber : data.blockNumber || ''
const i = data.receipt ? data.transactionIndex : data.transactionIndex const i = data.receipt ? data.transactionIndex : data.transactionIndex
const value = val ? typeConversion.toInt(val) : 0 const value = val ? typeConversion.toInt(val) : 0
if (blockchain.getProvider() === 'vm') {
if (provider === 'vm') {
return ( return (
<div> <div>
<span className='txLog_7Xiho'> <span>
<span className='tx'>[vm]</span> <span className='remix_ui_terminal_tx'>[vm]</span>
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>from:</span> {from}</div>
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>to:</span> {to}</div>
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>value:</span> {value} wei</div>
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>data:</span> {input}</div>
<div className='txItem'><span className='txItemTitle'>logs:</span> {logs}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>logs:</span> {logs}</div>
<div className='txItem'><span className='txItemTitle'>hash:</span> {hash}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>hash:</span> {hash}</div>
</span> </span>
</div>) </div>)
} else if (blockchain.getProvider() !== 'vm' && data.resolvedData) { } else if (provider !== 'vm' && data.resolvedData) {
return ( return (
<div> <div>
<span className='txLog_7Xiho'> <span>
<span className='tx'>[block:{block} txIndex:{i}]</span> <span className='remix_ui_terminal_tx'>[block:{block} txIndex:{i}]</span>
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>from:</span> {from}</div>
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>to:</span> {to}</div>
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>value:</span> {value} wei</div>
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>data:</span> {input}</div>
<div className='txItem'><span className='txItemTitle'>logs:</span> {logs}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>logs:</span> {logs}</div>
<div className='txItem'><span className='txItemTitle'>hash:</span> {hash}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>hash:</span> {hash}</div>
</span> </span>
</div>) </div>)
} else { } else {
hash = helper.shortenHexData(data.blockHash) hash = helper.shortenHexData(data.blockHash)
return ( return (
<div> <div>
<span className='txLog'> <span>
<span className='tx'>[block:{block} txIndex:{i}]</span> <span className='remix_ui_terminal_tx'>[block:{block} txIndex:{i}]</span>
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>from:</span> {from}</div>
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>to:</span> {to}</div>
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>value:</span> {value} wei</div>
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>data:</span> {input}</div>
<div className='txItem'><span className='txItemTitle'>logs:</span> {logs}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>logs:</span> {logs}</div>
<div className='txItem'><span className='txItemTitle'>hash:</span> {hash}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>hash:</span> {hash}</div>
</span> </span>
</div>) </div>)
} }

@ -1,9 +1,8 @@
import React, { useState } from 'react' // eslint-disable-line import React, { useState } from 'react' // eslint-disable-line
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import helper from 'apps/remix-ide/src/lib/helper' import helper from 'apps/remix-ide/src/lib/helper'
import CheckTxStatus from './ChechTxStatus' // eslint-disable-line import CheckTxStatus from './ChechTxStatus' // eslint-disable-line
import showTable from './Table' import showTable from './Table'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
const remixLib = require('@remix-project/remix-lib') const remixLib = require('@remix-project/remix-lib')
const typeConversion = remixLib.execution.typeConversion const typeConversion = remixLib.execution.typeConversion
@ -25,18 +24,18 @@ const RenderCall = ({ tx, resolvedData, logs, index, plugin, showTableHash, txDe
return ( return (
<span id={`tx${tx.hash}`} key={index}> <span id={`tx${tx.hash}`} key={index}>
<div className="log" onClick={(event) => txDetails(event, tx)}> <div className="remix_ui_terminal_log" onClick={(event) => txDetails(event, tx)}>
<CheckTxStatus tx={tx} type={txType} /> <CheckTxStatus tx={tx} type={txType} />
<span className="txLog"> <span>
<span className="tx">[call]</span> <span className="tx">[call]</span>
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>from:</span> {from}</div>
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>to:</span> {to}</div>
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div> <div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>data:</span> {input}</div>
</span> </span>
<div className='buttons'> <div className='remix_ui_terminal_buttons'>
<div className="debug btn btn-primary btn-sm" onClick={(event) => debug(event, tx)}>Debug</div> <div className="remix_ui_terminal_debug btn btn-primary btn-sm" onClick={(event) => debug(event, tx)}>Debug</div>
</div> </div>
<i className="terminal_arrow fas fa-angle-down"></i> <i className="remix_ui_terminal_terminal_arrow fas fa-angle-down"></i>
</div> </div>
{showTableHash.includes(tx.hash) ? showTable({ {showTableHash.includes(tx.hash) ? showTable({
hash: tx.hash, hash: tx.hash,

@ -8,7 +8,7 @@ import showTable from './Table'
const remixLib = require('@remix-project/remix-lib') const remixLib = require('@remix-project/remix-lib')
const typeConversion = remixLib.execution.typeConversion const typeConversion = remixLib.execution.typeConversion
const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugin, showTableHash, txDetails, modal }) => { const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugin, showTableHash, txDetails, modal, provider }) => {
const debug = (event, tx) => { const debug = (event, tx) => {
event.stopPropagation() event.stopPropagation()
if (tx.isCall && tx.envMode !== 'vm') { if (tx.isCall && tx.envMode !== 'vm') {
@ -24,13 +24,18 @@ const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugi
const options = { from, to, tx, logs } const options = { from, to, tx, logs }
return ( return (
<span id={`tx${tx.hash}`} key={index}> <span id={`tx${tx.hash}`} key={index}>
<div className="log" onClick={(event) => txDetails(event, tx)}> <div className="remix_ui_terminal_log" onClick={(event) => txDetails(event, tx)}>
<CheckTxStatus tx={receipt} type={txType} /> <CheckTxStatus tx={receipt} type={txType} />
<Context opts = { options } blockchain={plugin.blockchain} /> <Context opts = { options } provider={provider} />
<div className='buttons'> <div className='remix_ui_terminal_buttons'>
<div className='debug btn btn-primary btn-sm' data-shared='txLoggerDebugButton' data-id={`txLoggerDebugButton${tx.hash}`} onClick={(event) => debug(event, tx)}>Debug</div> <div
className='remix_ui_terminal_debug btn btn-primary btn-sm'
data-shared='txLoggerDebugButton'
data-id={`txLoggerDebugButton${tx.hash}`}
onClick={(event) => debug(event, tx)}
>Debug</div>
</div> </div>
<i className = {`terminal_arrow fas ${(showTableHash.includes(tx.hash)) ? 'fa-angle-up' : 'fa-angle-down'}`}></i> <i className={`remix_ui_terminal_arrow fas ${(showTableHash.includes(tx.hash)) ? 'fa-angle-up' : 'fa-angle-down'}`}></i>
</div> </div>
{showTableHash.includes(tx.hash) ? showTable({ {showTableHash.includes(tx.hash) ? showTable({
hash: tx.hash, hash: tx.hash,

@ -1,10 +1,9 @@
import React, { useState } from 'react' // eslint-disable-line import React, { useState } from 'react' // eslint-disable-line
import { ModalDialog } from '@remix-ui/modal-dialog'// eslint-disable-line
import CheckTxStatus from './ChechTxStatus' // eslint-disable-line import CheckTxStatus from './ChechTxStatus' // eslint-disable-line
import Context from './Context' // eslint-disable-line import Context from './Context' // eslint-disable-line
import showTable from './Table' import showTable from './Table'
const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash, txDetails, modal }) => { const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash, txDetails, modal, provider }) => {
const debug = (event, tx) => { const debug = (event, tx) => {
event.stopPropagation() event.stopPropagation()
if (tx.isCall && tx.envMode !== 'vm') { if (tx.isCall && tx.envMode !== 'vm') {
@ -20,13 +19,17 @@ const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash,
const options = { from, to, tx } const options = { from, to, tx }
return ( return (
<span id={`tx${tx.hash}`} key={index}> <span id={`tx${tx.hash}`} key={index}>
<div className="log" onClick={(event) => txDetails(event, tx)}> <div className="remix_ui_terminal_log" onClick={(event) => txDetails(event, tx)}>
<CheckTxStatus tx={receipt || tx} type={txType} /> <CheckTxStatus tx={receipt || tx} type={txType} />
<Context opts = { options } blockchain={plugin.blockchain} /> <Context opts = { options } provider={provider} />
<div className='buttons'> <div className='remix_ui_terminal_buttons'>
<div className='debug btn btn-primary btn-sm' data-shared='txLoggerDebugButton' data-id={`txLoggerDebugButton${tx.hash}`} onClick={(event) => debug(event, tx)}>Debug</div> <div className='remix_ui_terminal_debug btn btn-primary btn-sm'
data-shared='txLoggerDebugButton'
data-id={`txLoggerDebugButton${tx.hash}`}
onClick={(event) => debug(event, tx)}
>Debug</div>
</div> </div>
<i className = {`terminal_arrow fas ${(showTableHash.includes(tx.hash)) ? 'fa-angle-up' : 'fa-angle-down'}`}></i> <i className = {`remix_ui_terminal_arrow fas ${(showTableHash.includes(tx.hash)) ? 'fa-angle-up' : 'fa-angle-down'}`}></i>
</div> </div>
{showTableHash.includes(tx.hash) ? showTable({ {showTableHash.includes(tx.hash) ? showTable({
hash: tx.hash, hash: tx.hash,

@ -37,30 +37,30 @@ const showTable = (opts, showTableHash) => {
const val = opts.val != null ? typeConversion.toInt(opts.val) : 0 const val = opts.val != null ? typeConversion.toInt(opts.val) : 0
return ( return (
<table <table
className={`txTable ${showTableHash.includes(opts.hash) ? 'active' : ''}`} className={`mt-1 mb-2 mr-4 align-self-center ${showTableHash.includes(opts.hash) ? 'active' : ''}`}
id="txTable" id="txTable"
data-id={`txLoggerTable${opts.hash}`} data-id={`txLoggerTable${opts.hash}`}
> >
<tbody> <tbody>
{opts.status !== undefined ? ( {opts.status !== undefined ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
status status
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableStatus${opts.hash}`} data-id={`txLoggerTableStatus${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
>{`${opts.status} ${msg}`}</td> >{`${opts.status} ${msg}`}</td>
</tr> </tr>
) : null} ) : null}
{opts.hash && !opts.isCall ? ( {opts.hash && !opts.isCall ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
transaction hash transaction hash
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableHash${opts.hash}`} data-id={`txLoggerTableHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -70,12 +70,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.contractAddress ? ( {opts.contractAddress ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
contract address contract address
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableContractAddress${opts.hash}`} data-id={`txLoggerTableContractAddress${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -85,12 +85,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.from ? ( {opts.from ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td tableTitle" data-shared={`key_${opts.hash}`}> <td className="td tableTitle" data-shared={`key_${opts.hash}`}>
from from
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableFrom${opts.hash}`} data-id={`txLoggerTableFrom${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -100,12 +100,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.to ? ( {opts.to ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
to to
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableTo${opts.hash}`} data-id={`txLoggerTableTo${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -115,12 +115,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.gas ? ( {opts.gas ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
gas gas
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableGas${opts.hash}`} data-id={`txLoggerTableGas${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -130,12 +130,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.transactionCost ? ( {opts.transactionCost ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
transaction cost transaction cost
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableTransactionCost${opts.hash}`} data-id={`txLoggerTableTransactionCost${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -145,12 +145,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.executionCost ? ( {opts.executionCost ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
execution cost execution cost
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableExecutionHash${opts.hash}`} data-id={`txLoggerTableExecutionHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -160,12 +160,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.hash ? ( {opts.hash ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
hash hash
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableHash${opts.hash}`} data-id={`txLoggerTableHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -175,12 +175,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.input ? ( {opts.input ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
input input
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableHash${opts.hash}`} data-id={`txLoggerTableHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -190,12 +190,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts['decoded input'] ? ( {opts['decoded input'] ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
decoded input decoded input
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableHash${opts.hash}`} data-id={`txLoggerTableHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -205,12 +205,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts['decoded output'] ? ( {opts['decoded output'] ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
decoded output decoded output
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableHash${opts.hash}`} data-id={`txLoggerTableHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -220,12 +220,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.logs ? ( {opts.logs ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
logs logs
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableHash${opts.hash}`} data-id={`txLoggerTableHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >
@ -238,12 +238,12 @@ const showTable = (opts, showTableHash) => {
</tr> </tr>
) : null} ) : null}
{opts.val ? ( {opts.val ? (
<tr className="tr"> <tr className="remix_ui_terminal_tr">
<td className="td" data-shared={`key_${opts.hash}`}> <td className="remix_ui_terminal_td" data-shared={`key_${opts.hash}`}>
val val
</td> </td>
<td <td
className="td" className="remix_ui_terminal_td"
data-id={`txLoggerTableHash${opts.hash}`} data-id={`txLoggerTableHash${opts.hash}`}
data-shared={`pair_${opts.hash}`} data-shared={`pair_${opts.hash}`}
> >

@ -144,52 +144,52 @@ export const registerScriptRunnerReducer = (state, action) => {
case HTML: case HTML:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider })
} }
case LOG: case LOG:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info', provider: action.payload.provider })
} }
case INFO: case INFO:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info', provider: action.payload.provider })
} }
case WARN: case WARN:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-warning' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-warning', provider: action.payload.provider })
} }
case ERROR: case ERROR:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-danger' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-danger', provider: action.payload.provider })
} }
case SCRIPT: case SCRIPT:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider })
} }
case KNOWN_TRANSACTION: case KNOWN_TRANSACTION:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction', provider: action.payload.provider })
} }
case UNKNOWN_TRANSACTION: case UNKNOWN_TRANSACTION:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'unknownTransaction' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'unknownTransaction', provider: action.payload.provider })
} }
case EMPTY_BLOCK: case EMPTY_BLOCK:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'emptyBlock' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'emptyBlock', provider: action.payload.provider })
} }
case NEW_TRANSACTION: case NEW_TRANSACTION:
return { return {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '' }) journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', provider: action.payload.provider })
} }
} }
} }

@ -3,425 +3,228 @@ element.style {
height: 323px !important; height: 323px !important;
} }
#terminalCliInput{ #terminalCliInput{
width: 95%; width: 97%;
background: transparent; font-weight: 800;
border: none; background: var(--body-bg)
font-weight: bold;
color: #a2a3b4;
border-top-style: hidden;
border-right-style: hidden;
border-left-style: hidden;
border-bottom-style: hidden;
} }
#terminalCliInput:focus { #terminalCliInput:focus {
outline: none; outline: none;
} }
.border-primary { .remix_ui_terminal_panel {
border-color: #007aa6!important; position : relative;
display : flex;
flex-direction : column;
font-size : 12px;
min-height : 3em;
} }
.remix_ui_terminal_bar {
/* seleted option should reflect the theme color */ z-index : 2;
.selectedOptions { }
background-color: #222336; .remix_ui_terminal_menu {
max-height : 35px;
min-height : 35px;
}
.remix_ui_terminal_toggleTerminal {
cursor : pointer;
}
.remix_ui_terminal_toggleTerminal:hover {
color : var(--secondary);
}
.remix_ui_terminal_container {
overflow-y : auto;
font-family : monospace;
background-repeat : no-repeat;
background-position : center 15%;
background-size : auto calc(75% - 1.7em);
} }
.panel { .remix_ui_terminal_journal {
position : relative; margin-top : auto;
display : flex; margin-bottom : 2rem;
flex-direction : column; font-family : monospace;
font-size : 12px; overflow-y : scroll;
min-height : 3em; }
} .remix_ui_terminal_block {
.bar { white-space : pre-wrap;
display : flex; font-family : monospace;
z-index : 2; line-height : 2ch;
} }
.menu { .remix_ui_terminal_block > pre {
position : relative; max-height : 200px;
display : flex; }
align-items : center; .remix_ui_terminal_cli {
width : 100%; white-space : nowrap;
max-height : 35px; line-height : 1.7em;
min-height : 35px; font-family : monospace;
} padding : .4em;
.toggleTerminal { color : var(--primary);
cursor : pointer; }
} .remix_ui_terminal_prompt {
.toggleTerminal:hover { font-family : monospace;
color : var(--secondary); }
} .remix_ui_terminal_input {
.terminal_container { outline : none;
display : flex; font-family : monospace;
flex-direction : column; }
height : 100%; .remix_ui_terminal_search {
overflow-y : auto; width : 330px;
font-family : monospace; padding-left : 20px;
margin : 0px; padding-top : 1px;
background-repeat : no-repeat; padding-bottom : 1px;
background-position : center 15%; }
background-size : auto calc(75% - 1.7em); .remix_ui_terminal_filter {
} height : 80%;
.terminal { white-space : nowrap;
position : relative; overflow : hidden;
display : flex; text-overflow : ellipsis;
flex-direction : column; }
height : 100%; .remix_ui_terminal_searchIcon {
} width : 25px;
.journal { border-top-left-radius : 3px;
margin-top : auto; border-bottom-left-radius : 3px;
font-family : monospace; margin-right : 5px;
} }
.block {
word-break : break-word;
white-space : pre-wrap;
line-height : 2ch;
padding : 1ch;
margin-top : 2ch;
}
.block > pre {
max-height : 200px;
}
.cli {
line-height : 1.7em;
font-family : monospace;
padding : .4em;
color : var(--primary);
}
.prompt {
margin-right : 0.5em;
font-family : monospace;
font-weight : bold;
font-size : 14px;
color : lightgray;
}
.input {
word-break : break-word;
outline : none;
font-family : monospace;
}
.search {
display : flex;
align-items : center;
width : 330px;
padding-left : 20px;
height : 100%;
padding-top : 1px;
padding-bottom : 1px;
}
.filter {
height : 80%;
white-space : nowrap;
overflow : hidden;
text-overflow : ellipsis;
}
.searchIcon {
width : 25px;
border-top-left-radius : 3px;
border-bottom-left-radius : 3px;
display : flex;
align-items : center;
justify-content : center;
margin-right : 5px;
}
.listen {
margin-right : 30px;
min-width : 40px;
height : 13px;
display : flex;
align-items : center;
}
.listenLabel {
min-width: 50px;
}
.verticalLine {
border-left : 1px solid var(--secondary);
height : 65%;
}
.dragbarHorizontal {
position : absolute;
top : 0;
height : 0.2em;
right : 0;
left : 0;
cursor : row-resize;
z-index : 999;
}
.dragbarDragging {
background-color: var(--primary);
border: 2px solid var(--primary);
}
.console {
cursor : pointer;
}
.dragbarHorizontal:hover {
background-color: var(--secondary);
border:2px solid var(--secondary);
}
.listenOnNetwork {
min-height: auto;
}
.ghostbar {
position : absolute;
height : 6px;
opacity : 0.5;
cursor : row-resize;
z-index : 9999;
left : 0;
right : 0;
}
.divider-hitbox {
color: white;
cursor: row-resize;
align-self: stretch;
display: flex;
align-items: center;
padding: 0 1rem;
}
.ul {margin-left: 0; padding-left: 20px;}
.popup {
position : absolute;
text-align : left;
width : 95%;
font-family : monospace;
background-color : var(--secondary);
overflow : auto;
padding-bottom : 13px;
z-index : 80;
bottom : 1em;
border-width : 4px;
left : 2em;
overflow-y : scroll;
}
.autoCompleteItem {
padding : 4px;
border-radius : 2px;
}
.popup a {
cursor : pointer;
}
.listHandlerShow {
display : block;
}
.listHandlerHide {
display : none;
}
.listHandlerButtonShow {
position : fixed;
width : 46%;
}
.pageNumberAlignment {
font-size : 10px;
float : right;
}
.modalContent {
position : absolute;
margin-left : 20%;
margin-bottom : 32px;
bottom : 0px;
padding : 0;
line-height : 18px;
font-size : 12px;
width : 40%;
box-shadow : 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
-webkit-animation-name: animatebottom;
-webkit-animation-duration: 0.4s;
animation-name : animatetop;
animation-duration: 0.4s
}
@-webkit-keyframes animatetop { .remix_ui_terminal_console {
from {bottom: -300px; opacity: 0} cursor : pointer;
to {bottom: 0; opacity: 1} }
}
@keyframes animatetop { .remix_ui_terminal_listenOnNetwork {
from {bottom: -300px; opacity: 0} min-height: auto;
to {bottom: 0; opacity: 1} }
}
.remix_ui_terminal_popup {
width : 95%;
font-family : monospace;
background-color : var(--secondary);
overflow : auto;
z-index : 80;
bottom : 2.2em;
border-width : 4px;
overflow-y : scroll;
box-shadow : 0px 0px 13px -8px;
}
.remix_ui_terminal_autoCompleteItem {
padding : 4px;
border-radius : 2px;
}
.remix_ui_terminal_popup a {
cursor : pointer;
}
/* tx logger css*/ .call {
font-size: 7px;
border-radius: 50%;
min-width: 20px;
min-height: 20px;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-info);
text-transform: uppercase;
font-weight: bold;
}
.log { .remix_ui_terminal_txItem {
display: flex; color: var(--text-info);
cursor: pointer; margin-right: 5px;
align-items: center; float: left;
cursor: pointer; }
}
.log:hover {
opacity: 0.8;
}
.txStatus { .remix_ui_terminal_txItemTitle {
display: flex; font-weight: bold;
font-size: 20px; }
margin-right: 20px;
float: left;
}
.succeeded {
color: var(--success);
}
.failed {
color: var(--danger);
}
.terminal_arrow { /* tx logger css*/
color: var(--text-info);
font-size: 20px;
cursor: pointer;
display: flex;
margin-left: 10px;
}
.terminal_arrow:hover {
color: var(--secondary);
}
.notavailable {
}
.call {
font-size: 7px;
border-radius: 50%;
min-width: 20px;
min-height: 20px;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-info);
text-transform: uppercase;
font-weight: bold;
}
.txItem {
color: var(--text-info);
margin-right: 5px;
float: left;
}
.txItemTitle {
font-weight: bold;
}
.tx {
color: var(--text-info);
font-weight: bold;
float: left;
margin-right: 10px;
}
.txTable,
.tr,
.td {
border-collapse: collapse;
font-size: 10px;
color: var(--text-info);
border: 1px solid var(--text-info);
transition: max-height 0.3s, padding 0.3s;
}
table .active {
transition: max-height 0.6s, padding 0.6s;
}
#txTable {
margin-top: 1%;
margin-bottom: 5%;
align-self: center;
width: 85%;
}
.tr, .td {
padding: 4px;
vertical-align: baseline;
}
.td:first-child {
min-width: 30%;
width: 30%;
align-items: baseline;
font-weight: bold;
}
.tableTitle {
width: 25%;
}
.buttons {
display: flex;
margin-left: auto;
}
.debug {
white-space: nowrap;
}
.debug:hover {
opacity: 0.8;
}
.remix_ui_terminal_log {
display: flex;
cursor: pointer;
align-items: center;
cursor: pointer;
padding-top: 0.5rem;
}
.remix_ui_terminal_log:hover {
opacity: 0.8;
}
/* Style the accordion section */ .remix_ui_terminal_txStatus {
.accordion__section {
display: flex; display: flex;
flex-direction: column; font-size: 20px;
margin-right: 20px;
float: left;
}
.remix_ui_terminal_succeeded {
color: var(--success);
}
.remix_ui_terminal_failed {
color: var(--danger);
} }
/* Style the buttons that are used to open and close the accordion panel */ .remix_ui_terminal_arrow {
.accordion { color: var(--text-info);
background-color: #eee; font-size: 20px;
color: #444;
cursor: pointer; cursor: pointer;
padding: 18px;
display: flex; display: flex;
margin-left: 10px;
}
.remix_ui_terminal_arrow:hover {
color: var(--secondary);
}
.remix_ui_terminal_call {
font-size: 7px;
border-radius: 50%;
min-width: 20px;
min-height: 20px;
display: flex;
justify-content: center;
align-items: center; align-items: center;
border: none; color: var(--text-info);
outline: none; text-transform: uppercase;
transition: background-color 0.6s ease; font-weight: bold;
} }
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */ .remix_ui_terminal_tx {
/* .accordion:hover, color: var(--text-info);
.active { font-weight: bold;
background-color: #ccc; float: left;
} */ margin-right: 10px;
/* Style the accordion content title */
.accordion__title {
font-family: "Open Sans", sans-serif;
font-weight: 600;
font-size: 14px;
} }
/* Style the accordion chevron icon */ .remix_ui_terminal_tr,
.accordion__icon { .remix_ui_terminal_td {
margin-left: auto; border-collapse: collapse;
transition: transform 0.6s ease; font-size: 10px;
color: var(--text-info);
border: 1px solid var(--text-info);
transition: max-height 0.3s, padding 0.3s;
} }
table .active {
/* Style to rotate icon when state is active */ transition: max-height 0.6s, padding 0.6s;
.rotate {
transform: rotate(90deg);
} }
.remix_ui_terminal_tr, .remix_ui_terminal_td {
/* Style the accordion content panel. Note: hidden by default */ padding: 4px;
.accordion__content { vertical-align: baseline;
background-color: white;
overflow: hidden;
transition: max-height 0.6s ease;
} }
.remix_ui_terminal_td:first-child {
/* Style the accordion content text */ min-width: 30%;
.accordion__text { width: 30%;
font-family: "Open Sans", sans-serif; align-items: baseline;
font-weight: 400; font-weight: bold;
font-size: 14px; }
padding: 18px; .remix_ui_terminal_tableTitle {
width: 25%;
}
.remix_ui_terminal_buttons {
display: flex;
margin-left: auto;
}
.remix_ui_terminal_debug {
white-space: nowrap;
}
.remix_ui_terminal_debug:hover {
opacity: 0.8;
} }

@ -72,13 +72,9 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const messagesEndRef = useRef(null) const messagesEndRef = useRef(null)
// terminal dragable // terminal dragable
const leftRef = useRef(null)
const panelRef = useRef(null) const panelRef = useRef(null)
const terminalMenu = useRef(null) const terminalMenu = useRef(null)
const terminalMenuOffsetHeight = (terminalMenu.current && terminalMenu.current.offsetHeight) || 35
const terminalDefaultPosition = config.get('terminal-top-offset')
const scrollToBottom = () => { const scrollToBottom = () => {
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' }) messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
} }
@ -381,14 +377,24 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
} else { } else {
setShowTableHash((prevState) => ([...prevState, tx.hash])) setShowTableHash((prevState) => ([...prevState, tx.hash]))
} }
scrollToBottom()
} }
const handleAutoComplete = () => ( const handleAutoComplete = () => (
<div className='popup alert alert-secondary' style={{ display: (autoCompletState.showSuggestions && autoCompletState.userInput !== '' && (autoCompletState.userInput.length > 2)) && autoCompletState.data._options.length > 0 ? 'block' : 'none' }}> <div
className='remix_ui_terminal_popup bg-light ml-4 p-2 position-absolute text-left '
style={{ display: (autoCompletState.showSuggestions && autoCompletState.userInput !== '' && (autoCompletState.userInput.length > 2)) && autoCompletState.data._options.length > 0 ? 'block' : 'none' }}
>
<div> <div>
{autoCompletState.data._options.map((item, index) => { {autoCompletState.data._options.map((item, index) => {
return ( return (
<div key={index} data-id="autoCompletePopUpAutoCompleteItem" className={`autoCompleteItem listHandlerShow item ${autoCompletState.data._options[autoCompletState.activeSuggestion] === item ? 'border border-primary ' : ''}`} onKeyDown={ handleSelect } onClick={() => handleClickSelect(item)}> <div
key={index}
data-id="autoCompletePopUpAutoCompleteItem"
className={`remix_ui_terminal_autoCompleteItem item ${autoCompletState.data._options[autoCompletState.activeSuggestion] === item ? 'border border-primary ' : ''}`}
onKeyDown={ handleSelect }
onClick={() => handleClickSelect(item)}
>
<div> <div>
{getKeyOf(item)} {getKeyOf(item)}
</div> </div>
@ -413,17 +419,29 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
props.plugin.call('layout', 'minimize', props.plugin.profile.name, isOpen) props.plugin.call('layout', 'minimize', props.plugin.profile.name, isOpen)
} }
useEffect(() => {
props.plugin.on('layout', 'change', (panels) => {
setIsOpen(!panels.terminal.minimized)
})
return () => {
props.plugin.off('layout', 'change')
}
}, [])
const classNameBlock = 'remix_ui_terminal_block px-4 py-1 text-break'
return ( return (
<div style={{ flexGrow: 1 }} className='panel' ref={panelRef}> <div style={{ flexGrow: 1 }} className='remix_ui_terminal_panel' ref={panelRef}>
<div className="bar"> <div className="remix_ui_terminal_bar d-flex">
<div className="menu border-top border-dark bg-light" ref={terminalMenu} data-id="terminalToggleMenu"> <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 toggleTerminal fas ${isOpen ? 'fa-angle-double-down' : 'fa-angle-double-up'}`} data-id="terminalToggleIcon" onClick={handleToggleTerminal}></i> <i className={`mx-2 remix_ui_terminal_toggleTerminal fas ${isOpen ? 'fa-angle-double-down' : 'fa-angle-double-up'}`} data-id="terminalToggleIcon" onClick={handleToggleTerminal}></i>
<div className="mx-2 console" id="clearConsole" data-id="terminalClearConsole" onClick={handleClearConsole} > <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 className="fas fa-ban" aria-hidden="true" title="Clear console"
></i> ></i>
</div> </div>
<div className="mx-2" title='Pending Transactions'>0</div> <div className="mx-2" title='Pending Transactions'>0</div>
<div className="pt-1 h-80 mx-3 align-items-center listenOnNetwork custom-control custom-checkbox"> <div className="pt-1 h-80 mx-3 align-items-center remix_ui_terminal_listenOnNetwork custom-control custom-checkbox">
<input <input
className="custom-control-input" className="custom-control-input"
id="listenNetworkCheck" id="listenNetworkCheck"
@ -439,47 +457,81 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
listen on network listen on network
</label> </label>
</div> </div>
<div className="search"> <div className="remix_ui_terminal_search d-flex align-items-center h-100">
<i className="fas fa-search searchIcon bg-light" aria-hidden="true"></i> <i
className="remix_ui_terminal_searchIcon d-flex align-items-center justify-content-center fas fa-search bg-light"
aria-hidden="true">
</i>
<input <input
onChange={(event) => setSearchInput(event.target.value.trim()) } onChange={(event) => setSearchInput(event.target.value.trim()) }
type="text" type="text"
className="border filter form-control" className="remix_ui_terminal_filter border form-control"
id="searchInput" id="searchInput"
placeholder="Search with transaction hash or address" placeholder="Search with transaction hash or address"
data-id="terminalInputSearch" /> data-id="terminalInputSearch" />
</div> </div>
</div> </div>
</div> </div>
<div tabIndex={-1} className="terminal_container" data-id="terminalContainer" > <div tabIndex={-1} className="remix_ui_terminal_container d-flex h-100 m-0 flex-column" data-id="terminalContainer" >
{ {
handleAutoComplete() handleAutoComplete()
} }
<div data-id='terminalContainerDisplay' style = {{ <div className="position-relative d-flex flex-column-reverse h-100">
position: 'relative', <div id='journal' className='remix_ui_terminal_journal d-flex flex-column pt-3 pb-4 px-2 mx-2 mr-0' data-id='terminalJournal'>
height: '100%',
width: '100%',
opacity: '0.1',
zIndex: -1
}}></div>
<div className="terminal">
<div id='journal' className='journal' data-id='terminalJournal'>
{!clearConsole && <TerminalWelcomeMessage packageJson={version}/>} {!clearConsole && <TerminalWelcomeMessage packageJson={version}/>}
{newstate.journalBlocks && newstate.journalBlocks.map((x, index) => { {newstate.journalBlocks && newstate.journalBlocks.map((x, index) => {
if (x.name === EMPTY_BLOCK) { if (x.name === EMPTY_BLOCK) {
return ( return (
<div className="px-4 block" data-id='block' key={index}> <div className={classNameBlock} data-id='block' key={index}>
<span className='txLog'> <span className='remix_ui_terminal_tx'>
<span className='tx'><div className='txItem'>[<span className='txItemTitle'>block:{x.message} - </span> 0 {'transactions'} ] </div></span></span> <div className='remix_ui_terminal_txItem'>[<span className='remix_ui_terminal_txItemTitle'>block:{x.message} - </span> 0 {'transactions'} ]
</div>
</span>
</div> </div>
) )
} else if (x.name === UNKNOWN_TRANSACTION) { } else if (x.name === UNKNOWN_TRANSACTION) {
return x.message.filter(x => x.tx.hash.includes(searchInput) || x.tx.from.includes(searchInput) || (x.tx.to.includes(searchInput))).map((trans) => { return x.message.filter(x => x.tx.hash.includes(searchInput) || x.tx.from.includes(searchInput) || (x.tx.to.includes(searchInput))).map((trans) => {
return (<div className='px-4 block' data-id={`block_tx${trans.tx.hash}`} key={index}> { <RenderUnKnownTransactions tx={trans.tx} receipt={trans.receipt} index={index} plugin={props.plugin} showTableHash={showTableHash} txDetails={txDetails} modal={modal}/>} </div>) return (
<div className={classNameBlock} data-id={`block_tx${trans.tx.hash}`} key={index}> {
<RenderUnKnownTransactions
tx={trans.tx}
receipt={trans.receipt}
index={index} plugin={props.plugin}
showTableHash={showTableHash}
txDetails={txDetails}
modal={modal}
provider={x.provider}
/>}
</div>
)
}) })
} else if (x.name === KNOWN_TRANSACTION) { } else if (x.name === KNOWN_TRANSACTION) {
return x.message.map((trans) => { return x.message.map((trans) => {
return (<div className='px-4 block' data-id={`block_tx${trans.tx.hash}`} key={index}> { trans.tx.isCall ? <RenderCall tx={trans.tx} resolvedData={trans.resolvedData} logs={trans.logs} index={index} plugin={props.plugin} showTableHash={showTableHash} txDetails={txDetails} modal={modal}/> : (<RenderKnownTransactions tx = { trans.tx } receipt = { trans.receipt } resolvedData = { trans.resolvedData } logs = {trans.logs } index = { index } plugin = { props.plugin } showTableHash = { showTableHash } txDetails = { txDetails } modal={modal}/>) } </div>) return (
<div className={classNameBlock} data-id={`block_tx${trans.tx.hash}`} key={index}>
{ trans.tx.isCall ? <RenderCall
tx={trans.tx}
resolvedData={trans.resolvedData}
logs={trans.logs}
index={index}
plugin={props.plugin}
showTableHash={showTableHash}
txDetails={txDetails}
modal={modal}
/> : (<RenderKnownTransactions
tx = { trans.tx }
receipt = { trans.receipt }
resolvedData = { trans.resolvedData }
logs = {trans.logs }
index = { index }
plugin = { props.plugin }
showTableHash = { showTableHash }
txDetails = { txDetails }
modal={modal}
provider={x.provider}
/>) }
</div>
)
}) })
} else if (Array.isArray(x.message)) { } else if (Array.isArray(x.message)) {
return x.message.map((msg, i) => { return x.message.map((msg, i) => {
@ -489,28 +541,40 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
) )
} else if (typeof msg === 'object') { } else if (typeof msg === 'object') {
return ( return (
<div className="px-4 block" data-id="block" key={i}><span className={x.style}>{ msg.value ? parse(msg.value) : JSON.stringify(msg) } </span></div> <div className={classNameBlock} data-id="block" key={i}><span className={x.style}>{ msg.value ? parse(msg.value) : JSON.stringify(msg) } </span></div>
) )
} else { } else {
return ( return (
<div className="px-4 block" data-id="block" key={i}><span className={x.style}>{ msg ? msg.toString().replace(/,/g, '') : msg }</span></div> <div className={classNameBlock} data-id="block" key={i}><span className={x.style}>{ msg ? msg.toString().replace(/,/g, '') : msg }</span></div>
) )
} }
}) })
} else { } else {
if (typeof x.message !== 'function') { if (typeof x.message !== 'function') {
return ( return (
<div className="px-4 block" data-id="block" key={index}> <span className={x.style}> {x.message}</span></div> <div className={classNameBlock} data-id="block" key={index}> <span className={x.style}> {x.message}</span></div>
) )
} }
} }
})} })}
<div ref={messagesEndRef} /> <div ref={messagesEndRef} />
</div> </div>
<div id="terminalCli" data-id="terminalCli" className="cli" onClick={focusinput}> { isOpen && <div id="terminalCli" data-id="terminalCli" className="remix_ui_terminal_cli position-absolute w-100" onClick={focusinput}>
<span className="prompt">{'>'}</span> <span className="remix_ui_terminal_prompt blink mx-1 font-weight-bold text-dark">{'>'}</span>
<input className="input" ref={inputEl} spellCheck="false" contentEditable="true" id="terminalCliInput" data-id="terminalCliInput" onChange={(event) => onChange(event)} onKeyDown={(event) => handleKeyDown(event) } value={autoCompletState.userInput} onPaste={handlePaste}></input> <input
className="remix_ui_terminal_input ml-1 text-dark text-break border-0"
ref={inputEl}
spellCheck="false"
contentEditable="true"
id="terminalCliInput"
data-id="terminalCliInput"
onChange={(event) => onChange(event)}
onKeyDown={(event) => handleKeyDown(event) }
value={autoCompletState.userInput}
onPaste={handlePaste}
></input>
</div> </div>
}
</div> </div>
</div> </div>
<ModalDialog <ModalDialog

@ -2,10 +2,10 @@ import React from 'react' // eslint-disable-line
const TerminalWelcomeMessage = ({ packageJson }) => { const TerminalWelcomeMessage = ({ packageJson }) => {
return ( return (
<div className="px-4 block" data-id="block_null"> <div className="remix_ui_terminal_block px-4 " data-id="block_null">
<div> - Welcome to Remix {packageJson} - </div><br /> <div> - Welcome to Remix {packageJson} - </div><br />
<div>You can use this terminal to: </div> <div>You can use this terminal to: </div>
<ul className='ul'> <ul className='ml-0 mr-4'>
<li>Check transactions details and start debugging.</li> <li>Check transactions details and start debugging.</li>
<li>Execute JavaScript scripts: <li>Execute JavaScript scripts:
<br /> <br />
@ -17,7 +17,7 @@ const TerminalWelcomeMessage = ({ packageJson }) => {
</li> </li>
</ul> </ul>
<div>The following libraries are accessible:</div> <div>The following libraries are accessible:</div>
<ul className='ul'> <ul className='ml-0 mr-4'>
<li><a target="_blank" href="https://web3js.readthedocs.io/en/1.0/">web3 version 1.5.2</a></li> <li><a target="_blank" href="https://web3js.readthedocs.io/en/1.0/">web3 version 1.5.2</a></li>
<li><a target="_blank" href="https://docs.ethers.io">ethers.js</a> </li> <li><a target="_blank" href="https://docs.ethers.io">ethers.js</a> </li>
<li>remix (run remix.help() for more info)</li> <li>remix (run remix.help() for more info)</li>

@ -8,55 +8,6 @@ export interface RemixUiThemeModuleProps {
themeModule: ThemeModule; themeModule: ThemeModule;
} }
const defaultThemes = [
{
name: 'Dark',
quality: 'dark',
url: 'assets/css/themes/remix-dark_tvx1s2.css'
},
{
name: 'Light',
quality: 'light',
url: 'assets/css/themes/remix-light_powaqg.css'
},
{
name: 'Midcentury',
quality: 'light',
url: 'assets/css/themes/remix-midcentury_hrzph3.css'
},
{
name: 'Black',
quality: 'dark',
url: 'assets/css/themes/remix-black_undtds.css'
},
{
name: 'Candy',
quality: 'light',
url: 'assets/css/themes/remix-candy_ikhg4m.css'
},
{
name: 'Cerulean',
quality: 'light',
url: 'assets/css/themes/bootstrap-cerulean.min.css'
},
{
name: 'Flatly',
quality: 'light',
url: 'assets/css/themes/bootstrap-flatly.min.css'
},
{
name: 'Spacelab',
quality: 'light',
url: 'assets/css/themes/bootstrap-spacelab.min.css'
},
{
name: 'Cyborg',
quality: 'dark',
url: 'assets/css/themes/bootstrap-cyborg.min.css'
}
];
export function RemixUiThemeModule({ themeModule }: RemixUiThemeModuleProps) { export function RemixUiThemeModule({ themeModule }: RemixUiThemeModuleProps) {
const [themeName, setThemeName] = useState('') const [themeName, setThemeName] = useState('')
@ -85,7 +36,7 @@ export function RemixUiThemeModule({ themeModule }: RemixUiThemeModuleProps) {
name="theme" name="theme"
id={theme.name} id={theme.name}
data-id={`settingsTabTheme${theme.name}`} data-id={`settingsTabTheme${theme.name}`}
checked={themeModule.active === theme.name} checked={themeModule.active === theme.name.toLocaleLowerCase()}
/> />
<label <label
className="form-check-label custom-control-label" className="form-check-label custom-control-label"

@ -1,19 +1,19 @@
{ {
"env": { "env": {
"browser": true, "browser": true,
"es6": true "es6": true
}, },
"extends": ["../../../.eslintrc"], "ignorePatterns": ["!**/*"],
"extends": "../../../.eslintrc.json",
"globals": { "globals": {
"Atomics": "readonly", "Atomics": "readonly",
"SharedArrayBuffer": "readonly" "SharedArrayBuffer": "readonly"
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 11, "ecmaVersion": 11,
"sourceType": "module" "sourceType": "module"
}, },
"rules": { "rules": {
"no-unused-vars": "off", "standard/no-callback-literal": "off"
"@typescript-eslint/no-unused-vars": "error"
} }
} }

@ -1 +1,2 @@
export * from './lib/remix-ui-vertical-icons-panel' export { default as RemixUiVerticalIconsPanel } from './lib/remix-ui-vertical-icons-panel'
export { IconRecord } from './lib/types'

@ -1,10 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ import React from 'react'
// eslint-disable-next-line no-use-before-define import { BadgeStatus } from './Icon'
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
// eslint-disable-next-line no-use-before-define
import React, { Fragment, useEffect, useReducer } from 'react'
import { iconBadgeReducer, IconBadgeReducerAction } from '../reducers/iconBadgeReducer'
import { BadgeStatus, IconProfile, IconStatus } from './Icon'
interface BadgeProps { interface BadgeProps {
badgeStatus?: BadgeStatus badgeStatus?: BadgeStatus
@ -65,4 +60,4 @@ function Badge ({ badgeStatus }: BadgeProps) {
) )
} }
export default Badge export default Badge

@ -1,5 +1,3 @@
/* eslint-disable no-use-before-define */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import React from 'react' import React from 'react'
function BasicLogo () { function BasicLogo () {

@ -3,14 +3,23 @@ import React, { MutableRefObject } from 'react'
export interface ChevronProps { export interface ChevronProps {
divElementRef: MutableRefObject<any> divElementRef: MutableRefObject<any>
cssRule: string cssRule: string,
direction: string
} }
function Chevron (props: ChevronProps) { function Chevron (props: ChevronProps) {
const click = () => {
if (props.direction === 'down') {
props.divElementRef.current.scrollBy({ top: 40, behavior: 'smooth' })
} else {
props.divElementRef.current.scrollBy({ top: -40, behavior: 'smooth' })
}
}
return ( return (
<> <>
{ props.divElementRef.current && props.divElementRef.current.scrollHeight > props.divElementRef.current.clientHeight { props.divElementRef.current && props.divElementRef.current.scrollHeight > props.divElementRef.current.clientHeight
? <i className={props.cssRule}></i> : null } ? <i onClick={click} className={props.cssRule}></i> : null }
</> </>
) )
} }

@ -1,41 +0,0 @@
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
// eslint-disable-next-line no-use-before-define
import React, { Fragment } from 'react'
import Icon from './Icon'
interface DebuggerProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
}
function Debugger ({ verticalIconsPlugin, itemContextAction, addActive, removeActive }: DebuggerProps) {
return (
<Fragment>
{verticalIconsPlugin.targetProfileForChange &&
Object.keys(verticalIconsPlugin.targetProfileForChange).length
? Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(p => p === 'debugger')
.map(p => (
<div id="debuggingIcons" data-id="verticalIconsDebuggingIcons" key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}>
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}
/>
</div>
))
: null}
</Fragment>
)
}
export default Debugger

@ -1,59 +0,0 @@
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
// eslint-disable-next-line no-use-before-define
import React, { Fragment, useEffect, useRef } from 'react'
import Icon from './Icon'
interface FilePanelProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
}
function FilePanel ({ verticalIconsPlugin, itemContextAction, addActive, removeActive }: FilePanelProps) {
const filePanelRef = useRef(null)
function onThemeChanged (themeType: any) {
const invert = themeType === 'dark' ? 1 : 0
// @ts-ignore
const active = filePanelRef.current && filePanelRef.current.querySelector('.active')
if (active) {
// @ts-ignore
const image = filePanelRef.current.querySelector('.remixui_image')
image.style.setProperty('filter', `invert(${invert})`)
}
}
useEffect(() => {
const themeModule = verticalIconsPlugin.registry.get('themeModule').api
themeModule.events.on('themeChanged', (theme: any) => {
onThemeChanged(theme.quality)
})
}, [])
return (
<Fragment>
{verticalIconsPlugin.targetProfileForChange &&
Object.keys(verticalIconsPlugin.targetProfileForChange).length
? Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(p => p === 'filePanel')
.map(p => (
<div id="fileExplorerIcons" key={
verticalIconsPlugin.targetProfileForChange[p].displayName
} data-id="verticalIconsFileExplorerIcons"
ref={filePanelRef}
>
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
/>
</div>
))
: null}
</Fragment>
)
}
export default FilePanel

@ -1,19 +1,15 @@
/* eslint-disable no-use-before-define */ import React from 'react'
/* eslint-disable @typescript-eslint/no-unused-vars */
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
import React, { ReactNode } from 'react'
import BasicLogo from './BasicLogo' import BasicLogo from './BasicLogo'
interface HomeProps { interface HomeProps {
verticalIconPlugin: VerticalIcons verticalIconPlugin: any
} }
function Home ({ verticalIconPlugin }: HomeProps) { function Home ({ verticalIconPlugin }: HomeProps) {
return ( return (
<div <div
className="pl-1 mt-2 remixui_homeIcon" className="mt-2 my-1 remixui_homeIcon"
onClick={async () => verticalIconPlugin.activateHome()} onClick={async () => await verticalIconPlugin.activateHome()}
// @ts-ignore {...{ plugin: 'home'}}
plugin="home"
title="Home" title="Home"
data-id="verticalIconsHomeIcon" data-id="verticalIconsHomeIcon"
id="verticalIconsHomeIcon" id="verticalIconsHomeIcon"

@ -1,9 +1,10 @@
import VerticalIconsContextMenu from '../vertical-icons-context-menu' import VerticalIconsContextMenu from '../vertical-icons-context-menu'
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, { Fragment, SyntheticEvent, useEffect, useReducer, useRef, useState } from 'react' import React, { Fragment, SyntheticEvent, useEffect, useReducer, useRef, useState } from 'react'
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
import Badge from './Badge' import Badge from './Badge'
import { iconBadgeReducer, IconBadgeReducerAction } from '../reducers/iconBadgeReducer' import { iconBadgeReducer, IconBadgeReducerAction } from '../reducers/iconBadgeReducer'
import { Plugin } from '@remixproject/engine'
import { IconRecord } from '../types'
export interface IconStatus { export interface IconStatus {
key: string key: string
@ -16,26 +17,11 @@ export interface BadgeStatus extends IconStatus {
text: string text: string
} }
export interface IconProfile {
description: string
displayName: string
documentation: string
events: any[]
icon: string
kind: string
location: string
methods: string[]
name: string
version: string
tooltip?: string
}
interface IconProps { interface IconProps {
verticalIconPlugin: VerticalIcons verticalIconPlugin: Plugin
profile: IconProfile iconRecord: IconRecord
contextMenuAction: (evt: any, profileName: string, documentation: string) => void contextMenuAction: (evt: any, profileName: string, documentation: string) => void
addActive: (profileName: string) => void theme: string
removeActive: () => void
} }
const initialState = { const initialState = {
@ -46,18 +32,15 @@ const initialState = {
pluginName: '' pluginName: ''
} }
function Icon ({ const Icon = ({
profile, iconRecord,
verticalIconPlugin, verticalIconPlugin,
contextMenuAction, contextMenuAction,
addActive, theme
removeActive }: IconProps) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars const { displayName, name, icon, documentation } = iconRecord.profile
}: IconProps) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { tooltip, displayName, name, kind, icon, documentation } = profile
const [title] = useState(() => { const [title] = useState(() => {
const temp = tooltip || displayName || name const temp = null || displayName || name
return temp.replace(/^\w/, (word: string) => word.toUpperCase()) return temp.replace(/^\w/, (word: string) => word.toUpperCase())
}) })
const [links, setLinks] = useState<{ Documentation: string, CanDeactivate: boolean }>( const [links, setLinks] = useState<{ Documentation: string, CanDeactivate: boolean }>(
@ -71,11 +54,9 @@ function Icon ({
const [showContext, setShowContext] = useState(false) const [showContext, setShowContext] = useState(false)
const [canDeactivate] = useState(false) const [canDeactivate] = useState(false)
const iconRef = useRef<any>(null) const iconRef = useRef<any>(null)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const handleContextMenu = (e: SyntheticEvent & PointerEvent) => { const handleContextMenu = (e: SyntheticEvent & PointerEvent) => {
const deactivationState = verticalIconPlugin.appManager const deactivationState = iconRecord.canbeDeactivated
.canDeactivatePlugin(verticalIconPlugin.defaultProfile, { name })
if (documentation && documentation.length > 0 && deactivationState) { if (documentation && documentation.length > 0 && deactivationState) {
setLinks({ Documentation: documentation, CanDeactivate: deactivationState }) setLinks({ Documentation: documentation, CanDeactivate: deactivationState })
} else { } else {
@ -102,21 +83,13 @@ function Icon ({
}, []) }, [])
return ( return (
<Fragment> <>
<div <div
className={name === 'pluginManager' ? 'remixui_icon ml-2 mt-2 mr-2 mb-0 pl-1' : 'remixui_icon m-2 pl-1'} className={`remixui_icon m-2 pt-1`}
onLoad={() => {
if (name === 'filePanel') {
addActive(name)
}
}}
onClick={() => { onClick={() => {
removeActive() (verticalIconPlugin as any).toggle(name)
addActive(name)
verticalIconPlugin.toggle(name)
}} }}
// @ts-ignore {...{plugin: name}}
plugin={name}
title={title} title={title}
onContextMenu={(e: any) => { onContextMenu={(e: any) => {
e.preventDefault() e.preventDefault()
@ -127,11 +100,10 @@ function Icon ({
id={`verticalIconsKind${name}`} id={`verticalIconsKind${name}`}
ref={iconRef} ref={iconRef}
> >
<img className="remixui_image" src={icon} alt={name} /> <img className={`${theme === 'dark' ? 'invert' : ''} ${theme} remixui_image ${iconRecord.active ? `selected-${theme}`:''}`} src={icon} alt={name} />
{ badgeStatus && badgeStatus.pluginName === name ? (
<Badge <Badge
badgeStatus={badgeStatus} badgeStatus={badgeStatus}
/>) : null } />
</div> </div>
{showContext ? ( {showContext ? (
<VerticalIconsContextMenu <VerticalIconsContextMenu
@ -145,7 +117,7 @@ function Icon ({
contextMenuAction={contextMenuAction} contextMenuAction={contextMenuAction}
/> />
) : null} ) : null}
</Fragment> </>
) )
} }

@ -0,0 +1,33 @@
/* eslint-disable no-use-before-define */
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useEffect } from 'react'
import { IconRecord } from '../types'
import Icon from './Icon'
interface OtherIconsProps {
verticalIconsPlugin: any
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
icons: IconRecord[]
theme: string
}
function IconList ({ verticalIconsPlugin, itemContextAction, icons, theme }: OtherIconsProps) {
return (
<div id="otherIcons">
{
icons
.map(p => (
<Icon
theme={theme}
iconRecord={p}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
key={
p.profile.name
}
/>
))}
</div>
)
}
export default IconList

@ -1,43 +0,0 @@
/* eslint-disable no-use-before-define */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
import React from 'react'
import Icon from './Icon'
function customFilter (p: string) {
if (p !== 'settings' && p !== 'pluginManager' &&
p !== 'filePanel' && p !== 'debugger' &&
p !== 'compiler' && p !== 'solidity' &&
p !== 'udapp' && p !== 'testing' && p !== 'solidityStaticAnalysis') return true
return false
}
interface OtherIconsProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
}
function OtherIcons ({ verticalIconsPlugin, itemContextAction, addActive, removeActive }: OtherIconsProps) {
return (
<div id="otherIcons">
{
Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(customFilter)
.map(p => (
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}
/>
))}
</div>
)
}
export default OtherIcons

@ -1,36 +0,0 @@
/* eslint-disable no-use-before-define */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
import React, { Fragment } from 'react'
import Icon from './Icon'
interface PluginManagerProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
}
function PluginManager ({ verticalIconsPlugin, itemContextAction, addActive, removeActive }: PluginManagerProps) {
return (
<Fragment>
{Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(p => p === 'pluginManager')
.map(p => (
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
key={
verticalIconsPlugin.targetProfileForChange[p]
.displayName
}
/>
))}
</Fragment>
)
}
export default PluginManager

@ -1,72 +0,0 @@
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
// eslint-disable-next-line no-use-before-define
import React, { Fragment, MutableRefObject } from 'react'
import { Chevron } from './Chevron'
import Debugger from './Debugger'
import FilePanel from './FilePanel'
import PluginManager from './PluginManager'
import Solidity from './Solidity'
import SolidityStaticAnalysis from './SolidityStaticAnalysis'
import Udapp from './Udapp'
interface RequiredSectionProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
scrollableRef: MutableRefObject<any>
}
function RequiredSection ({ verticalIconsPlugin, itemContextAction, addActive, removeActive, scrollableRef }: RequiredSectionProps) {
return (
<Fragment>
<FilePanel
verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction}
/>
<PluginManager
verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction}
/>
<Solidity
verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction}
/>
<Udapp
verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction}
/>
<SolidityStaticAnalysis
verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction}
/>
<Debugger
verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction}
/>
{
scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight
? (
<Chevron
divElementRef={scrollableRef}
cssRule={'fa fa-chevron-up remixui_icon-chevron mt-0 mb-0 ml-1 pl-3'}
/>
) : null
}
</Fragment>
)
}
export { RequiredSection }

@ -1,60 +0,0 @@
/* eslint-disable no-use-before-define */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
import React, { useEffect, useRef } from 'react'
import { Chevron } from './Chevron'
import Icon from './Icon'
interface SettingsProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
scrollableRef: React.MutableRefObject<any>
}
function Settings ({ scrollableRef, verticalIconsPlugin, itemContextAction, addActive, removeActive }: SettingsProps) {
const settingsRef = useRef(null)
function onThemeChanged (themeType: any) {
const invert = themeType === 'dark' ? 1 : 0
// @ts-ignore
const active = settingsRef.current.querySelector('.active')
if (active) {
// @ts-ignore
const image = settingsRef.current.querySelector('.remixui_image')
image.style.setProperty('filter', `invert(${invert})`)
}
}
useEffect(() => {
const themeModule = verticalIconsPlugin.registry.get('themeModule').api
themeModule.events.on('themeChanged', (theme: any) => {
onThemeChanged(theme.quality)
})
}, [])
return (
<div id="settingsIcons" className="remixui_settings mb-0 flex-grow-0" data-id="vertialIconsSettingsIcons" ref={settingsRef}>
{ scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? (<Chevron
divElementRef={scrollableRef}
cssRule={'fa fa-chevron-down remixui_icon-chevron mt-0 mb-0 ml-1 pl-3'}
/>) : null }
{Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(p => p === 'settings')
.map(p => (
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
key={
verticalIconsPlugin.targetProfileForChange[p]
.displayName
}
/>
))}
</div>
)
}
export default Settings

@ -1,41 +0,0 @@
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
// eslint-disable-next-line no-use-before-define
import React, { Fragment } from 'react'
import Icon from './Icon'
interface SolidityProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
}
function Solidity ({ verticalIconsPlugin, itemContextAction, addActive, removeActive }: SolidityProps) {
return (
<Fragment>
{verticalIconsPlugin.targetProfileForChange &&
Object.keys(verticalIconsPlugin.targetProfileForChange).length
? Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(p => p === 'solidity')
.map(p => (
<div id="compileIcons" key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}>
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}
/>
</div>
))
: null}
</Fragment>
)
}
export default Solidity

@ -1,41 +0,0 @@
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
// eslint-disable-next-line no-use-before-define
import React, { Fragment } from 'react'
import Icon from './Icon'
interface SolidityStaticAnalysisProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
}
function SolidityStaticAnalysis ({ verticalIconsPlugin, itemContextAction, addActive, removeActive }: SolidityStaticAnalysisProps) {
return (
<Fragment>
{verticalIconsPlugin.targetProfileForChange &&
Object.keys(verticalIconsPlugin.targetProfileForChange).length
? Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(p => p === 'solidityStaticAnalysis')
.map(p => (
<div id="analysisIcons" className="remixui_iconContainer" key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}>
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}
/>
</div>
))
: null}
</Fragment>
)
}
export default SolidityStaticAnalysis

@ -1,42 +0,0 @@
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
// eslint-disable-next-line no-use-before-define
import React, { Fragment } from 'react'
import Icon from './Icon'
interface UdappProps {
verticalIconsPlugin: VerticalIcons
itemContextAction: (e: any, name: string, documentation: string) => Promise<void>
addActive: (name: string) => void
removeActive: () => void
}
function Udapp ({ verticalIconsPlugin, itemContextAction, addActive, removeActive }: UdappProps) {
return (
<Fragment>
{verticalIconsPlugin.targetProfileForChange &&
Object.keys(verticalIconsPlugin.targetProfileForChange).length
? Object.keys(verticalIconsPlugin.targetProfileForChange)
.filter(p => p === 'udapp')
.map(p => (
<div id="runIcons" data-id="verticalIconsKindUdapp" key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}
>
<Icon
profile={verticalIconsPlugin.targetProfileForChange[p]}
verticalIconPlugin={verticalIconsPlugin}
contextMenuAction={itemContextAction}
addActive={addActive}
removeActive={removeActive}
key={
verticalIconsPlugin.targetProfileForChange[p].displayName
}
/>
</div>
))
: null}
</Fragment>
)
}
export default Udapp

@ -1,8 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ import { checkSpecialChars } from '@remix-ui/helper'
import helper from 'apps/remix-ide/src/lib/helper'
import { BadgeStatus, IconStatus } from '../components/Icon' import { BadgeStatus, IconStatus } from '../components/Icon'
import React, { MutableRefObject } from 'react'
import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel'
export type IconBadgeReducerAction = { export type IconBadgeReducerAction = {
readonly type: string readonly type: string
@ -14,32 +11,30 @@ export type IconBadgeReducerAction = {
* @param {String} name * @param {String} name
* @param {Object} status * @param {Object} status
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function setIconStatus (name: string, status: IconStatus) { function setIconStatus(name: string, status: IconStatus) {
if (status.key === 'none') return { ...status, text: '' } // remove status if (status.key === 'none') return { ...status, text: '' } // remove status
let text = '' let text = ''
let key = '' let key = ''
if (typeof status.key === 'number') { if (typeof status.key === 'number') {
key = status.key key = status.key
// eslint-disable-next-line @typescript-eslint/no-unused-vars
text = key text = key
} else key = helper.checkSpecialChars(status.key) ? '' : status.key } else key = checkSpecialChars(status.key) ? '' : status.key
let thisType = '' let thisType = ''
if (status.type === 'error') { if (status.type === 'error') {
thisType = 'danger' // to use with bootstrap thisType = 'danger' // to use with bootstrap
} else thisType = helper.checkSpecialChars(status.type) ? '' : status.type! } else thisType = checkSpecialChars(status.type) ? '' : status.type!
const title = helper.checkSpecialChars(status.title) ? '' : status.title const title = checkSpecialChars(status.title) ? '' : status.title
const pluginName = status.pluginName const pluginName = status.pluginName
return { title, type: thisType, key, text, pluginName } return { title, type: thisType, key, text, pluginName }
} }
export function iconBadgeReducer (state: BadgeStatus, action: IconBadgeReducerAction) { export function iconBadgeReducer(state: BadgeStatus, action: IconBadgeReducerAction) {
const { status, ref, verticalIconPlugin } = action.payload const { status } = action.payload
if (Object.keys(verticalIconPlugin.targetProfileForChange).includes(action.type)) {
const setStatus = setIconStatus(action.type, status) const setStatus = setIconStatus(action.type, status)
return setStatus return setStatus
}
return state
} }

@ -6,7 +6,8 @@ export type actionType = {
export function verticalScrollReducer (prevState: any, actionPayload: actionType) { export function verticalScrollReducer (prevState: any, actionPayload: actionType) {
if (actionPayload.type === 'resize') { if (actionPayload.type === 'resize') {
let { scrollHeight, clientHeight, scrollState } = actionPayload.payload const { scrollHeight, clientHeight } = actionPayload.payload
let { scrollState } = actionPayload.payload
if (scrollHeight > clientHeight) scrollState = true if (scrollHeight > clientHeight) scrollState = true
return { scrollHeight, clientHeight, scrollState } return { scrollHeight, clientHeight, scrollState }
} }

@ -4,6 +4,10 @@
height: 42px; height: 42px;
cursor: pointer; cursor: pointer;
} }
.remixui_homeIcon:hover {
box-shadow: 0px 0px 14px -7px;
}
.remixui_homeIcon svg path { .remixui_homeIcon svg path {
fill: var(--primary); fill: var(--primary);
} }
@ -16,14 +20,16 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.remixui_icon:hover {
box-shadow: 0px 0px 14px -7px;
}
.remixui_icon { .remixui_icon {
cursor: pointer; cursor: pointer;
margin-bottom: 3px;
position: 12px;
width: 36px; width: 36px;
height: 36px; height: 36px;
padding: relative;
border-radius: 8px; border-radius: 8px;
align-items: center;
} }
.remixui_icon img { .remixui_icon img {
width: 28px; width: 28px;
@ -31,8 +37,15 @@
padding: 4px; padding: 4px;
filter: invert(0.5); filter: invert(0.5);
} }
.remixui_image {
.remixui_icon .selected-dark {
filter: invert(1) grayscale(1);
} }
.remixui_icon .selected-light {
filter: invert(0) grayscale(1);
}
.remixui_icon svg { .remixui_icon svg {
width: 28px; width: 28px;
height: 28px; height: 28px;
@ -104,9 +117,12 @@
} }
.remixui_default-icons-container { .remixui_default-icons-container {
border-bottom: 2px solid #3f4455; border-bottom: 2px solid #3f4455;
text-align: center;
} }
.remixui_icon-chevron { .remixui_icon-chevron {
z-index: 1000; z-index: 1000;
cursor: pointer;
align-items: center;
} }
.remixui_settings { .remixui_settings {
@ -116,4 +132,4 @@
#menuitems { #menuitems {
list-style: none; list-style: none;
margin: 0px; margin: 0px;
} }

@ -1,5 +1,3 @@
/* eslint-disable no-use-before-define */
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { import React, {
Fragment, Fragment,
useEffect, useEffect,
@ -7,16 +5,16 @@ import React, {
useRef, useRef,
useState useState
} from 'react' } from 'react'
import { Plugin } from '@remixproject/engine'
import './remix-ui-vertical-icons-panel.css' import './remix-ui-vertical-icons-panel.css'
import OtherIcons from './components/OtherIcons' import IconList from './components/IconList'
import { VerticalIcons } from '../../types/vertical-icons-panel'
import Home from './components/Home' import Home from './components/Home'
import Settings from './components/Settings'
import { RequiredSection } from './components/RequiredSection'
import { verticalScrollReducer } from './reducers/verticalScrollReducer' import { verticalScrollReducer } from './reducers/verticalScrollReducer'
import { Chevron } from './components/Chevron'
import { IconRecord } from './types'
export interface RemixUiVerticalIconsPanelProps { export interface RemixUiVerticalIconsPanelProps {
verticalIconsPlugin: VerticalIcons verticalIconsPlugin: Plugin
icons: IconRecord[]
} }
const initialState = { const initialState = {
@ -25,95 +23,54 @@ const initialState = {
scrollState: false scrollState: false
} }
export function RemixUiVerticalIconsPanel ({ const RemixUiVerticalIconsPanel = ({
verticalIconsPlugin verticalIconsPlugin, icons
}: RemixUiVerticalIconsPanelProps) { }: RemixUiVerticalIconsPanelProps) => {
const scrollableRef = useRef<any>() const scrollableRef = useRef<any>()
const iconPanelRef = useRef<any>() const iconPanelRef = useRef<any>()
const [activateScroll, dispatchScrollAction] = useReducer(verticalScrollReducer, initialState) const [activateScroll, dispatchScrollAction] = useReducer(verticalScrollReducer, initialState)
const [theme, setTheme] = useState<string>('dark')
useEffect(() => {
const evaluateScrollability = (evt: any) => {
dispatchScrollAction({
type: 'resize',
payload: {
scrollHeight: document.querySelector('#remixuiScrollable')?.scrollHeight,
clientHeight: document.querySelector('#remixuiScrollable')?.clientHeight,
scrollState: false
}
})
}
addEventListener('resize', evaluateScrollability)
return () => {
removeEventListener('resize', evaluateScrollability)
}
})
function onThemeChanged (themeType: any) {
const invert = themeType === 'dark' ? 1 : 0
// @ts-ignore
const active = iconPanelRef.current.querySelector('.active')
if (active) {
// @ts-ignore
const image = iconPanelRef.current.querySelector('.remixui_image')
image.style.setProperty('filter', `invert(${invert})`)
}
}
function removeActive () { const evaluateScrollability = () => {
// @ts-ignore dispatchScrollAction({
const images = iconPanelRef.current.querySelectorAll('.remixui_image') type: 'resize',
images.forEach(function (im: any) { payload: {
im.style.setProperty('filter', 'invert(0.5)') scrollHeight: scrollableRef.current?.scrollHeight,
clientHeight: scrollableRef.current?.clientHeight,
scrollState: false
}
}) })
// remove active
// @ts-ignore
const currentActive = iconPanelRef.current.querySelector('.active')
if (currentActive) {
currentActive.classList.remove('active')
}
} }
function addActive (name: string) { useEffect(() => {
if (name === 'home') return window.addEventListener('resize', evaluateScrollability)
const themeType = verticalIconsPlugin.registry.get('themeModule').api.currentTheme().quality evaluateScrollability()
const invert = themeType === 'dark' ? 1 : 0 return () => {
const brightness = themeType === 'dark' ? '150' : '0' // should be >100 for icons with color window.removeEventListener('resize', evaluateScrollability)
// @ts-ignore
const nextActive = iconPanelRef.current.querySelector(`[plugin="${name}"]`)
if (nextActive) {
const image = nextActive.querySelector('.remixui_image')
nextActive.classList.add('active')
image.style.setProperty('filter', `invert(${invert}) grayscale(1) brightness(${brightness}%)`)
} }
} }, [])
async function itemContextAction (e: any, name: string, documentation: string) { useEffect(() => {
verticalIconsPlugin.appManager.deactivatePlugin(name) evaluateScrollability()
if (e.target.parentElement.classList.contains('active')) { },[icons, theme])
verticalIconsPlugin.select('filePanel')
}
verticalIconsPlugin.renderComponent()
}
useEffect(() => { useEffect(() => {
const themeModule = verticalIconsPlugin.registry.get('themeModule').api verticalIconsPlugin.call('theme', 'currentTheme').then((th: any) => {
themeModule.events.on('themeChanged', (theme: any) => { setTheme(th.quality)
onThemeChanged(theme.quality) })
verticalIconsPlugin.on('theme', 'themeChanged', (th: any) => {
setTheme(th.quality)
}) })
return () => { return () => {
themeModule.events.off('themeChanged') verticalIconsPlugin.off('theme', 'themeChanged')
} }
}, []) }, [])
useEffect(() => { async function itemContextAction (e: any, name: string, documentation: string) {
if (verticalIconsPlugin.targetProfileForChange && verticalIconsPlugin.targetProfileForChange.udapp) { verticalIconsPlugin.call('manager', 'deactivatePlugin', name)
const doWalkThroughEvent = new Event('doWalkThrough') }
document.dispatchEvent(doWalkThroughEvent)
}
}, [Object.keys(verticalIconsPlugin.targetProfileForChange).length])
return ( return (
<div id="iconsP" className="h-100"> <div id="iconsP" className="h-100">
@ -121,13 +78,22 @@ export function RemixUiVerticalIconsPanel ({
<Home verticalIconPlugin={verticalIconsPlugin} /> <Home verticalIconPlugin={verticalIconsPlugin} />
<div className={scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight <div className={scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight
? 'remixui_default-icons-container remixui_requiredSection' : activateScroll && activateScroll.scrollState ? 'remixui_default-icons-container remixui_requiredSection' : 'remixui_requiredSection'}> ? 'remixui_default-icons-container remixui_requiredSection' : activateScroll && activateScroll.scrollState ? 'remixui_default-icons-container remixui_requiredSection' : 'remixui_requiredSection'}>
<RequiredSection <IconList
theme={theme}
icons={icons.filter((p) => p.isRequired && p.profile.name !== 'pluginManager')}
verticalIconsPlugin={verticalIconsPlugin} verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction} itemContextAction={itemContextAction}
scrollableRef={scrollableRef}
/> />
{
scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight
? (
<Chevron
direction='up'
divElementRef={scrollableRef}
cssRule={'fa fa-chevron-up remixui_icon-chevron my-0'}
/>
) : null
}
</div> </div>
<div <div
id="remixuiScrollable" id="remixuiScrollable"
@ -136,26 +102,29 @@ export function RemixUiVerticalIconsPanel ({
: activateScroll && activateScroll.scrollState ? 'remixui_default-icons-container remixui_scrollable-container remixui_scrollbar remixui_hide-scroll' : 'remixui_scrollable-container remixui_scrollbar remixui_hide-scroll'} : activateScroll && activateScroll.scrollState ? 'remixui_default-icons-container remixui_scrollable-container remixui_scrollbar remixui_hide-scroll' : 'remixui_scrollable-container remixui_scrollbar remixui_hide-scroll'}
ref={scrollableRef} ref={scrollableRef}
> >
<OtherIcons <IconList
theme={theme}
icons={icons.filter((p) => {return !p.isRequired && p.profile.name !== 'settings'})}
verticalIconsPlugin={verticalIconsPlugin}
itemContextAction={itemContextAction}
/>
</div>
<div className="remixui_default-icons-container border-0">
{ scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? (<Chevron
divElementRef={scrollableRef}
direction='down'
cssRule={'fa fa-chevron-down remixui_icon-chevron my-0'}
/>) : null }
<IconList
theme={theme}
icons={icons.filter((p) => p.profile.name === 'settings' || p.profile.name === 'pluginManager')}
verticalIconsPlugin={verticalIconsPlugin} verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction} itemContextAction={itemContextAction}
/> />
</div> </div>
{verticalIconsPlugin.targetProfileForChange &&
Object.keys(verticalIconsPlugin.targetProfileForChange).length ? (
<Fragment>
<Settings
verticalIconsPlugin={verticalIconsPlugin}
addActive={addActive}
removeActive={removeActive}
itemContextAction={itemContextAction}
scrollableRef={scrollableRef}
/>
</Fragment>
) : null}
</div> </div>
</div> </div>
) )
} }
export default RemixUiVerticalIconsPanel

@ -0,0 +1,10 @@
import { Profile } from '@remixproject/plugin-utils'
export type IconRecord = {
profile: Profile
active: boolean
class?: string
canbeDeactivated?: boolean
isRequired?: boolean
timestamp: number
}

@ -1,9 +1,5 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */ import { Plugin } from '@remixproject/engine'
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-use-before-define */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import React, { Fragment, useEffect, useRef } from 'react' import React, { Fragment, useEffect, useRef } from 'react'
import { VerticalIcons } from '../../types/vertical-icons-panel'
export interface VerticalIconsContextMenuProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> { export interface VerticalIconsContextMenuProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
pageX: number pageX: number
@ -11,7 +7,7 @@ export interface VerticalIconsContextMenuProps extends React.DetailedHTMLProps<R
profileName: string profileName: string
links: { Documentation: string, CanDeactivate: boolean } links: { Documentation: string, CanDeactivate: boolean }
canBeDeactivated: boolean canBeDeactivated: boolean
verticalIconPlugin: VerticalIcons verticalIconPlugin: any
hideContextMenu: () => void hideContextMenu: () => void
contextMenuAction: (evt: any, profileName: string, documentation: string) => void contextMenuAction: (evt: any, profileName: string, documentation: string) => void
} }
@ -21,31 +17,22 @@ interface MenuLinksProps {
hide: () => void hide: () => void
profileName: string profileName: string
canBeDeactivated: boolean canBeDeactivated: boolean
verticalIconPlugin: VerticalIcons verticalIconPlugin: any
ref?: React.MutableRefObject<any> ref?: React.MutableRefObject<any>
toggle: (name: string) => void toggle: (name: string) => void
contextMenuAction: (evt: any, profileName: string, documentation: string) => void contextMenuAction: (evt: any, profileName: string, documentation: string) => void
} }
interface MenuProps { interface MenuProps {
verticalIconsPlugin: VerticalIcons verticalIconsPlugin: Plugin
profileName: string profileName: string
listItems: { Documentation: string, CanDeactivate: boolean } listItems: { Documentation: string, CanDeactivate: boolean }
hide: () => void hide: () => void
} }
const requiredModules = [ const VerticalIconsContextMenu = (props: VerticalIconsContextMenuProps) =>{
'manager', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme',
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic']
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider']
function VerticalIconsContextMenu (props: VerticalIconsContextMenuProps) {
const menuRef = useRef(null) const menuRef = useRef(null)
useEffect(() => { ClickOutside(menuRef, props.hideContextMenu)
document.addEventListener('click', props.hideContextMenu)
return () => document.removeEventListener('click', props.hideContextMenu)
}, [])
useEffect(() => { useEffect(() => {
// @ts-ignore // @ts-ignore
menuRef.current.focus() menuRef.current.focus()
@ -54,7 +41,6 @@ function VerticalIconsContextMenu (props: VerticalIconsContextMenuProps) {
<div <div
id="menuItemsContainer" id="menuItemsContainer"
className="p-1 remixui_verticalIconContextcontainer bg-light shadow border" className="p-1 remixui_verticalIconContextcontainer bg-light shadow border"
onBlur={props.hideContextMenu}
style={{ style={{
left: props.pageX, left: props.pageX,
top: props.pageY, top: props.pageY,
@ -80,15 +66,15 @@ function VerticalIconsContextMenu (props: VerticalIconsContextMenuProps) {
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
function MenuForLinks ({ const MenuForLinks = ({
listItems, listItems,
hide, hide,
profileName, profileName,
contextMenuAction contextMenuAction
}: MenuLinksProps) { }: MenuLinksProps) => {
return ( return (
<Fragment> <Fragment>
{listItems.CanDeactivate && !requiredModules.includes(profileName) {listItems.CanDeactivate
? <li ? <li
id="menuitemdeactivate" id="menuitemdeactivate"
onClick={(evt) => { onClick={(evt) => {
@ -118,4 +104,18 @@ function MenuForLinks ({
) )
} }
function ClickOutside(ref: React.MutableRefObject<HTMLElement>, hideFn: () => void) {
useEffect(() => {
function handleClickOutside(event: any) {
if (ref.current && !ref.current.contains(event.target)) {
hideFn()
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
}
export default VerticalIconsContextMenu export default VerticalIconsContextMenu

@ -1,21 +1,19 @@
{ {
"extends": "../../../tsconfig.base.json", "extends": "../../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"jsx": "react-jsx", "jsx": "react",
"allowJs": true, "allowJs": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true
}, },
"files": [], "files": [],
"include": [], "include": [],
"references": [ "references": [
{ {
"path": "./tsconfig.lib.json" "path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
} }
] ]
} }

@ -2,8 +2,7 @@
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "../../../dist/out-tsc", "outDir": "../../../dist/out-tsc",
"types": ["node"], "types": ["node"]
"resolveJsonModule": true
}, },
"files": [ "files": [
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",

@ -1,111 +0,0 @@
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable no-use-before-define */
import { Plugin } from '@remixproject/engine/lib/abstract'
import * as packageJson from '../../../../package.json'
import Registry from 'apps/remix-ide/src/app/state/registry'
import { RemixAppManager } from '@remix-ui/plugin-manager'
export type Kind =
| 'fileexplorer'
| 'compiler'
| 'udapp'
| 'testing'
| 'analysis'
| 'debugging'
| 'settings'
| 'none'
type IconKindType = {
kind: {}
}
interface defaultModuleProfile {
name: string
displayName: string
description: string
version: packageJson.version
methods: string[]
}
interface PassedProfile {
name: string
displayName: string
description: string
version: packageJson.version
methods: string[]
icon?: string
tooltip?: string
kind?: string
documentation?: string
}
interface targetProfileIcons {
profile: PassedProfile
}
export class VerticalIcons extends Plugin<any, any> {
events: EventEmitter
appManager: RemixAppManager
htmlElement: HTMLDivElement
icons: any
iconKind: {}
iconStatus: {}
defaultProfile: defaultModuleProfile
targetProfileForChange: any
targetProfileForRemoval: any
registry: Registry
keys: string[]
types: string[]
renderComponent(): void
linkContent(profile: any): void
unlinkContent(profile: any): void
listenOnStatus(profile: any): void
activateHome(): void
/**
* Add an icon to the map
* @param {ModuleProfile} profile The profile of the module
*/
addIcon({ kind, name, icon, displayName, tooltip, documentation }: any): void
/**
* resolve a classes list for @arg key
* @param {Object} key
* @param {Object} type
*/
resolveClasses(key: any, type: any): any
/**
* Set a new status for the @arg name
* @param {String} name
* @param {Object} status
*/
setIconStatus(name: string, status: any): void
/**
* Remove an icon from the map
* @param {ModuleProfile} profile The profile of the module
*/
removeIcon({ name }: any): void
/**
* Remove active for the current activated icons
*/
removeActive(): void
/**
* Add active for the new activated icon
* @param {string} name Name of profile of the module to activate
*/
addActive(name: string): void
/**
* Set an icon as active
* @param {string} name Name of profile of the module to activate
*/
select(name: string): void
/**
* Toggles the side panel for plugin
* @param {string} name Name of profile of the module to activate
*/
toggle(name: string): void
updateActivations(name: any): void
onThemeChanged(themeType: any): void
itemContextMenu(e: any, name: any, documentation: any): Promise<void>
render(): any
view: any
}
import EventEmitter = require('events')

@ -117,7 +117,7 @@ export class RemixURLResolver {
// If you don't find greeter.sol on ipfs gateway use local // If you don't find greeter.sol on ipfs gateway use local
// const req = 'http://localhost:8080/' + url // const req = 'http://localhost:8080/' + url
const response: AxiosResponse = await axios.get(req) const response: AxiosResponse = await axios.get(req)
return { content: response.data, cleanUrl: url } return { content: response.data, cleanUrl: url.replace('ipfs/', '') }
} catch (e) { } catch (e) {
throw e throw e
} }

@ -260,7 +260,7 @@ describe('testRunner', () => {
const content = fs.readFileSync(__dirname + '/example_1/greeter.sol', { encoding: 'utf8'}) const content = fs.readFileSync(__dirname + '/example_1/greeter.sol', { encoding: 'utf8'})
const expt: object = { const expt: object = {
content: content, content: content,
cleanUrl: 'ipfs/QmcuCKyokk9Z6f65ADAADNiS2R2xCjfRkv7mYBSWDwtA7M', cleanUrl: 'QmcuCKyokk9Z6f65ADAADNiS2R2xCjfRkv7mYBSWDwtA7M',
type: 'ipfs' type: 'ipfs'
} }
assert.deepEqual(results, expt) assert.deepEqual(results, expt)

Loading…
Cancel
Save