Merge branch 'master' into loadingImprove

pull/5370/head
David Disu 3 years ago committed by GitHub
commit 6fcb8a3492
  1. 15
      apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts
  2. 211
      apps/remix-ide-e2e/src/tests/editor.test.ts
  3. 12
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  4. 107
      apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
  5. 3
      apps/remix-ide-e2e/src/tests/url.spec.ts
  6. 1
      apps/remix-ide-e2e/src/types/index.d.ts
  7. 2
      apps/remix-ide/src/app/editor/editor.js
  8. 2
      apps/remix-ide/src/app/files/fileManager.ts
  9. 3
      apps/remix-ide/src/app/panels/layout.ts
  10. 6
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  11. BIN
      apps/remix-ide/src/assets/img/cairoLogo.webp
  12. BIN
      apps/remix-ide/src/assets/img/starkNetLogo.webp
  13. 2
      apps/remix-ide/src/blockchain/blockchain.js
  14. 22
      libs/remix-core-plugin/src/lib/editor-context-listener.ts
  15. 2
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  16. 24
      libs/remix-solidity/jest.config.js
  17. 1
      libs/remix-solidity/package.json
  18. 12
      libs/remix-solidity/src/compiler/compiler-input.ts
  19. 2
      libs/remix-solidity/src/index.ts
  20. 25
      libs/remix-solidity/tests/compiler-input.spec.ts
  21. 2
      libs/remix-solidity/tsconfig.json
  22. 17
      libs/remix-tests/src/compiler.ts
  23. 98
      libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx
  24. 3
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  25. 84
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  26. 1
      libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx
  27. 12
      libs/remix-ui/panel/src/lib/main/main-panel.tsx
  28. 2
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  29. 1
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  30. 2
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  31. 9
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  32. 9
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  33. 2
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  34. 4
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  35. 10
      libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
  36. 7
      libs/remix-ui/terminal/src/lib/components/Context.tsx
  37. 4
      libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx
  38. 4
      libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx
  39. 20
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  40. 12
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  41. 2
      libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx
  42. 2
      libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx
  43. 15
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css
  44. 28
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx
  45. 2
      libs/remix-ui/workspace/src/lib/actions/index.ts
  46. 6
      workspace.json

@ -0,0 +1,15 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class CurrentSelectedFileIs extends EventEmitter {
command (this: NightwatchBrowser, value: string): NightwatchBrowser {
this.api
.waitForElementContainsText('*[data-id="tabs-component"] *[data-id="tab-active"]', value)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = CurrentSelectedFileIs

@ -147,6 +147,7 @@ module.exports = {
.waitForElementContainsText('.contextview .type', 'uint256') .waitForElementContainsText('.contextview .type', 'uint256')
.waitForElementContainsText('.contextview .name', 'number') .waitForElementContainsText('.contextview .name', 'number')
.click('.contextview [data-action="previous"]') // declaration .click('.contextview [data-action="previous"]') // declaration
.pause(1000)
.execute(() => { .execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition() return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => { }, [], (result) => {
@ -154,6 +155,7 @@ module.exports = {
browser.assert.equal(result.value, '180') browser.assert.equal(result.value, '180')
}) })
.click('.contextview [data-action="next"]') // back to the initial state .click('.contextview [data-action="next"]') // back to the initial state
.pause(1000)
.execute(() => { .execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition() return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => { }, [], (result) => {
@ -161,6 +163,7 @@ module.exports = {
browser.assert.equal(result.value, '323') browser.assert.equal(result.value, '323')
}) })
.click('.contextview [data-action="next"]') // next reference .click('.contextview [data-action="next"]') // next reference
.pause(1000)
.execute(() => { .execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition() return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => { }, [], (result) => {
@ -168,12 +171,74 @@ module.exports = {
browser.assert.equal(result.value, '489') browser.assert.equal(result.value, '489')
}) })
.click('.contextview [data-action="gotoref"]') // back to the declaration .click('.contextview [data-action="gotoref"]') // back to the declaration
.pause(1000)
.execute(() => { .execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition() return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => { }, [], (result) => {
console.log('result', result) console.log('result', result)
browser.assert.equal(result.value, '180') browser.assert.equal(result.value, '180')
}) })
},
'Should display the context view, loop over "Owner" by switching file #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('solidity')
.click('[for="autoCompile"]') // disable auto compile
.openFile('contracts')
.openFile('contracts/3_Ballot.sol')
.waitForElementVisible('#editorView')
.setEditorValue(BallotWithARefToOwner)
.clickLaunchIcon('solidity')
.click('*[data-id="compilerContainerCompileBtn"]') // compile
.pause(2000)
.execute(() => {
(document.getElementById('editorView') as any).gotoLine(14, 6)
}, [], () => {})
.waitForElementVisible('.contextview')
.waitForElementContainsText('.contextview .type', 'ContractDefinition')
.waitForElementContainsText('.contextview .name', 'Owner')
.click('.contextview [data-action="next"]')
.pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
console.log('result', result)
browser.assert.equal(result.value, '1061')
})
.click('.contextview [data-action="next"]')
.pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
console.log('result', result)
browser.assert.equal(result.value, '122')
})
.currentSelectedFileIs('2_Owner.sol') // make sure the current file has been properly changed
.click('.contextview [data-action="next"]')
.pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
console.log('result', result)
browser.assert.equal(result.value, '211')
})
.click('.contextview [data-action="next"]')
.currentSelectedFileIs('3_Ballot.sol')
.pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
console.log('result', result)
browser.assert.equal(result.value, '1061')
})
.click('.contextview [data-action="gotoref"]') // go to the declaration
.pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
console.log('result', result)
browser.assert.equal(result.value, '122')
})
.end() .end()
} }
} }
@ -281,3 +346,149 @@ contract Storage {
return number; return number;
} }
}` }`
const BallotWithARefToOwner = `
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "./2_Owner.sol";
/**
* @title Ballot
* @dev Implements voting process along with vote delegation
*/
contract Ballot {
Owner c;
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
struct Proposal {
// If you can limit the length to a certain number of bytes,
// always use one of bytes1 to bytes32 because they are much cheaper
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
mapping(address => Voter) public voters;
Proposal[] public proposals;
/**
* @dev Create a new ballot to choose one of 'proposalNames'.
* @param proposalNames names of proposals
*/
constructor(bytes32[] memory proposalNames) {
c = new Owner();
chairperson = msg.sender;
voters[chairperson].weight = 1;
for (uint i = 0; i < proposalNames.length; i++) {
// 'Proposal({...})' creates a temporary
// Proposal object and 'proposals.push(...)'
// appends it to the end of 'proposals'.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
/**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter
*/
function giveRightToVote(address voter) public {
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
/**
* @dev Delegate your vote to the voter 'to'.
* @param to address to which vote is delegated
*/
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
/**
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
// If 'proposal' is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/**
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
* @return winnerName_ the name of the winner
*/
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
`

@ -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;
}
}`
}
} }
] ]

@ -77,12 +77,13 @@ module.exports = {
'Should load using URL compiler params': function (browser: NightwatchBrowser) { 'Should load using URL compiler params': function (browser: NightwatchBrowser) {
browser browser
.pause(5000) .pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js') .url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&language=Yul')
.refresh() .refresh()
.pause(5000) .pause(5000)
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770') .assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul') .assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul')
.assert.containsText('#compilierLanguageSelector option[data-id="selected"]', 'Yul')
.verify.elementPresent('#optimize:checked') .verify.elementPresent('#optimize:checked')
.verify.elementPresent('#autoCompile:checked') .verify.elementPresent('#autoCompile:checked')
.verify.attributeEquals('#runs', 'value', '300') .verify.attributeEquals('#runs', 'value', '300')

@ -61,6 +61,7 @@ declare module 'nightwatch' {
acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser
clearConsole (this: NightwatchBrowser): NightwatchBrowser clearConsole (this: NightwatchBrowser): NightwatchBrowser
clearTransactions (this: NightwatchBrowser): NightwatchBrowser clearTransactions (this: NightwatchBrowser): NightwatchBrowser
currentSelectedFileIs (name: string): NightwatchBrowser
} }
export interface NightwatchBrowser { export interface NightwatchBrowser {

@ -438,7 +438,7 @@ class Editor extends Plugin {
if (!filePath) return if (!filePath) return
filePath = await this.call('fileManager', 'getPathFromUrl', filePath) filePath = await this.call('fileManager', 'getPathFromUrl', filePath)
filePath = filePath.file filePath = filePath.file
if (!this.sessions[filePath]) throw new Error('file not found' + filePath) if (!this.sessions[filePath]) return
const path = filePath || this.currentFile const path = filePath || this.currentFile
const { from } = this.currentRequest const { from } = this.currentRequest

@ -470,7 +470,7 @@ class FileManager extends Plugin {
} }
currentFile () { currentFile () {
return this._deps.config.get('currentFile') return this.editor.current()
} }
async closeAllFiles () { async closeAllFiles () {

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 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>))
}) })
}) })

@ -84,11 +84,6 @@ export class EditorContextListener extends Plugin {
async _highlightItems (cursorPosition, compilationResult, file) { async _highlightItems (cursorPosition, compilationResult, file) {
if (this.currentPosition === cursorPosition) return if (this.currentPosition === cursorPosition) return
if (this.currentFile !== file) {
this.currentFile = file
this.currentPosition = cursorPosition
return
}
this._stopHighlighting() this._stopHighlighting()
this.currentPosition = cursorPosition this.currentPosition = cursorPosition
this.currentFile = file this.currentFile = file
@ -122,9 +117,13 @@ export class EditorContextListener extends Plugin {
async _highlight (node, compilationResult) { async _highlight (node, compilationResult) {
if (!node) return if (!node) return
const position = sourceMappingDecoder.decode(node.src) const position = sourceMappingDecoder.decode(node.src)
const fileTarget = compilationResult.getSourceName(position.file)
const nodeFound = this._activeHighlights.find((el) => el.fileTarget === fileTarget && el.position.file === position.file && el.position.length === position.length && el.position.start === position.start)
if (nodeFound) return // if the content is already highlighted, do nothing.
await this._highlightInternal(position, node, compilationResult) await this._highlightInternal(position, node, compilationResult)
if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) { if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) {
this._activeHighlights.push({ position, fileTarget: compilationResult.getSourceName(position.file), nodeId: node.id }) this._activeHighlights.push({ position, fileTarget, nodeId: node.id })
} }
} }
@ -204,13 +203,16 @@ export class EditorContextListener extends Plugin {
} }
_loadContractInfos (node) { _loadContractInfos (node) {
const path = (this.nodes.length && this.nodes[0].absolutePath) || this.results.source.target
for (const i in this.nodes) { for (const i in this.nodes) {
if (this.nodes[i].id === node.scope) { if (this.nodes[i].id === node.scope) {
const contract = this.nodes[i] const contract = this.nodes[i]
this.contract = this.results.data.contracts[this.results.source.target][contract.name] this.contract = this.results.data.contracts[path][contract.name]
this.estimationObj = this.contract.evm.gasEstimates if (contract) {
this.creationCost = this.estimationObj === null ? '-' : this.estimationObj.creation.totalCost this.estimationObj = this.contract.evm.gasEstimates
this.codeDepositCost = this.estimationObj === null ? '-' : this.estimationObj.creation.codeDepositCost this.creationCost = this.estimationObj === null ? '-' : this.estimationObj.creation.totalCost
this.codeDepositCost = this.estimationObj === null ? '-' : this.estimationObj.creation.codeDepositCost
}
} }
} }
} }

@ -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)
} }

@ -0,0 +1,24 @@
module.exports = {
name: 'remix-solidity',
preset: '../../jest.config.js',
verbose: true,
silent: false, // Silent console messages, specially the 'remix-simulator' ones
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
transformIgnorePatterns: ["/node_modules/", "/dist/", "\\.pnp\\.[^\\\/]+$"],
rootDir: "./",
testTimeout: 40000,
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html', 'json'],
// Coverage
collectCoverage: true,
coverageReporters: ['text', 'text-summary'],
collectCoverageFrom: [
"**/*.ts",
"!**/sol/**",
"!src/types.ts",
"!src/logger.ts"
],
coverageDirectory: '../../coverage/libs/remix-solidity'
};

@ -41,7 +41,6 @@
"@types/node": "^13.1.1", "@types/node": "^13.1.1",
"babel-eslint": "^10.0.0", "babel-eslint": "^10.0.0",
"babelify": "^10.0.0", "babelify": "^10.0.0",
"tape": "^4.6.0",
"typescript": "^3.7.4" "typescript": "^3.7.4"
}, },
"scripts": { "scripts": {

@ -1,6 +1,6 @@
'use strict' 'use strict'
import { CompilerInput, Source, CompilerInputOptions } from './types' import { CompilerInput, Source, CompilerInputOptions, Language } from './types'
export default (sources: Source, opts: CompilerInputOptions): string => { export default (sources: Source, opts: CompilerInputOptions): string => {
const o: CompilerInput = { const o: CompilerInput = {
@ -32,3 +32,13 @@ export default (sources: Source, opts: CompilerInputOptions): string => {
} }
return JSON.stringify(o) return JSON.stringify(o)
} }
export const Languages = ['Solidity', 'Yul']
export function getValidLanguage (val: string): Language {
if (val !== undefined && val !== null && val) {
const lang = val.slice(0, 1).toUpperCase() + val.slice(1).toLowerCase()
return Languages.indexOf(lang) > -1 ? lang as Language : null
}
return null
}

@ -1,6 +1,6 @@
export { Compiler } from './compiler/compiler' export { Compiler } from './compiler/compiler'
export { compile } from './compiler/compiler-helpers' export { compile } from './compiler/compiler-helpers'
export { default as CompilerInput } from './compiler/compiler-input' export { default as CompilerInput, getValidLanguage } from './compiler/compiler-input'
export { CompilerAbstract } from './compiler/compiler-abstract' export { CompilerAbstract } from './compiler/compiler-abstract'
export * from './compiler/types' export * from './compiler/types'
export { promisedMiniXhr, pathToURL, baseURLBin, baseURLWasm, canUseWorker, urlFromVersion } from './compiler/compiler-utils' export { promisedMiniXhr, pathToURL, baseURLBin, baseURLWasm, canUseWorker, urlFromVersion } from './compiler/compiler-utils'

@ -0,0 +1,25 @@
import { getValidLanguage } from '../src/compiler/compiler-input'
import { Language } from '../src/compiler/types'
describe('compiler-input', () => {
test('getValidLanguage', () => {
const correctYul: Language = 'Yul'
const correctSolidity: Language = 'Solidity'
const yulUpperCase = 'Yul'
const yulLowerCase = 'yul'
const solidityUpperCase = 'Solidity'
const solidityLowerCase = 'solidity'
expect(getValidLanguage(yulLowerCase)).toBe(correctYul)
expect(getValidLanguage(yulUpperCase)).toBe(correctYul)
expect(getValidLanguage(solidityUpperCase)).toBe(correctSolidity)
expect(getValidLanguage(solidityLowerCase)).toBe(correctSolidity)
expect(getValidLanguage(null)).toBe(null)
expect(getValidLanguage(undefined)).toBe(null)
expect(getValidLanguage('')).toBe(null)
expect(getValidLanguage('A')).toBe(null)
expect(getValidLanguage('Something')).toBe(null)
})
})

@ -1,7 +1,7 @@
{ {
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"types": ["node"] "types": ["jest", "node"]
}, },
"include": ["**/*.ts"] "include": ["**/*.ts"]
} }

@ -47,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
@ -62,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() : '-'
@ -123,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)

@ -8,15 +8,26 @@ import './remix-ui-editor-context-view.css'
export type astNode = { export type astNode = {
name: string, name: string,
id: number, id: number,
children: Array<any>, children?: Array<any>,
typeDescriptions: any, typeDescriptions: any,
nodeType: String, nodeType: String,
src: any, src: string // e.g "142:1361:0"
nodeId: any, }
position: any
export type nodePositionLight = {
file: number,
length: number,
start: number
}
export type astNodeLight = {
fileTarget: String,
nodeId: number,
position: nodePositionLight
} }
export type onContextListenerChangedListener = (nodes: Array<astNode>) => void export type onContextListenerChangedListener = (nodes: Array<astNode>) => void
export type ononCurrentFileChangedListener = (name: string) => void
export type gasEstimationType = { export type gasEstimationType = {
executionCost: string, executionCost: string,
@ -30,8 +41,9 @@ export interface RemixUiEditorContextViewProps {
offsetToLineColumn: (position: any, file: any, sources: any, asts: any) => any, offsetToLineColumn: (position: any, file: any, sources: any, asts: any) => any,
getCurrentFileName: () => String getCurrentFileName: () => String
onContextListenerChanged: (listener: onContextListenerChangedListener) => void onContextListenerChanged: (listener: onContextListenerChangedListener) => void
onCurrentFileChanged: (listener: ononCurrentFileChangedListener) => void
referencesOf: (nodes: astNode) => Array<astNode> referencesOf: (nodes: astNode) => Array<astNode>
getActiveHighlights: () => Array<astNode> getActiveHighlights: () => Array<astNodeLight>
gasEstimation: (node: astNode) => gasEstimationType gasEstimation: (node: astNode) => gasEstimationType
declarationOf: (node: astNode) => astNode declarationOf: (node: astNode) => astNode
} }
@ -48,49 +60,56 @@ function isDefinition (node: any) {
type nullableAstNode = astNode | null type nullableAstNode = astNode | null
export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) { export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) {
/* const loopOverReferences = useRef(0)
gotoLineDisableRef is used to temporarily disable the update of the view. const currentNodeDeclaration = useRef<nullableAstNode>(null)
e.g when the user ask the component to "gotoLine" we don't want to rerender the component (but just to put the mouse on the desired line)
*/
const gotoLineDisableRef = useRef(false)
const [state, setState] = useState<{ const [state, setState] = useState<{
nodes: Array<astNode>, nodes: Array<astNode>,
references: Array<astNode>,
activeHighlights: Array<any> activeHighlights: Array<any>
currentNode: nullableAstNode,
gasEstimation: gasEstimationType gasEstimation: gasEstimationType
}>({ }>({
nodes: [], nodes: [],
references: [],
activeHighlights: [], activeHighlights: [],
currentNode: null,
gasEstimation: { executionCost: '', codeDepositCost: '' } gasEstimation: { executionCost: '', codeDepositCost: '' }
}) })
useEffect(() => { useEffect(() => {
props.onCurrentFileChanged(() => {
currentNodeDeclaration.current = null
setState(prevState => {
return { ...prevState, nodes: [], activeHighlights: [] }
})
})
props.onContextListenerChanged(async (nodes: Array<astNode>) => { props.onContextListenerChanged(async (nodes: Array<astNode>) => {
if (gotoLineDisableRef.current) { let nextNodeDeclaration
gotoLineDisableRef.current = false let nextNode
return
}
let currentNode
if (!props.hide && nodes && nodes.length) { if (!props.hide && nodes && nodes.length) {
currentNode = nodes[nodes.length - 1] nextNode = nodes[nodes.length - 1]
if (!isDefinition(currentNode)) { if (!isDefinition(nextNode)) {
currentNode = await props.declarationOf(currentNode) nextNodeDeclaration = await props.declarationOf(nextNode)
} else {
nextNodeDeclaration = nextNode
} }
} }
let references if (nextNodeDeclaration && currentNodeDeclaration.current && nextNodeDeclaration.id === currentNodeDeclaration.current.id) return
currentNodeDeclaration.current = nextNodeDeclaration
let gasEstimation let gasEstimation
if (currentNode) { if (currentNodeDeclaration.current) {
references = await props.referencesOf(currentNode) if (currentNodeDeclaration.current.nodeType === 'FunctionDefinition') {
if (currentNode.nodeType === 'FunctionDefinition') { gasEstimation = await props.gasEstimation(currentNodeDeclaration.current)
gasEstimation = await props.gasEstimation(currentNode)
} }
} }
const activeHighlights = await props.getActiveHighlights() const activeHighlights: Array<astNodeLight> = await props.getActiveHighlights()
if (nextNode && activeHighlights && activeHighlights.length) {
loopOverReferences.current = activeHighlights.findIndex((el: astNodeLight) => `${el.position.start}:${el.position.length}:${el.position.file}` === nextNode.src)
loopOverReferences.current = loopOverReferences.current === -1 ? 0 : loopOverReferences.current
} else {
loopOverReferences.current = 0
}
setState(prevState => { setState(prevState => {
return { ...prevState, nodes, references, activeHighlights, currentNode, gasEstimation } return { ...prevState, nodes, activeHighlights, gasEstimation }
}) })
}) })
}, []) }, [])
@ -123,8 +142,7 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
if (fileName !== await props.getCurrentFileName()) { if (fileName !== await props.getCurrentFileName()) {
await props.openFile(fileName) await props.openFile(fileName)
} }
if (lineColumn.start && lineColumn.start.line && lineColumn.start.column) { if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) {
gotoLineDisableRef.current = true
props.gotoLine(lineColumn.start.line, lineColumn.end.column + 1) props.gotoLine(lineColumn.start.line, lineColumn.end.column + 1)
} }
} }
@ -141,14 +159,14 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
} }
} }
const _render = (node: nullableAstNode) => { const _render = () => {
const node = currentNodeDeclaration.current
if (!node) return (<div></div>) if (!node) return (<div></div>)
const references = state.references const references = state.activeHighlights
const type = node.typeDescriptions && node.typeDescriptions.typeString ? node.typeDescriptions.typeString : node.nodeType const type = node.typeDescriptions && node.typeDescriptions.typeString ? node.typeDescriptions.typeString : node.nodeType
const referencesCount = `${references ? references.length : '0'} reference(s)` const referencesCount = `${references ? references.length : '0'} reference(s)`
let ref = 0 const nodes: Array<astNodeLight> = state.activeHighlights
const nodes: Array<astNode> = state.activeHighlights
const jumpTo = () => { const jumpTo = () => {
if (node && node.src) { if (node && node.src) {
@ -161,10 +179,10 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
// JUMP BETWEEN REFERENCES // JUMP BETWEEN REFERENCES
const jump = (e: any) => { const jump = (e: any) => {
e.target.dataset.action === 'next' ? ref++ : ref-- e.target.dataset.action === 'next' ? loopOverReferences.current++ : loopOverReferences.current--
if (ref < 0) ref = nodes.length - 1 if (loopOverReferences.current < 0) loopOverReferences.current = nodes.length - 1
if (ref >= nodes.length) ref = 0 if (loopOverReferences.current >= nodes.length) loopOverReferences.current = 0
_jumpToInternal(nodes[ref].position) _jumpToInternal(nodes[loopOverReferences.current].position)
} }
return ( return (
@ -181,7 +199,7 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
return ( return (
!props.hide && <div className="container-context-view contextviewcontainer bg-light text-dark border-0 py-1"> !props.hide && <div className="container-context-view contextviewcontainer bg-light text-dark border-0 py-1">
{_render(state.currentNode)} {_render()}
</div> </div>
) )
} }

@ -398,11 +398,12 @@ export const EditorUI = (props: EditorUIProps) => {
<RemixUiEditorContextView <RemixUiEditorContextView
hide={false} hide={false}
gotoLine={(line, column) => props.plugin.call('editor', 'gotoLine', line, column)} gotoLine={(line, column) => props.plugin.call('editor', 'gotoLine', line, column)}
openFile={(file) => props.plugin.call('editor', 'openFile', file)} openFile={(file) => props.plugin.call('fileManager', 'switchFile', file)}
getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') } } getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') } }
offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) } } offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) } }
getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') } } getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') } }
onContextListenerChanged={(listener) => { props.plugin.on('contextualListener', 'contextChanged', listener) }} onContextListenerChanged={(listener) => { props.plugin.on('contextualListener', 'contextChanged', listener) }}
onCurrentFileChanged={(listener) => { props.plugin.on('fileManager', 'currentFileChanged', listener) }}
referencesOf={(node: astNode) => { return props.plugin.call('contextualListener', 'referencesOf', node) }} referencesOf={(node: astNode) => { return props.plugin.call('contextualListener', 'referencesOf', node) }}
getActiveHighlights={() => { return props.plugin.call('contextualListener', 'getActiveHighlights') }} getActiveHighlights={() => { return props.plugin.call('contextualListener', 'getActiveHighlights') }}
gasEstimation={(node: astNode) => { return props.plugin.call('contextualListener', 'gasEstimation', node) }} gasEstimation={(node: astNode) => { return props.plugin.call('contextualListener', 'gasEstimation', node) }}

@ -1,6 +1,7 @@
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import './remix-ui-home-tab.css' import './remix-ui-home-tab.css'
import JSZip from 'jszip'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import PluginButton from './components/pluginButton' // eslint-disable-line import PluginButton from './components/pluginButton' // eslint-disable-line
@ -152,10 +153,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 startCairo = async () => { const startStarkNet = async () => {
await plugin.appManager.activatePlugin('cairo_compiler') await plugin.appManager.activatePlugin('starkNet_compiler')
plugin.verticalIcons.select('cairo_compiler') plugin.verticalIcons.select('starkNet_compiler')
_paq.push(['trackEvent', 'pluginManager', 'userActivate', 'cairo_compiler']) _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'starkNet_compiler'])
} }
const startSolhint = async () => { const startSolhint = async () => {
await plugin.appManager.activatePlugin(['solidity', 'solhint']) await plugin.appManager.activatePlugin(['solidity', 'solhint'])
@ -173,9 +174,45 @@ 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')
} }
const saveAs = (blob, name) => {
const node = document.createElement('a')
node.download = name
node.rel = 'noopener'
node.href = URL.createObjectURL(blob)
setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s
setTimeout(function () {
try {
node.dispatchEvent(new MouseEvent('click'))
} catch (e) {
var evt = document.createEvent('MouseEvents')
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
20, false, false, false, false, 0, null)
node.dispatchEvent(evt)
}
}, 0) // 40s
}
const downloadFiles = async () => {
try {
plugin.call('notification', 'toast', 'preparing files for download, please wait..')
const zip = new JSZip()
const browserProvider = fileManager.getProvider('browser')
await browserProvider.copyFolderToJson('/', ({ path, content }) => {
zip.file(path, content)
})
zip.generateAsync({ type: 'blob' }).then(function (blob) {
var today = new Date()
var date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate()
var time = today.getHours() + 'h' + today.getMinutes() + 'min'
saveAs(blob, `remix-backup-at-${time}-${date}.zip`)
}).catch((e) => {
plugin.call('notification', 'toast', e.message)
})
} catch (e) {
plugin.call('notification', 'toast', e.message)
}
}
const showFullMessage = (title: string, loadItem: string, examples: Array<string>) => { const showFullMessage = (title: string, loadItem: string, examples: Array<string>) => {
setState(prevState => { setState(prevState => {
@ -229,18 +266,27 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
</ModalDialog> </ModalDialog>
<Toaster message={state.toasterMsg} /> <Toaster message={state.toasterMsg} />
<div className="d-flex flex-column ml-4" id="remixUiRightPanel"> <div className="d-flex flex-column ml-4" id="remixUiRightPanel">
<div className="border-bottom d-flex justify-content-between mr-4 pb-3 mb-3"> <div className="border-bottom d-flex flex-column mr-4 pb-3 mb-3">
<div className="mx-4 my-4 d-flex"> <div className="d-flex justify-content-between ">
<label style={ { fontSize: 'xxx-large', height: 'auto', alignSelf: 'flex-end' } }>Remix IDE</label> <div className="mx-4 my-4 d-flex">
<label style={ { fontSize: 'xxx-large', height: 'auto', alignSelf: 'flex-end' } }>Remix IDE</label>
</div>
<div className="mr-4 d-flex">
<img className="mt-4 mb-2 remixui_home_logoImg" src="assets/img/guitarRemiCroped.webp" onClick={ () => playRemi() } alt=""></img>
<audio
id="remiAudio"
muted={false}
src="assets/audio/remiGuitar-single-power-chord-A-minor.wav"
ref={remiAudioEl}
></audio>
</div>
</div> </div>
<div className="mr-4 d-flex"> <div>
<img className="mt-4 mb-2 remixui_home_logoImg" src="assets/img/guitarRemiCroped.webp" onClick={ () => playRemi() } alt=""></img> <i className="pl-4 text-danger fas fa-exclamation-triangle"></i>
<audio <span className="px-2 remixui_home_text text-danger mt-4 pt-4">
id="remiAudio" Scam Alert: Beware of Youtube videos promoting "liquidity front runner bots" asking to paste contract code into Remix IDE.
muted={false} </span>
src="assets/audio/remiGuitar-single-power-chord-A-minor.wav" <a className="remixui_home_text" target="__blank" href="https://medium.com/remix-ide/remix-in-youtube-crypto-scams-71c338da32d">Learn more</a>
ref={remiAudioEl}
></audio>
</div> </div>
</div> </div>
<div className="row mx-2 mr-4" data-id="landingPageHpSections"> <div className="row mx-2 mr-4" data-id="landingPageHpSections">
@ -250,7 +296,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<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/cairoLogo.webp" envID="CairoLogo" envText="Cairo compiler" l2={true} callback={() => startCairo()} /> <PluginButton imgPath="assets/img/starkNetLogo.webp" envID="starkNetLogo" envText="StarkNet" l2={true} callback={() => startStarkNet()} />
<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()} />
@ -280,6 +326,10 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<i className="mr-1 far fa-hdd"></i> <i className="mr-1 far fa-hdd"></i>
<label className="ml-1 remixui_home_text" onClick={() => connectToLocalhost()}>Connect to Localhost</label> <label className="ml-1 remixui_home_text" onClick={() => connectToLocalhost()}>Connect to Localhost</label>
</p> </p>
<p className="mb-1">
<i className="mr-1 far fa-download"></i>
<label className="ml-1 remixui_home_text" onClick={() => downloadFiles()}>Download Backup</label>
</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">
<button className="btn mr-1 btn-secondary" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button> <button className="btn mr-1 btn-secondary" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button>

@ -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}

@ -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(() => {

@ -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>
) )

@ -7,6 +7,7 @@ import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promi
import { compilerReducer, compilerInitialState } from './reducers/compiler' import { compilerReducer, compilerInitialState } from './reducers/compiler'
import { resetEditorMode, listenToEvents } from './actions/compiler' import { resetEditorMode, listenToEvents } from './actions/compiler'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
import { getValidLanguage } from '@remix-project/remix-solidity'
import './css/style.css' import './css/style.css'
@ -74,6 +75,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const optimize = params.optimize const optimize = params.optimize
const runs = params.runs as string const runs = params.runs as string
const evmVersion = params.evmVersion const evmVersion = params.evmVersion
const language = getValidLanguage(params.language)
return { return {
...prevState, ...prevState,
@ -82,7 +84,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
includeNightlies: includeNightlies, includeNightlies: includeNightlies,
optimize: optimize, optimize: optimize,
runs: runs, runs: runs,
evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default' evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default',
language: (language !== null) ? language : 'Solidity'
} }
}) })
} }
@ -537,8 +540,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<div className="mb-2"> <div className="mb-2">
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label> <label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label>
<select onChange={(e) => handleLanguageChange(e.target.value)} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7"> <select onChange={(e) => handleLanguageChange(e.target.value)} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7">
<option value='Solidity'>Solidity</option> <option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option value='Yul'>Yul</option> <option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select> </select>
</div> </div>
<div className="mb-2"> <div className="mb-2">

@ -1,4 +1,5 @@
import { ICompilerApi } from '@remix-project/remix-lib-ts' import { ICompilerApi } from '@remix-project/remix-lib-ts'
import { getValidLanguage } from '@remix-project/remix-solidity'
const Compiler = require('@remix-project/remix-solidity').Compiler const Compiler = require('@remix-project/remix-solidity').Compiler
const EventEmitter = require('events') const EventEmitter = require('events')
@ -15,6 +16,7 @@ export class CompileTabLogic {
public optimize public optimize
public runs public runs
public evmVersion: string public evmVersion: string
public language: string
public compilerImport public compilerImport
public event public event
@ -39,6 +41,11 @@ export class CompileTabLogic {
} }
this.api.setCompilerParameters({ evmVersion: this.evmVersion }) this.api.setCompilerParameters({ evmVersion: this.evmVersion })
this.compiler.set('evmVersion', this.evmVersion) this.compiler.set('evmVersion', this.evmVersion)
this.language = getValidLanguage(this.api.getCompilerParameters().language)
if (this.language != null) {
this.compiler.set('language', this.language)
}
} }
setOptimize (newOptimizeValue) { setOptimize (newOptimizeValue) {
@ -68,6 +75,8 @@ export class CompileTabLogic {
* @params lang {'Solidity' | 'Yul'} ... * @params lang {'Solidity' | 'Yul'} ...
*/ */
setLanguage (lang) { setLanguage (lang) {
this.language = lang
this.api.setCompilerParameters({ language: lang })
this.compiler.set('language', lang) this.compiler.set('language', lang)
} }

@ -723,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 ref={el => { tabsRef.current[index] = el }} className={classNameTab} title={tab.tooltip}> <div ref={el => { tabsRef.current[index] = el }} className={classNameTab} data-id={index === currentIndexRef.current ? 'tab-active' : ''} title={tab.tooltip}>
{tab.icon ? (<img className="my-1 mr-1 iconImage" 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() }}>
@ -74,7 +74,7 @@ export const TabsUI = (props: TabsUIProps) => {
}, []) }, [])
return ( return (
<div className="remix-ui-tabs d-flex justify-content-between border-0 header nav-tabs"> <div className="remix-ui-tabs d-flex justify-content-between border-0 header nav-tabs" data-id="tabs-component">
<div className="d-flex flex-row" style={ { maxWidth: 'fit-content', width: '97%' } }> <div className="d-flex flex-row" style={ { maxWidth: 'fit-content', width: '97%' } }>
<div className="d-flex flex-row justify-content-center align-items-center m-1 mt-2"> <div className="d-flex flex-row justify-content-center align-items-center m-1 mt-2">
<span data-id="tabProxyZoomOut" className="btn btn-sm px-2 fas fa-search-minus text-dark" title="Zoom out" onClick={() => props.onZoomOut()}></span> <span data-id="tabProxyZoomOut" className="btn btn-sm px-2 fas fa-search-minus text-dark" title="Zoom out" onClick={() => props.onZoomOut()}></span>

@ -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 } })
} }
} }

@ -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,7 +16,8 @@ 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> <span>
@ -29,7 +30,7 @@ const Context = ({ opts, blockchain }) => {
<div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_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> <span>

@ -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') {
@ -26,7 +26,7 @@ const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugi
<span id={`tx${tx.hash}`} key={index}> <span id={`tx${tx.hash}`} key={index}>
<div className="remix_ui_terminal_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='remix_ui_terminal_buttons'> <div className='remix_ui_terminal_buttons'>
<div <div
className='remix_ui_terminal_debug btn btn-primary btn-sm' className='remix_ui_terminal_debug btn btn-primary btn-sm'

@ -3,7 +3,7 @@ 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') {
@ -21,7 +21,7 @@ const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash,
<span id={`tx${tx.hash}`} key={index}> <span id={`tx${tx.hash}`} key={index}>
<div className="remix_ui_terminal_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='remix_ui_terminal_buttons'> <div className='remix_ui_terminal_buttons'>
<div className='remix_ui_terminal_debug btn btn-primary btn-sm' <div className='remix_ui_terminal_debug btn btn-primary btn-sm'
data-shared='txLoggerDebugButton' data-shared='txLoggerDebugButton'

@ -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 })
} }
} }
} }

@ -419,6 +419,16 @@ 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' const classNameBlock = 'remix_ui_terminal_block px-4 py-1 text-break'
return ( return (
@ -490,6 +500,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
showTableHash={showTableHash} showTableHash={showTableHash}
txDetails={txDetails} txDetails={txDetails}
modal={modal} modal={modal}
provider={x.provider}
/>} />}
</div> </div>
) )
@ -517,6 +528,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
showTableHash = { showTableHash } showTableHash = { showTableHash }
txDetails = { txDetails } txDetails = { txDetails }
modal={modal} modal={modal}
provider={x.provider}
/>) } />) }
</div> </div>
) )

@ -7,7 +7,7 @@ interface HomeProps {
function Home ({ verticalIconPlugin }: HomeProps) { function Home ({ verticalIconPlugin }: HomeProps) {
return ( return (
<div <div
className="mt-3 my-1 remixui_homeIcon" className="mt-2 my-1 remixui_homeIcon"
onClick={async () => await verticalIconPlugin.activateHome()} onClick={async () => await verticalIconPlugin.activateHome()}
{...{ plugin: 'home'}} {...{ plugin: 'home'}}
title="Home" title="Home"

@ -85,7 +85,7 @@ const Icon = ({
return ( return (
<> <>
<div <div
className={`remixui_icon m-2 pl-1`} className={`remixui_icon m-2 pt-1`}
onClick={() => { onClick={() => {
(verticalIconPlugin as any).toggle(name) (verticalIconPlugin as any).toggle(name)
}} }}

@ -29,6 +29,7 @@
width: 36px; width: 36px;
height: 36px; height: 36px;
border-radius: 8px; border-radius: 8px;
align-items: center;
} }
.remixui_icon img { .remixui_icon img {
width: 28px; width: 28px;
@ -39,15 +40,12 @@
.remixui_icon .selected-dark { .remixui_icon .selected-dark {
filter: invert(1) grayscale(1); filter: invert(1) grayscale(1);
} }
.remixui_icon .selected-light { .remixui_icon .selected-light {
filter: invert(0) grayscale(1); filter: invert(0) grayscale(1);
} }
.remixui_image {
}
.remixui_icon svg { .remixui_icon svg {
width: 28px; width: 28px;
height: 28px; height: 28px;
@ -106,9 +104,13 @@
scrollbar-width: none; /* Firefox hide scrollbar */ scrollbar-width: none; /* Firefox hide scrollbar */
-ms-overflow-style: none; -ms-overflow-style: none;
} }
.remixui_requiredSection {
text-align: center;
}
.remixui_scrollable-container { .remixui_scrollable-container {
flex-basis: 510px; flex-basis: 510px;
flex-grow: 2; flex-grow: 2;
text-align: center;
/* border-bottom: 3px solid #3f4455; */ /* border-bottom: 3px solid #3f4455; */
} }
.remixui_scrollbar::-webkit-scrollbar { /* Chrome, Safari and other Webkit browsers*/ .remixui_scrollbar::-webkit-scrollbar { /* Chrome, Safari and other Webkit browsers*/
@ -119,9 +121,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 {
@ -132,7 +137,3 @@
list-style: none; list-style: none;
margin: 0px; margin: 0px;
} }
.remixui_icon-chevron {
cursor: pointer;
}

@ -90,7 +90,7 @@ const RemixUiVerticalIconsPanel = ({
<Chevron <Chevron
direction='up' direction='up'
divElementRef={scrollableRef} divElementRef={scrollableRef}
cssRule={'fa fa-chevron-up remixui_icon-chevron mt-0 mb-0 ml-1 pl-3'} cssRule={'fa fa-chevron-up remixui_icon-chevron my-0'}
/> />
) : null ) : null
} }
@ -109,19 +109,19 @@ const RemixUiVerticalIconsPanel = ({
itemContextAction={itemContextAction} itemContextAction={itemContextAction}
/> />
</div> </div>
<div> <div className="remixui_default-icons-container border-0">
{ scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? (<Chevron { scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? (<Chevron
divElementRef={scrollableRef} divElementRef={scrollableRef}
direction='down' direction='down'
cssRule={'fa fa-chevron-down remixui_icon-chevron mt-0 mb-0 ml-1 pl-3'} cssRule={'fa fa-chevron-down remixui_icon-chevron my-0'}
/>) : null } />) : null }
<IconList <IconList
theme={theme} theme={theme}
icons={icons.filter((p) => p.profile.name === 'settings' || p.profile.name === 'pluginManager')} icons={icons.filter((p) => p.profile.name === 'settings' || p.profile.name === 'pluginManager')}
verticalIconsPlugin={verticalIconsPlugin} verticalIconsPlugin={verticalIconsPlugin}
itemContextAction={itemContextAction} itemContextAction={itemContextAction}
/> />
</div> </div>
</div> </div>
</div> </div>
) )

@ -179,7 +179,7 @@ export const createNewFolder = async (path: string, rootDir: string) => {
const exists = await fileManager.exists(dirName) const exists = await fileManager.exists(dirName)
if (exists) { if (exists) {
return dispatch(displayNotification('Rename File Failed', `A file or folder ${extractNameFromKey(path)} already exists at this location. Please choose a different name.`, 'Close', null, () => {})) return dispatch(displayNotification('Failed to create folder', `A folder ${extractNameFromKey(path)} already exists at this location. Please choose a different name.`, 'Close', null, () => {}))
} }
await fileManager.mkdir(dirName) await fileManager.mkdir(dirName)
path = path.indexOf(rootDir + '/') === 0 ? path.replace(rootDir + '/', '') : path path = path.indexOf(rootDir + '/') === 0 ? path.replace(rootDir + '/', '') : path

@ -309,10 +309,10 @@
} }
}, },
"test": { "test": {
"builder": "@nrwl/workspace:run-commands", "builder": "@nrwl/jest:jest",
"options": { "options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"], "jestConfig": "libs/remix-solidity/jest.config.js",
"cwd": "libs/remix-solidity" "tsConfig": "libs/remix-solidity/tsconfig.spec.json"
} }
}, },
"build": { "build": {

Loading…
Cancel
Save