Merge branch 'swap_it' into debugger

pull/1/head
Liana Husikyan 6 years ago committed by GitHub
commit cc0351a704
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      extensions/etherscan-general/index.js
  2. 2
      package.json
  3. 105
      src/app.js
  4. 30
      src/app/components/plugin-manager-component.js
  5. 2
      src/app/components/swap-panel-component.js
  6. 113
      src/app/components/vertical-icons-component.js
  7. 3
      src/app/debugger/debuggerUI/vmDebugger/CodeListView.js
  8. 24
      src/app/editor/SourceHighlighters.js
  9. 34
      src/app/editor/editor.js
  10. 2
      src/app/editor/sourceHighlighter.js
  11. 2
      src/app/execution/txLogger.js
  12. 30
      src/app/files/browser-files-tree.js
  13. 31
      src/app/files/fileManager.js
  14. 24
      src/app/files/remixd-handle.js
  15. 5
      src/app/panels/editor-panel.js
  16. 30
      src/app/panels/file-panel.js
  17. 4
      src/app/panels/styles/file-panel-styles.js
  18. 4
      src/app/panels/styles/terminal-styles.js
  19. 15
      src/app/panels/tab-proxy.js
  20. 30
      src/app/panels/terminal.js
  21. 94
      src/app/plugin/bundle.js
  22. 55
      src/app/plugin/index.js
  23. 71
      src/app/plugin/package.json
  24. 53
      src/app/plugin/plugin.md
  25. 150
      src/app/plugin/pluginAPI.js
  26. 179
      src/app/plugin/pluginManager.js
  27. 38
      src/app/plugin/plugins.js
  28. 1
      src/app/staticanalysis/styles/staticAnalysisView-styles.js
  29. 30
      src/app/tabs/analysis-tab.js
  30. 41
      src/app/tabs/compile-tab.js
  31. 2
      src/app/tabs/compileTab/compilerContainer.js
  32. 30
      src/app/tabs/debugger-tab.js
  33. 66
      src/app/tabs/network-module.js
  34. 30
      src/app/tabs/run-tab.js
  35. 4
      src/app/tabs/runTab/contractDropdown.js
  36. 18
      src/app/tabs/runTab/settings.js
  37. 30
      src/app/tabs/settings-tab.js
  38. 35
      src/app/tabs/styles/run-tab-styles.js
  39. 38
      src/app/tabs/test-tab.js
  40. 2
      src/app/tabs/testTab/testTab.js
  41. 23
      src/app/tabs/theme-module.js
  42. 23
      src/app/tabs/txlistener-module.js
  43. 30
      src/app/ui/auto-complete-popup.js
  44. 20
      src/app/ui/card.js
  45. 341
      src/app/ui/landing-page/landing-page.js
  46. 2
      src/app/ui/landing-page/workspace.js
  47. 2
      src/app/ui/modal-dialog-custom.js
  48. 4
      src/app/ui/styles/dropdown-styles.js
  49. 13
      src/framingService.js
  50. 4
      src/lib/cmdInterpreterAPI.js
  51. 2
      src/lib/gist-handler.js
  52. 2
      src/multiParamManager.js
  53. 4
      src/remixAppManager.js
  54. 26
      src/universal-dapp-styles.js
  55. 31
      src/universal-dapp.js
  56. 2
      test-browser/plugin/index.html
  57. 10
      test-browser/plugin/plugin.js

@ -40,7 +40,7 @@ function load () {
} }
}) })
setInterval(function () { setInterval(function () {
remix.call('app', 'detectNetWork', [], function (error, result) { remix.call('network', 'detectNetWork', [], function (error, result) {
if (error) console.log(error) if (error) console.log(error)
if (network.innerHTML !== result[0].name + ' - ' + result[0].id) { if (network.innerHTML !== result[0].name + ' - ' + result[0].id) {
currentNetWork = result[0].name currentNetWork = result[0].name

@ -65,7 +65,7 @@
}, },
"dependencies": { "dependencies": {
"http-server": "0.9.0", "http-server": "0.9.0",
"remix-plugin": "0.0.1-alpha.43", "remix-plugin": "0.0.2-alpha.3",
"remixd": "0.1.8-alpha.6" "remixd": "0.1.8-alpha.6"
}, },
"repository": { "repository": {

@ -6,7 +6,6 @@ var yo = require('yo-yo')
var async = require('async') var async = require('async')
var request = require('request') var request = require('request')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var EventManager = require('./lib/events')
var registry = require('./global/registry') var registry = require('./global/registry')
var UniversalDApp = require('./universal-dapp.js') var UniversalDApp = require('./universal-dapp.js')
var UniversalDAppUI = require('./universal-dapp-ui.js') var UniversalDAppUI = require('./universal-dapp-ui.js')
@ -54,9 +53,8 @@ import { EntityStore } from './lib/store'
import { RemixAppManager } from './remixAppManager' import { RemixAppManager } from './remixAppManager'
import { LandingPage } from './app/ui/landing-page/landing-page' import { LandingPage } from './app/ui/landing-page/landing-page'
import framingService from './framingService' import framingService from './framingService'
import { ApiFactory } from 'remix-plugin'
import { TxListenerModule } from './app/tabs/txlistener-module'
import { ThemeModule } from './app/tabs/theme-module' import { ThemeModule } from './app/tabs/theme-module'
import { NetworkModule } from './app/tabs/network-module'
var css = csjs` var css = csjs`
html { box-sizing: border-box; } html { box-sizing: border-box; }
@ -116,11 +114,9 @@ var css = csjs`
} }
` `
class App extends ApiFactory { class App {
constructor (api = {}, events = {}, opts = {}) { constructor (api = {}, events = {}, opts = {}) {
super()
var self = this var self = this
this.event = new EventManager()
self._components = {} self._components = {}
registry.put({api: self, name: 'app'}) registry.put({api: self, name: 'app'})
@ -167,18 +163,10 @@ class App extends ApiFactory {
init () { init () {
var self = this var self = this
self._components.resizeFeature = new PanelsResize('#swap-panel', '#editor-container', { 'minWidth': 300, x: 300 }) self._components.resizeFeature = new PanelsResize('#swap-panel', '#editor-container', { 'minWidth': 300, x: 450 })
run.apply(self) run.apply(self)
} }
get profile () {
return {
name: 'app',
description: 'service - provides information about current context (network).',
methods: ['getExecutionContextProvider', 'getProviderEndpoint', 'detectNetWork', 'addProvider', 'removeProvider']
}
}
render () { render () {
var self = this var self = this
if (self._view.el) return self._view.el if (self._view.el) return self._view.el
@ -250,45 +238,6 @@ class App extends ApiFactory {
if (callback) callback(error) if (callback) callback(error)
}) })
} }
getExecutionContextProvider () {
return new Promise((resolve, reject) => {
resolve(executionContext.getProvider())
})
}
getProviderEndpoint () {
return new Promise((resolve, reject) => {
if (executionContext.getProvider() === 'web3') {
resolve(executionContext.web3().currentProvider.host)
} else {
reject('no endpoint: current provider is either injected or vm')
}
})
}
detectNetWork () {
return new Promise((resolve, reject) => {
executionContext.detectNetwork((error, network) => {
if (error) return reject(error)
resolve(network)
})
})
}
addProvider (name, url) {
return new Promise((resolve, reject) => {
executionContext.addProvider({ name, url })
resolve()
})
}
removeProvider (name) {
return new Promise((resolve, reject) => {
executionContext.removeProvider(name)
resolve()
})
}
} }
module.exports = App module.exports = App
@ -329,7 +278,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'}) registry.put({api: self._components.compilersArtefacts, name: 'compilersartefacts'})
// ----------------- UniversalDApp ----------------- // ----------------- UniversalDApp -----------------
var udapp = new UniversalDApp(registry) const udapp = new UniversalDApp(registry)
// TODO: to remove when possible // TODO: to remove when possible
registry.put({api: udapp, name: 'udapp'}) registry.put({api: udapp, name: 'udapp'})
udapp.event.register('transactionBroadcasted', (txhash, networkName) => { udapp.event.register('transactionBroadcasted', (txhash, networkName) => {
@ -337,14 +286,14 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
if (txLink) registry.get('logCallback').api.logCallback(yo`<a href="${txLink}" target="_blank">${txLink}</a>`) if (txLink) registry.get('logCallback').api.logCallback(yo`<a href="${txLink}" target="_blank">${txLink}</a>`)
}) })
var udappUI = new UniversalDAppUI(udapp, registry) const udappUI = new UniversalDAppUI(udapp, registry)
// TODO: to remove when possible // TODO: to remove when possible
registry.put({api: udappUI, name: 'udappUI'}) registry.put({api: udappUI, name: 'udappUI'})
// ----------------- Tx listener ----------------- // ----------------- Tx listener -----------------
var transactionReceiptResolver = new TransactionReceiptResolver() const transactionReceiptResolver = new TransactionReceiptResolver()
var txlistener = new Txlistener({ const txlistener = new Txlistener({
api: { api: {
contracts: function () { contracts: function () {
if (self._components.compilersArtefacts['__last']) return self._components.compilersArtefacts['__last'].getContracts() if (self._components.compilersArtefacts['__last']) return self._components.compilersArtefacts['__last'].getContracts()
@ -358,8 +307,9 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
udapp: udapp.event udapp: udapp.event
}}) }})
registry.put({api: txlistener, name: 'txlistener'}) registry.put({api: txlistener, name: 'txlistener'})
udapp.startListening(txlistener)
var eventsDecoder = new EventsDecoder({ const eventsDecoder = new EventsDecoder({
api: { api: {
resolveReceipt: function (tx, cb) { resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb) transactionReceiptResolver.resolve(tx, cb)
@ -368,11 +318,6 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
}) })
registry.put({api: eventsDecoder, name: 'eventsdecoder'}) registry.put({api: eventsDecoder, name: 'eventsdecoder'})
/*
that proxy is used by appManager to broadcast new transaction event
*/
const txListenerModule = new TxListenerModule(txlistener)
txlistener.startListening() txlistener.startListening()
// TODO: There are still a lot of dep between editorpanel and filemanager // TODO: There are still a lot of dep between editorpanel and filemanager
@ -385,9 +330,13 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// ----------------- file manager ---------------------------- // ----------------- file manager ----------------------------
self._components.fileManager = new FileManager() self._components.fileManager = new FileManager()
var fileManager = self._components.fileManager const fileManager = self._components.fileManager
registry.put({api: fileManager, name: 'filemanager'}) registry.put({api: fileManager, name: 'filemanager'})
// ----------------- Network ----------------------------
const networkModule = new NetworkModule()
registry.put({api: networkModule, name: 'network'})
// ----------------- theme module ---------------------------- // ----------------- theme module ----------------------------
const themeModule = new ThemeModule() const themeModule = new ThemeModule()
registry.put({api: themeModule, name: 'themeModule'}) registry.put({api: themeModule, name: 'themeModule'})
@ -397,7 +346,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
registry.put({ api: self._components.editorpanel, name: 'editorpanel' }) registry.put({ api: self._components.editorpanel, name: 'editorpanel' })
// ----------------- Renderer ----------------- // ----------------- Renderer -----------------
var renderer = new Renderer() const renderer = new Renderer()
registry.put({api: renderer, name: 'renderer'}) registry.put({api: renderer, name: 'renderer'})
// ----------------- app manager ---------------------------- // ----------------- app manager ----------------------------
@ -413,17 +362,18 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const pluginManagerComponent = new PluginManagerComponent() const pluginManagerComponent = new PluginManagerComponent()
const swapPanelComponent = new SwapPanelComponent('swapPanel', appStore, appManager, { default: true, displayHeader: true }) const swapPanelComponent = new SwapPanelComponent('swapPanel', appStore, appManager, { default: true, displayHeader: true })
registry.put({api: appManager.proxy(), name: 'pluginmanager'})
pluginManagerComponent.setApp(appManager)
pluginManagerComponent.setStore(appStore)
// ----------------- Vertical Icon ----------------------------
const verticalIconsComponent = new VerticalIconsComponent('swapPanel', appStore) const verticalIconsComponent = new VerticalIconsComponent('swapPanel', appStore)
const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconsComponent) // eslint-disable-line const swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconsComponent) // eslint-disable-line
const mainPanelApi = new SwapPanelApi(mainPanelComponent, verticalIconsComponent) // eslint-disable-line const mainPanelApi = new SwapPanelApi(mainPanelComponent, verticalIconsComponent) // eslint-disable-line
const verticalIconsApi = new VerticalIconsApi(verticalIconsComponent) // eslint-disable-line const verticalIconsApi = new VerticalIconsApi(verticalIconsComponent) // eslint-disable-line
registry.put({api: appManager.proxy(), name: 'pluginmanager'})
registry.put({api: verticalIconsApi, name: 'verticalicon'}) registry.put({api: verticalIconsApi, name: 'verticalicon'})
pluginManagerComponent.setApp(appManager)
pluginManagerComponent.setStore(appStore)
self._components.editorpanel.init() self._components.editorpanel.init()
self._components.fileManager.init() self._components.fileManager.init()
@ -469,16 +419,15 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
let sourceHighlighters = registry.get('editor').api.sourceHighlighters let sourceHighlighters = registry.get('editor').api.sourceHighlighters
appManager.init([ appManager.init([
this.api(),
landingPage.api(), landingPage.api(),
udapp.api(), udapp.api(),
fileManager.api(), fileManager.api(),
sourceHighlighters.api(), sourceHighlighters.api(),
txListenerModule.api(),
filePanel.api(), filePanel.api(),
// { profile: support.profile(), api: support }, // { profile: support.profile(), api: support },
settings.api(), settings.api(),
pluginManagerComponent.api(), pluginManagerComponent.api(),
networkModule.api(),
themeModule.api() themeModule.api()
]) ])
@ -496,8 +445,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// The event listener needs to be registered as early as possible, because the // The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event. // parent will send the message upon the "load" event.
var filesToLoad = null let filesToLoad = null
var loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later let loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later
window.addEventListener('message', function (ev) { window.addEventListener('message', function (ev) {
if (typeof ev.data === typeof [] && ev.data[0] === 'loadFiles') { if (typeof ev.data === typeof [] && ev.data[0] === 'loadFiles') {
@ -515,7 +464,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
self.loadFiles(filesToLoad) self.loadFiles(filesToLoad)
} }
var txLogger = new TxLogger() // eslint-disable-line const txLogger = new TxLogger() // eslint-disable-line
txLogger.event.register('debuggingRequested', (hash) => { txLogger.event.register('debuggingRequested', (hash) => {
if (!appStore.isActive('debugger')) appManager.activateOne('debugger') if (!appStore.isActive('debugger')) appManager.activateOne('debugger')
debug.debugger().debug(hash) debug.debugger().debug(hash)
@ -547,9 +496,9 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
} }
udapp.resetAPI(transactionContextAPI) udapp.resetAPI(transactionContextAPI)
var queryParams = new QueryParams() const queryParams = new QueryParams()
var loadingFromGist = self.loadFromGist(queryParams.get()) const loadingFromGist = self.loadFromGist(queryParams.get())
if (!loadingFromGist) { if (!loadingFromGist) {
// insert ballot contract if there are no files to show // insert ballot contract if there are no files to show
self._components.filesProviders['browser'].resolveDirectory('browser', (error, filesList) => { self._components.filesProviders['browser'].resolveDirectory('browser', (error, filesList) => {

@ -2,7 +2,7 @@ const yo = require('yo-yo')
const csjs = require('csjs-inject') const csjs = require('csjs-inject')
const EventEmitter = require('events') const EventEmitter = require('events')
const LocalPlugin = require('./local-plugin') const LocalPlugin = require('./local-plugin')
import { Plugin, ApiFactory } from 'remix-plugin' import { Plugin, BaseApi } from 'remix-plugin'
const css = csjs` const css = csjs`
.pluginSearch { .pluginSearch {
@ -34,10 +34,21 @@ const css = csjs`
} }
` `
class PluginManagerComponent extends ApiFactory { const profile = {
name: 'pluginManager',
displayName: 'Plugin manager',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzU1IDQ1M3EzNyAzOCAzNyA5MC41dC0zNyA5MC41bC00MDEgNDAwIDE1MCAxNTAtMTYwIDE2MHEtMTYzIDE2My0zODkuNSAxODYuNXQtNDExLjUtMTAwLjVsLTM2MiAzNjJoLTE4MXYtMTgxbDM2Mi0zNjJxLTEyNC0xODUtMTAwLjUtNDExLjV0MTg2LjUtMzg5LjVsMTYwLTE2MCAxNTAgMTUwIDQwMC00MDFxMzgtMzcgOTEtMzd0OTAgMzcgMzcgOTAuNS0zNyA5MC41bC00MDAgNDAxIDIzNCAyMzQgNDAxLTQwMHEzOC0zNyA5MS0zN3Q5MCAzN3oiLz48L3N2Zz4=',
description: 'Start/stop services, modules and plugins',
kind: 'settings',
location: 'swapPanel'
}
class PluginManagerComponent extends BaseApi {
constructor () { constructor () {
super() super(profile)
this.event = new EventEmitter() this.event = new EventEmitter()
this.views = { this.views = {
root: null, root: null,
@ -47,19 +58,6 @@ class PluginManagerComponent extends ApiFactory {
this.filter = '' this.filter = ''
} }
get profile () {
return {
displayName: 'plugin manager',
name: 'pluginManager',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzU1IDQ1M3EzNyAzOCAzNyA5MC41dC0zNyA5MC41bC00MDEgNDAwIDE1MCAxNTAtMTYwIDE2MHEtMTYzIDE2My0zODkuNSAxODYuNXQtNDExLjUtMTAwLjVsLTM2MiAzNjJoLTE4MXYtMTgxbDM2Mi0zNjJxLTEyNC0xODUtMTAwLjUtNDExLjV0MTg2LjUtMzg5LjVsMTYwLTE2MCAxNTAgMTUwIDQwMC00MDFxMzgtMzcgOTEtMzd0OTAgMzcgMzcgOTAuNS0zNyA5MC41bC00MDAgNDAxIDIzNCAyMzQgNDAxLTQwMHEzOC0zNyA5MS0zN3Q5MCAzN3oiLz48L3N2Zz4=',
description: 'start/stop services, modules and plugins',
kind: 'settings',
location: 'swapPanel'
}
}
setApp (appManager) { setApp (appManager) {
this.appManager = appManager this.appManager = appManager
} }

@ -73,7 +73,7 @@ module.exports = SwapPanelComponent
const css = csjs` const css = csjs`
.plugins { .plugins {
height : 100%; height : 95%;
} }
.plugItIn { .plugItIn {
display : none; display : none;

@ -1,6 +1,7 @@
var yo = require('yo-yo') var yo = require('yo-yo')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var helper = require('../../lib/helper') var helper = require('../../lib/helper')
let globalRegistry = require('../../global/registry')
const EventEmitter = require('events') const EventEmitter = require('events')
@ -32,6 +33,11 @@ class VerticalIconComponent {
}) })
this.store.event.on('add', (api) => { }) this.store.event.on('add', (api) => { })
this.store.event.on('remove', (api) => { }) this.store.event.on('remove', (api) => { })
let themeModule = globalRegistry.get('themeModule').api
themeModule.events.on('themeChanged', (type) => {
this.onThemeChanged(type)
})
} }
stopListenOnStatus (api) { stopListenOnStatus (api) {
@ -56,8 +62,15 @@ class VerticalIconComponent {
* Add an icon to the map * Add an icon to the map
* @param {ModuleProfile} profile The profile of the module * @param {ModuleProfile} profile The profile of the module
*/ */
addIcon ({kind, name, icon, displayName}) { addIcon ({kind, name, icon, displayName, tooltip}) {
this.icons[name] = yo`<div class="${css.icon}" onclick="${(e) => { this._iconClick(name) }}" plugin="${name}" title="${displayName || name}" ><img src="${icon}" alt="${name}" /></div>` let title = (displayName || name)// + (tooltip ? tooltip : "")
this.icons[name] = yo`
<div
class="${css.icon}"
onclick="${(e) => { this._iconClick(name) }}"
plugin="${name}" title="${title}" >
<img class="image" src="${icon}" alt="${name}" />
</div>`
this.iconKind[kind || 'other'].appendChild(this.icons[name]) this.iconKind[kind || 'other'].appendChild(this.icons[name])
} }
@ -69,7 +82,7 @@ class VerticalIconComponent {
setIconStatus (name, status) { setIconStatus (name, status) {
const el = this.icons[name] const el = this.icons[name]
if (!el) return if (!el) return
let statusEl = el.querySelector('i') let statusEl = el.querySelector('span')
if (statusEl) { if (statusEl) {
el.removeChild(statusEl) el.removeChild(statusEl)
} }
@ -77,7 +90,17 @@ class VerticalIconComponent {
let key = helper.checkSpecialChars(status.key) ? '' : status.key let key = helper.checkSpecialChars(status.key) ? '' : status.key
let type = helper.checkSpecialChars(status.type) ? '' : status.type let type = helper.checkSpecialChars(status.type) ? '' : status.type
let title = helper.checkSpecialChars(status.title) ? '' : status.title let title = helper.checkSpecialChars(status.title) ? '' : status.title
el.appendChild(yo`<i title="${title}" class="fa fa-${key} ${css.status} text-${type}" aria-hidden="true"></i>`) el.appendChild(yo`<span title="${title}" class="fa fa-${key} ${css.status} text-${type}" aria-hidden="true"></span>`)
// el.classList = "" doesn't work on all browser use instead
var classList = el.classList
while (classList.length > 0) {
classList.remove(classList.item(0))
}
el.classList.add(`${css.icon}`)
el.classList.add('border')
el.classList.add(`border-${type}`)
} }
} }
@ -89,22 +112,57 @@ class VerticalIconComponent {
if (this.icons[name]) this.iconKind[kind || 'other'].removeChild(this.icons[name]) if (this.icons[name]) this.iconKind[kind || 'other'].removeChild(this.icons[name])
} }
select (name) { /**
let currentActive = this.view.querySelector(`.${css.active}`) * Remove active for the current activated icons
*/
removeActive () {
// reset filters
const images = this.view.querySelectorAll(`.image`)
images.forEach(function (im) {
im.style.setProperty('filter', 'invert(0.5)')
})
// remove active
const currentActive = this.view.querySelector(`.${css.active}`)
if (currentActive) { if (currentActive) {
let currentTitle = currentActive.getAttribute('title') currentActive.classList.remove(css.active)
currentActive.classList.toggle(`${css.active}`) }
if (currentTitle !== name) { }
let activate = this.view.querySelector(`[plugin="${name}"]`)
if (activate) activate.classList.toggle(`${css.active}`) /**
} * Add active for the new activated icon
} else { * @param {string} name Name of profile of the module to activate
let activate = this.view.querySelector(`[plugin="${name}"]`) */
if (activate) activate.classList.toggle(`${css.active}`) addActive (name) {
const themeType = globalRegistry.get('themeModule').api.currentTheme().quality
const invert = themeType === 'dark' ? 1 : 0
const nextActive = this.view.querySelector(`[plugin="${name}"]`)
if (nextActive) {
let image = nextActive.querySelector('.image')
nextActive.classList.add(css.active)
image.style.setProperty('filter', `invert(${invert})`)
} }
}
/**
* Set an icon as active
* @param {string} name Name of profile of the module to activate
*/
select (name) {
this.removeActive()
this.addActive(name)
this.events.emit('showContent', name) this.events.emit('showContent', name)
} }
onThemeChanged (themeType) {
const invert = themeType === 'dark' ? 1 : 0
const active = this.view.querySelector(`.${css.active}`)
if (active) {
let image = active.querySelector('.image')
image.style.setProperty('filter', `invert(${invert})`)
}
}
_iconClick (name) { _iconClick (name) {
this.select(name) this.select(name)
} }
@ -173,31 +231,29 @@ const css = csjs`
margin-left: 10px; margin-left: 10px;
margin-top: 15px; margin-top: 15px;
} }
.icon { .icon {
cursor: pointer; cursor: pointer;
margin-bottom: 12px; margin-bottom: 12px;
width: 36px; width: 36px;
height: 36px; height: 36px;
padding: 3px; padding: 3px;
position: relative; position: relative;
border-radius: 8px;
} }
.icon img { .icon img {
width: 28px; width: 28px;
height: 28px; height: 28px;
padding: 4px; padding: 4px;
filter: invert(0.5);
}
.image {
} }
.icon svg { .icon svg {
width: 28px; width: 28px;
height: 28px; height: 28px;
padding: 4px; padding: 4px;
} }
.icon.active { .icon[title='Settings'] {
border: solid 3px hsla(229, 75%, 87%, 1);
border-radius: 8px;
padding-top: 1px;
padding-left: 1px;
}
.icon[title='settings'] {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
} }
@ -206,4 +262,15 @@ const css = csjs`
bottom: 0; bottom: 0;
right: 0; right: 0;
} }
.statusWithBG
border-radius: 8px;
background-color: var(--danger);
color: var(--light);
font-size: 12px;
height: 15px;
text-align: center;
font-weight: bold;
padding-left: 5px;
padding-right: 5px;
}
` `

@ -40,7 +40,8 @@ CodeListView.prototype.indexChanged = function (index) {
} }
} }
this.itemSelected = this.codeView.children[index] this.itemSelected = this.codeView.children[index]
this.itemSelected.style.setProperty('background-color', 'var(--danger)') this.itemSelected.style.setProperty('background-color', 'var(--info)')
this.itemSelected.style.setProperty('color', 'var(--light)')
this.itemSelected.setAttribute('selected', 'selected') this.itemSelected.setAttribute('selected', 'selected')
if (this.itemSelected.firstChild) { if (this.itemSelected.firstChild) {
this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px') this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px')

@ -1,24 +1,24 @@
'use strict' 'use strict'
const SourceHighlighter = require('./sourceHighlighter') const SourceHighlighter = require('./sourceHighlighter')
import { ApiFactory } from 'remix-plugin' import { EditorApi } from 'remix-plugin'
class SourceHighlighters extends ApiFactory { const profile = {
displayName: 'source highlighters',
name: 'editor',
description: 'service - highlight source code'
}
// EditorApi:
// - methods: ['highlight', 'discardHighlight'],
class SourceHighlighters extends EditorApi {
constructor () { constructor () {
super() super(profile)
this.highlighters = {} this.highlighters = {}
} }
get profile () {
return {
displayName: 'source highlighters',
name: 'sourceHighlighters',
methods: ['highlight', 'discardHighlight'],
description: 'service - highlight source code'
}
}
highlight (lineColumnPos, filePath, hexColor) { highlight (lineColumnPos, filePath, hexColor) {
const { from } = this.currentRequest const { from } = this.currentRequest
try { try {

@ -33,25 +33,6 @@ document.head.appendChild(yo`
.ace_gutter-cell.ace_breakpoint{ .ace_gutter-cell.ace_breakpoint{
background-color: var(--secondary); background-color: var(--secondary);
} }
.highlightreference {
position:absolute;
z-index:20;
background-color: var(--primary);
opacity: 0.6
}
.highlightreferenceline {
position:absolute;
z-index:20;
background-color: var(--primary);
opacity: 0.6
}
.highlightcode {
position:absolute;
z-index:20;
background-color: var(--danger);
}
</style> </style>
`) `)
@ -97,10 +78,14 @@ class Editor {
// Editor Setup // Editor Setup
const el = yo`<div id="input"></div>` const el = yo`<div id="input"></div>`
this.editor = ace.edit(el) this.editor = ace.edit(el)
ace.acequire('ace/ext/language_tools') ace.acequire('ace/ext/language_tools')
// Unmap ctrl-t & ctrl-f // Unmap ctrl-l & cmd-l
this.editor.commands.bindKeys({ 'ctrl-t': null }) this.editor.commands.bindKeys({
'ctrl-L': null,
'Command-L': null
})
// shortcuts for "Ctrl-"" and "Ctrl+"" to increase/decrease font size of the editor // shortcuts for "Ctrl-"" and "Ctrl+"" to increase/decrease font size of the editor
this.editor.commands.addCommand({ this.editor.commands.addCommand({
@ -150,6 +135,13 @@ class Editor {
} }
langTools.addCompleter(flowCompleter) langTools.addCompleter(flowCompleter)
// zoom with Ctrl+wheel
window.addEventListener('wheel', (e) => {
if (e.ctrlKey && Math.abs(e.wheelY) > 5) {
this.editorFontSize(e.wheelY > 0 ? 1 : -1)
}
})
// EVENTS LISTENERS // EVENTS LISTENERS
// Gutter Mouse down // Gutter Mouse down

@ -42,7 +42,7 @@ class SourceHighlighter {
this._deps.fileManager.switchFile(this.source) this._deps.fileManager.switchFile(this.source)
} }
const backgoundClass = style || 'bg-info' const backgoundClass = style || 'alert-info'
const css = csjs` const css = csjs`
.highlightcode { .highlightcode {
position:absolute; position:absolute;

@ -178,7 +178,7 @@ class TxLogger {
}) })
this._deps.txListener.event.register('newBlock', (block) => { this._deps.txListener.event.register('newBlock', (block) => {
if (!block.transactions.length) { if (!block.transactions || block.transactions && !block.transactions.length) {
this.logEmptyBlock({ block: block }) this.logEmptyBlock({ block: block })
} }
}) })

@ -2,12 +2,16 @@
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
class FilesTree extends ApiFactory {
class FilesTree extends BaseApi {
constructor (name, storage) { constructor (name, storage) {
super() super({
name: name,
methods: ['get', 'set', 'remove'],
description:
'service - read/write file to the `config` explorer without need of additionnal permission.'
})
this.event = new EventManager() this.event = new EventManager()
this.storage = storage this.storage = storage
this.type = name this.type = name
@ -15,15 +19,6 @@ class FilesTree extends ApiFactory {
this.tree = {} this.tree = {}
} }
get profile () {
// TODO should make them promisable
return {
name: this.type,
methods: ['get', 'set', 'remove'],
description: 'service - read/write file to the `config` explorer without need of additionnal permission.'
}
}
exists (path, cb) { exists (path, cb) {
cb(null, this._exists(path)) cb(null, this._exists(path))
} }
@ -114,7 +109,11 @@ class FilesTree extends ApiFactory {
if (!this.storage.rename(unprefixedoldPath, unprefixednewPath)) { if (!this.storage.rename(unprefixedoldPath, unprefixednewPath)) {
return false return false
} }
this.event.trigger('fileRenamed', [this.type + '/' + unprefixedoldPath, this.type + '/' + unprefixednewPath, isFolder]) this.event.trigger('fileRenamed', [
this.type + '/' + unprefixedoldPath,
this.type + '/' + unprefixednewPath,
isFolder
])
return true return true
} }
return false return false
@ -122,7 +121,7 @@ class FilesTree extends ApiFactory {
resolveDirectory (path, callback) { resolveDirectory (path, callback) {
if (path[0] === '/') path = path.substring(1) if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [this.type]: { } }) if (!path) return callback(null, { [this.type]: {} })
var tree = {} var tree = {}
path = this.removePrefix(path) path = this.removePrefix(path)
@ -143,7 +142,6 @@ class FilesTree extends ApiFactory {
if (path[0] === '/') return path.substring(1) if (path[0] === '/') return path.substring(1)
return path return path
} }
} }
module.exports = FilesTree module.exports = FilesTree

@ -1,20 +1,31 @@
'use strict' 'use strict'
import { ApiFactory } from 'remix-plugin'
import yo from 'yo-yo' import yo from 'yo-yo'
const EventEmitter = require('events') const EventEmitter = require('events')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var CompilerImport = require('../compiler/compiler-imports') var CompilerImport = require('../compiler/compiler-imports')
var toaster = require('../ui/tooltip') var toaster = require('../ui/tooltip')
import { FileSystemApi } from 'remix-plugin'
/* /*
attach to files event (removed renamed) attach to files event (removed renamed)
trigger: currentFileChanged trigger: currentFileChanged
*/ */
class FileManager extends ApiFactory { const profile = {
name: 'fileManager',
displayName: 'File manager',
description: 'Service - read/write to any files or folders, require giving permissions',
permission: true
}
// File System profile
// - events: ['currentFileChanged']
// - methods: ['getFolder', 'getCurrentFile', 'getFile', 'setFile']
class FileManager extends FileSystemApi {
constructor (localRegistry) { constructor (localRegistry) {
super() super(profile)
this.openedFiles = {} // list all opened files this.openedFiles = {} // list all opened files
this.events = new EventEmitter() this.events = new EventEmitter()
this._components = {} this._components = {}
@ -42,18 +53,6 @@ class FileManager extends ApiFactory {
this._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) }) this._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
} }
get profile () {
return {
displayName: 'file manager',
name: 'fileManager',
methods: ['getFilesFromPath', 'getCurrentFile', 'getFile', 'setFile'],
events: ['currentFileChanged'],
description: 'service - read/write to any files or folders, require giving permissions',
permission: true,
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4='
}
}
fileRenamedEvent (oldName, newName, isFolder) { fileRenamedEvent (oldName, newName, isFolder) {
if (!isFolder) { if (!isFolder) {
this._deps.config.set('currentFile', '') this._deps.config.set('currentFile', '')
@ -223,7 +222,7 @@ class FileManager extends ApiFactory {
} }
} }
getFilesFromPath (path) { getFolder (path) {
// TODO : Change provider with promise // TODO : Change provider with promise
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const provider = this.fileProviderOf(path) const provider = this.fileProviderOf(path)

@ -1,5 +1,5 @@
import { ApiFactory } from 'remix-plugin'
let globalRegistry = require('../../global/registry') let globalRegistry = require('../../global/registry')
import { BaseApi } from 'remix-plugin'
var yo = require('yo-yo') var yo = require('yo-yo')
var modalDialog = require('../ui/modaldialog') var modalDialog = require('../ui/modaldialog')
@ -18,23 +18,21 @@ var css = csjs`
} }
` `
export class RemixdHandle extends ApiFactory { const profile = {
name: 'remixd',
methods: [],
events: [],
description: 'using Remixd daemon, allow to access file system',
kind: 'other'
}
export class RemixdHandle extends BaseApi {
constructor (fileSystemExplorer, locahostProvider) { constructor (fileSystemExplorer, locahostProvider) {
super() super(profile)
this.fileSystemExplorer = fileSystemExplorer this.fileSystemExplorer = fileSystemExplorer
this.locahostProvider = locahostProvider this.locahostProvider = locahostProvider
} }
get profile () {
return {
name: 'remixd',
methods: [],
events: [],
description: 'using Remixd daemon, allow to access file system',
kind: 'other'
}
}
deactivate () { deactivate () {
this.locahostProvider.close((error) => { this.locahostProvider.close((error) => {
if (error) console.log(error) if (error) console.log(error)

@ -100,7 +100,8 @@ class EditorPanel {
self._components.contextView = contextView self._components.contextView = contextView
self._components.terminal = new Terminal({ self._components.terminal = new Terminal({
udapp: self._deps.udapp, udapp: self._deps.udapp,
compilers: {} appStore: self.appStore,
appManager: self.appManager
}, },
{ {
getPosition: (event) => { getPosition: (event) => {
@ -198,7 +199,7 @@ class EditorPanel {
self._adjustLayout('top', self.data._layout.top.offset) self._adjustLayout('top', self.data._layout.top.offset)
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if (e.altKey && e.keyCode === 84) self.tabProxy.switchNextTab() if (e.altKey && e.keyCode === 84) self.tabProxy.switchNextTab() // alt + t
}) })
return self._view.el return self._view.el

@ -6,7 +6,7 @@ var { RemixdHandle } = require('../files/remixd-handle.js')
var globalRegistry = require('../../global/registry') var globalRegistry = require('../../global/registry')
var css = require('./styles/file-panel-styles') var css = require('./styles/file-panel-styles')
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
var canUpload = window.File || window.FileReader || window.FileList || window.Blob var canUpload = window.File || window.FileReader || window.FileList || window.Blob
@ -27,10 +27,21 @@ var canUpload = window.File || window.FileReader || window.FileList || window.Bl
- call fileProvider API - call fileProvider API
*/ */
module.exports = class Filepanel extends ApiFactory { const profile = {
name: 'fileExplorers',
displayName: 'File explorers',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=',
description: ' - ',
kind: 'fileexplorer',
location: 'swapPanel'
}
module.exports = class Filepanel extends BaseApi {
constructor (localRegistry) { constructor (localRegistry) {
super() super(profile)
var self = this var self = this
self._components = {} self._components = {}
self._components.registry = localRegistry || globalRegistry self._components.registry = localRegistry || globalRegistry
@ -132,18 +143,5 @@ module.exports = class Filepanel extends ApiFactory {
self.render = function render () { return element } self.render = function render () { return element }
} }
get profile () {
return {
name: 'fileExplorers',
displayName: 'file explorers',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4=',
description: ' - ',
kind: 'fileexplorer',
location: 'swapPanel'
}
}
} }

@ -13,6 +13,8 @@ var css = csjs`
flex-direction : column; flex-direction : column;
position : relative; position : relative;
width : 100%; width : 100%;
padding-left : 6px;
padding-top : 6px;
} }
.gist { .gist {
padding : 10px; padding : 10px;
@ -50,7 +52,7 @@ var css = csjs`
.uploadFile label { .uploadFile label {
cursor : pointer; cursor : pointer;
} }
.treeviews { .treeview {
overflow-y : auto; overflow-y : auto;
} }
.dialog { .dialog {

@ -12,7 +12,6 @@ var css = csjs`
} }
.bar { .bar {
display : flex; display : flex;
min-height : 3em;
z-index : 3; z-index : 3;
} }
.menu { .menu {
@ -34,7 +33,6 @@ var css = csjs`
.toggleTerminal { .toggleTerminal {
margin-right : 20px; margin-right : 20px;
margin-left : 2px; margin-left : 2px;
margin-top : 2px;
font-size : 14px; font-size : 14px;
font-weight : bold; font-weight : bold;
cursor : pointer; cursor : pointer;
@ -129,7 +127,6 @@ var css = csjs`
height : 65%; height : 65%;
} }
.pendingTx { .pendingTx {
border : 1px solid var(--secondary);
border-radius : 50%; border-radius : 50%;
margin-right : 30px; margin-right : 30px;
min-width : 13px; min-width : 13px;
@ -138,6 +135,7 @@ var css = csjs`
justify-content : center; justify-content : center;
align-items : center; align-items : center;
font-size : 14px; font-size : 14px;
user-select : none;
} }
.dragbarHorizontal { .dragbarHorizontal {
position : absolute; position : absolute;

@ -27,7 +27,7 @@ export class TabProxy {
this._view.filetabs.activateTab(file) this._view.filetabs.activateTab(file)
return return
} }
this.addTab(file, () => { this.addTab(file, '', () => {
this.fileManager.switchFile(file) this.fileManager.switchFile(file)
this.event.emit('switchFile', file) this.event.emit('switchFile', file)
}, },
@ -39,7 +39,7 @@ export class TabProxy {
fileManager.events.on('fileRenamed', (oldName, newName) => { fileManager.events.on('fileRenamed', (oldName, newName) => {
this.removeTab(oldName) this.removeTab(oldName)
this.addTab(newName, () => { this.addTab(newName, '', () => {
this.fileManager.switchFile(newName) this.fileManager.switchFile(newName)
this.event.emit('switchFile', newName) this.event.emit('switchFile', newName)
}, },
@ -54,6 +54,7 @@ export class TabProxy {
if (profile.location === 'mainPanel') { if (profile.location === 'mainPanel') {
this.addTab( this.addTab(
name, name,
profile.displayName,
() => this.event.emit('switchApp', name), () => this.event.emit('switchApp', name),
() => { () => {
this.event.emit('closeApp', name) this.event.emit('closeApp', name)
@ -104,9 +105,13 @@ export class TabProxy {
this._view.filetabs.activateTab(name) this._view.filetabs.activateTab(name)
} }
addTab (name, switchTo, close, kind) { addTab (name, title, switchTo, close, kind) {
if (this._handlers[name]) return
var slash = name.split('/') var slash = name.split('/')
let title = name.indexOf('/') !== -1 ? slash[slash.length - 1] : name if (!title) {
title = name.indexOf('/') !== -1 ? slash[slash.length - 1] : name
}
this._view.filetabs.addTab({ this._view.filetabs.addTab({
id: name, id: name,
title, title,
@ -117,7 +122,7 @@ export class TabProxy {
} }
removeTab (name) { removeTab (name) {
this._view.filetabs.closeTab(name) this._view.filetabs.removeTab(name)
delete this._handlers[name] delete this._handlers[name]
} }

@ -17,6 +17,7 @@ var AutoCompletePopup = require('../ui/auto-complete-popup')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var css = require('./styles/terminal-styles') var css = require('./styles/terminal-styles')
import { BaseApi } from 'remix-plugin'
var packageV = require('../../../package.json') var packageV = require('../../../package.json')
@ -26,8 +27,18 @@ function register (api) { KONSOLES.push(api) }
var ghostbar = yo`<div class=${css.ghostbar} bg-secondary></div>` var ghostbar = yo`<div class=${css.ghostbar} bg-secondary></div>`
class Terminal { const profile = {
displayName: 'Terminal',
name: 'terminal',
methods: [],
events: [],
description: ' - ',
required: false
}
class Terminal extends BaseApi {
constructor (opts, api) { constructor (opts, api) {
super(profile)
var self = this var self = this
self.event = new EventManager() self.event = new EventManager()
self._api = api self._api = api
@ -62,12 +73,12 @@ class Terminal {
self.updateJournal({ type: 'select', value: label }) self.updateJournal({ type: 'select', value: label })
} }
}) })
self._components.autoCompletePopup = new AutoCompletePopup() self._components.autoCompletePopup = new AutoCompletePopup(self._opts)
self._components.autoCompletePopup.event.register('handleSelect', function (input) { self._components.autoCompletePopup.event.register('handleSelect', function (input) {
let textList = self._view.input.innerText.split(' ') let textList = self._view.input.innerText.split(' ')
textList.pop() textList.pop()
textList.push(input) textList.push(input)
self._view.input.innerText = `${textList}`.replace(/,/g, ' ') self._view.input.innerText = textList
self._view.input.focus() self._view.input.focus()
self.putCursor2End(self._view.input) self.putCursor2End(self._view.input)
}) })
@ -103,14 +114,18 @@ class Terminal {
self._jsSandboxContext = {} self._jsSandboxContext = {}
self._jsSandboxRegistered = {} self._jsSandboxRegistered = {}
self.externalApi = this.api()
self.externalApi.notifs = {'theme': ['switchTheme']}
opts.appManager.init([self.externalApi])
opts.appManager.activateRequestAndNotification(self.externalApi)
if (opts.shell) self._shell = opts.shell if (opts.shell) self._shell = opts.shell
register(self) register(self)
} }
focus () { focus () {
if (this._view.input) this._view.input.focus() if (this._view.input) this._view.input.focus()
} }
render () { render () {
var self = this var self = this
if (self._view.el) return self._view.el if (self._view.el) return self._view.el
@ -127,7 +142,7 @@ class Terminal {
` `
self._view.icon = yo` self._view.icon = yo`
<i onmouseenter=${hover} onmouseleave=${hover} onmousedown=${minimize} <i onmouseenter=${hover} onmouseleave=${hover} onmousedown=${minimize}
class="btn btn-secondary align-items-center ${css.toggleTerminal} fa fa-angle-double-down"></i>` class="btn btn-secondary btn-sm align-items-center ${css.toggleTerminal} fa fa-angle-double-down"></i>`
self._view.dragbar = yo` self._view.dragbar = yo`
<div onmousedown=${mousedown} class=${css.dragbarHorizontal}></div>` <div onmousedown=${mousedown} class=${css.dragbarHorizontal}></div>`
self._view.dropdown = self._components.dropdown.render() self._view.dropdown = self._components.dropdown.render()
@ -136,7 +151,7 @@ class Terminal {
self._view.bar = yo` self._view.bar = yo`
<div class="${css.bar}"> <div class="${css.bar}">
${self._view.dragbar} ${self._view.dragbar}
<div class="${css.menu} bg-light"> <div class="${css.menu} border-top bg-light">
${self._view.icon} ${self._view.icon}
<div class=${css.clear} onclick=${clear}> <div class=${css.clear} onclick=${clear}>
<i class="fa fa-ban" aria-hidden="true" title="Clear console" <i class="fa fa-ban" aria-hidden="true" title="Clear console"
@ -649,7 +664,6 @@ class Terminal {
function domTerminalFeatures (self, scopedCommands) { function domTerminalFeatures (self, scopedCommands) {
return { return {
compilers: self._opts.compilers,
swarmgw, swarmgw,
ethers, ethers,
remix: self._components.cmdInterpreter, remix: self._components.cmdInterpreter,

@ -1,94 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
}
}return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var RemixExtension = function () {
function RemixExtension() {
var _this = this;
_classCallCheck(this, RemixExtension);
this._notifications = {};
this._pendingRequests = {};
this._id = 0;
window.addEventListener('message', function (event) {
return _this._newMessage(event);
}, false);
}
_createClass(RemixExtension, [{
key: 'listen',
value: function listen(key, type, callback) {
if (!this._notifications[key]) this._notifications[key] = {};
this._notifications[key][type] = callback;
}
}, {
key: 'call',
value: function call(key, type, params, callback) {
this._id++;
this._pendingRequests[this._id] = callback;
window.parent.postMessage(JSON.stringify({
action: 'request',
key: key,
type: type,
value: params,
id: this._id
}), '*');
}
}, {
key: '_newMessage',
value: function _newMessage(event) {
if (!event.data) return;
if (typeof event.data !== 'string') return;
var msg;
try {
msg = JSON.parse(event.data);
} catch (e) {
return console.log('unable to parse data');
}
var _msg = msg,
action = _msg.action,
key = _msg.key,
type = _msg.type,
value = _msg.value;
if (action === 'notification') {
if (this._notifications[key] && this._notifications[key][type]) {
this._notifications[key][type](value);
}
} else if (action === 'response') {
var _msg2 = msg,
id = _msg2.id,
error = _msg2.error;
if (this._pendingRequests[id]) {
this._pendingRequests[id](error, value);
delete this._pendingRequests[id];
}
}
}
}]);
return RemixExtension;
}();
if (window) window.RemixExtension = RemixExtension;
if (module && module.exports) module.exports = RemixExtension;
},{}]},{},[1]);

@ -1,55 +0,0 @@
'use strict'
class RemixExtension {
constructor () {
this._notifications = {}
this._pendingRequests = {}
this._id = 0
window.addEventListener('message', (event) => this._newMessage(event), false)
}
listen (key, type, callback) {
if (!this._notifications[key]) this._notifications[key] = {}
this._notifications[key][type] = callback
}
call (key, type, params, callback) {
this._id++
this._pendingRequests[this._id] = callback
window.parent.postMessage(JSON.stringify({
action: 'request',
key,
type,
value: params,
id: this._id
}), '*')
}
_newMessage (event) {
if (!event.data) return
if (typeof event.data !== 'string') return
var msg
try {
msg = JSON.parse(event.data)
} catch (e) {
return console.log('unable to parse data')
}
const {action, key, type, value} = msg
if (action === 'notification') {
if (this._notifications[key] && this._notifications[key][type]) {
this._notifications[key][type](value)
}
} else if (action === 'response') {
const {id, error} = msg
if (this._pendingRequests[id]) {
this._pendingRequests[id](error, value)
delete this._pendingRequests[id]
}
}
}
}
if (window) window.RemixExtension = RemixExtension
if (module && module.exports) module.exports = RemixExtension

@ -1,71 +0,0 @@
{
"name": "remix-extension",
"version": "0.0.1",
"description": "Ethereum IDE and tools for the web",
"contributors": [
{
"name": "Yann Levreau",
"email": "yann@ethdev.com"
}
],
"main": "./index.js",
"dependencies": {
"babel-eslint": "^7.1.1",
"babel-plugin-transform-object-assign": "^6.22.0",
"babel-preset-es2015": "^6.24.0",
"babelify": "^7.3.0",
"standard": "^7.0.1",
"tape": "^4.6.0"
},
"scripts": {
"browserify": "browserify index.js -o bundle.js"
},
"standard": {
"ignore": [
"node_modules/*"
],
"parser": "babel-eslint"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ethereum/remix-ide.git"
},
"author": "cpp-ethereum team",
"license": "MIT",
"bugs": {
"url": "https://github.com/ethereum/remix-ide/issues"
},
"homepage": "https://github.com/ethereum/remix-ide#readme",
"browserify": {
"transform": [
[
"babelify",
{
"plugins": [
[
"fast-async",
{
"runtimePatten": null,
"compiler": {
"promises": true,
"es7": true,
"noRuntime": true,
"wrapAwait": true
}
}
],
"transform-object-assign"
]
}
],
[
"babelify",
{
"presets": [
"es2015"
]
}
]
]
}
}

@ -1,53 +0,0 @@
plugin api
# current APIs:
## 1) notifications
### app (key: app)
- unfocus `[]`
- focus `[]`
### compiler (key: compiler)
- compilationFinished `[success (bool), data (obj), source (obj)]`
- compilationData `[compilationResult]`
### transaction listener (key: txlistener)
- newTransaction `tx (obj)`
## 2) interactions
### app
- getExecutionContextProvider `@return {String} provider (injected | web3 | vm)`
- getProviderEndpoint `@return {String} url`
- updateTitle `@param {String} title`
- detectNetWork `@return {Object} {name, id}`
- addProvider `@param {String} name, @param {String} url`
- removeProvider `@return {String} name`
### config
- setConfig `@param {String} path, @param {String} content`
- getConfig `@param {String} path`
- removeConfig `@param {String} path`
### compiler
- getCompilationResult `@return {Object} compilation result`
### udapp (only VM)
- runTx `@param {Object} tx`
- getAccounts `@return {Array} acccounts`
- createVMAccount `@param {String} privateKey, @param {String} balance (hex)`
### editor
- getFilesFromPath `@param {Array} [path]`
- getCurrentFile `@return {String} path`
- getFile `@param {String} path`
- setFile `@param {String} path, @param {String} content`
- highlight `@param {Object} lineColumnPos {start: {line, column}, end: {line, column}}, @param {String} path, @param {String} hexColor`
- discardHighlight

@ -1,150 +0,0 @@
'use strict'
var executionContext = require('../../execution-context')
var SourceHighlighter = require('../editor/sourceHighlighter')
/*
Defines available API. `key` / `type`
*/
module.exports = (pluginManager, fileProviders, fileManager, compilesrArtefacts, udapp) => {
let highlighters = {}
return {
app: {
getExecutionContextProvider: (mod, cb) => {
cb(null, executionContext.getProvider())
},
getProviderEndpoint: (mod, cb) => {
if (executionContext.getProvider() === 'web3') {
cb(null, executionContext.web3().currentProvider.host)
} else {
cb('no endpoint: current provider is either injected or vm')
}
},
updateTitle: (mod, title, cb) => {
pluginManager.plugins[mod].modal.setTitle(title)
if (cb) cb()
},
detectNetWork: (mod, cb) => {
executionContext.detectNetwork((error, network) => {
cb(error, network)
})
},
addProvider: (mod, name, url, cb) => {
executionContext.addProvider({ name, url })
cb()
},
removeProvider: (mod, name, cb) => {
executionContext.removeProvider(name)
cb()
}
},
config: {
setConfig: (mod, path, content, cb) => {
fileProviders['config'].set(mod + '/' + path, content)
cb()
},
getConfig: (mod, path, cb) => {
cb(null, fileProviders['config'].get(mod + '/' + path))
},
removeConfig: (mod, path, cb) => {
cb(null, fileProviders['config'].remove(mod + '/' + path))
if (cb) cb()
}
},
compiler: {
getCompilationResult: (mod, cb) => {
cb(null, compilesrArtefacts['__last'])
},
sendCompilationResult: (mod, file, source, languageVersion, data, cb) => {
pluginManager.receivedDataFrom('sendCompilationResult', mod, [file, source, languageVersion, data])
}
},
udapp: {
runTx: (mod, tx, cb) => {
executionContext.detectNetwork((error, network) => {
if (error) return cb(error)
if (network.name === 'Main' && network.id === '1') {
return cb('It is not allowed to make this action against mainnet')
}
udapp.silentRunTx(tx, (error, result) => {
if (error) return cb(error)
cb(null, {
transactionHash: result.transactionHash,
status: result.result.status,
gasUsed: '0x' + result.result.gasUsed.toString('hex'),
error: result.result.vm.exceptionError,
return: result.result.vm.return ? '0x' + result.result.vm.return.toString('hex') : '0x',
createdAddress: result.result.createdAddress ? '0x' + result.result.createdAddress.toString('hex') : undefined
})
})
})
},
getAccounts: (mod, cb) => {
executionContext.detectNetwork((error, network) => {
if (error) return cb(error)
if (network.name === 'Main' && network.id === '1') {
return cb('It is not allowed to make this action against mainnet')
}
udapp.getAccounts(cb)
})
},
createVMAccount: (mod, privateKey, balance, cb) => {
if (executionContext.getProvider() !== 'vm') return cb('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed')
udapp.createVMAccount(privateKey, balance, (error, address) => {
cb(error, address)
})
}
},
editor: {
getFilesFromPath: (mod, path, cb) => {
fileManager.filesFromPath(path, cb)
},
getCurrentFile: (mod, cb) => {
var path = fileManager.currentFile()
if (!path) {
cb('no file selected')
} else {
cb(null, path)
}
},
getFile: (mod, path, cb) => {
var provider = fileManager.fileProviderOf(path)
if (provider) {
// TODO add approval to user for external plugin to get the content of the given `path`
provider.get(path, (error, content) => {
cb(error, content)
})
} else {
cb(path + ' not available')
}
},
setFile: (mod, path, content, cb) => {
var provider = fileManager.fileProviderOf(path)
if (provider) {
// TODO add approval to user for external plugin to set the content of the given `path`
provider.set(path, content, (error) => {
if (error) return cb(error)
fileManager.syncEditor(path)
cb()
})
} else {
cb(path + ' not available')
}
},
highlight: (mod, lineColumnPos, filePath, hexColor, cb) => {
var position
try {
position = JSON.parse(lineColumnPos)
} catch (e) {
return cb(e.message)
}
if (!highlighters[mod]) highlighters[mod] = new SourceHighlighter()
highlighters[mod].currentSourceLocation(null)
highlighters[mod].currentSourceLocationFromfileName(position, filePath, hexColor)
cb()
},
discardHighlight: (mod, cb) => {
if (highlighters[mod]) highlighters[mod].currentSourceLocation(null)
cb()
}
}
}
}

@ -1,179 +0,0 @@
'use strict'
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
const PluginAPI = require('./pluginAPI')
/**
* Register and Manage plugin:
*
* Plugin registration is done in the settings tab,
* using the following format:
* {
* "title": "<plugin name>",
* "url": "<plugin url>"
* }
*
* structure of messages:
*
* - Notification sent by Remix:
*{
* action: 'notification',
* key: <string>,
* type: <string>,
* value: <array>
*}
*
* - Request sent by the plugin:
*{
* id: <number>,
* action: 'request',
* key: <string>,
* type: <string>,
* value: <array>
*}
*
* - Response sent by Remix and receive by the plugin:
*{
* id: <number>,
* action: 'response',
* key: <string>,
* type: <string>,
* value: <array>,
* error: (see below)
*}
* => The `error` property is `undefined` if no error happened.
* => In case of error (due to permission, system error, API error, etc...):
* error: { code, msg (optional), data (optional), stack (optional)
* => possible error code are still to be defined, but the generic one would be 500.
*
* Plugin receive 4 types of message:
* - focus (when he get focus)
* - unfocus (when he loose focus - is hidden)
* - compilationData (that is triggered just after a focus - and send the current compilation data or null)
* - compilationFinished (that is only sent to the plugin that has focus)
*
* Plugin can emit messages and receive response.
*
* CONFIG:
* - getConfig(filename). The data to send should be formatted like:
* {
* id: <requestid>,
* action: 'request',
* key: 'config',
* type: 'getConfig',
* value: ['filename.ext']
* }
* the plugin will reveice a response like:
* {
* id: <requestid>,
* action: 'response',
* key: 'config',
* type: 'getConfig',
* error,
* value: ['content of filename.ext']
* }
* same apply for the other call
* - setConfig(filename, content)
* - removeConfig
*
* See index.html and remix.js in test-browser folder for sample
*
*/
module.exports = class PluginManager {
constructor (app, compilersArtefacts, txlistener, fileProviders, fileManager, udapp) {
const self = this
self.event = new EventManager()
var pluginAPI = new PluginAPI(
this,
fileProviders,
fileManager,
compilersArtefacts,
udapp
)
self._components = { pluginAPI }
self.plugins = {}
self.origins = {}
self.inFocus
fileManager.events.on('currentFileChanged', (file) => {
self.broadcast(JSON.stringify({
action: 'notification',
key: 'editor',
type: 'currentFileChanged',
value: [ file ]
}))
})
txlistener.event.register('newTransaction', (tx) => {
self.broadcast(JSON.stringify({
action: 'notification',
key: 'txlistener',
type: 'newTransaction',
value: [tx]
}))
})
window.addEventListener('message', (event) => {
if (event.type !== 'message') return
var extension = self.origins[event.origin]
if (!extension) return
function response (key, type, callid, error, result) {
self.postToOrigin(event.origin, JSON.stringify({
id: callid,
action: 'response',
key: key,
type: type,
error: error,
value: [ result ]
}))
}
var data = JSON.parse(event.data)
data.value.unshift(extension)
data.value.push((error, result) => {
response(data.key, data.type, data.id, error, result)
})
if (pluginAPI[data.key] && pluginAPI[data.key][data.type]) {
pluginAPI[data.key][data.type].apply({}, data.value)
} else {
response(data.key, data.type, data.id, `Endpoint ${data.key}/${data.type} not present`, null)
}
}, false)
}
unregister (desc) {
const self = this
self._components.pluginAPI.editor.discardHighlight(desc.title, () => {})
delete self.plugins[desc.title]
delete self.origins[desc.url]
}
register (desc, modal, content) {
const self = this
self.plugins[desc.title] = { content, modal, origin: desc.url }
self.origins[desc.url] = desc.title
}
broadcast (value) {
for (var plugin in this.plugins) {
this.post(plugin, value)
}
}
postToOrigin (origin, value) {
if (this.origins[origin]) {
this.post(this.origins[origin], value)
}
}
receivedDataFrom (methodName, mod, argumentsArray) {
// TODO check whether 'mod' as right to do that
console.log(argumentsArray)
this.event.trigger(methodName, argumentsArray) // forward to internal modules
this.broadcast(JSON.stringify({ // forward to plugins
action: 'notification',
key: mod,
type: methodName,
value: argumentsArray
}))
}
post (name, value) {
const self = this
if (self.plugins[name]) {
self.plugins[name].content.querySelector('iframe').contentWindow.postMessage(value, self.plugins[name].origin)
}
}
}

@ -1,38 +0,0 @@
'use strict'
module.exports = {
'oraclize': {
url: 'https://remix-plugin.oraclize.it',
title: 'Oraclize'
},
'solium': {
url: 'https://two-water.surge.sh',
title: 'Solium'
},
'ethdoc': {
url: 'https://30400.swarm-gateways.net/bzz:/ethdoc.remixide.eth',
title: 'Ethdoc'
},
'openzeppelin snippet': {
url: 'https://left-edge.surge.sh',
title: 'Openzeppelin snippet'
},
'vyper': {
url: 'https://plugin.vyper.live',
title: 'Vyper'
},
'slither/mythril': {
url: 'http://jittery-space.surge.sh',
title: 'Slither/Mythril'
},
'pipeline': {
url: 'https://pipeline.pipeos.one',
title: 'Pipeline'
}
/*
'etherscan-general': {
url: 'http://127.0.0.1:8081',
title: 'Etherscan-general'
}
*/
}

@ -8,7 +8,6 @@ var css = csjs`
.result { .result {
margin-top: 1%; margin-top: 1%;
max-height: 300px; max-height: 300px;
overflow-y: auto;
} }
.buttons { .buttons {
margin: 1rem 0; margin: 1rem 0;

@ -3,31 +3,29 @@ var StaticAnalysis = require('../staticanalysis/staticAnalysisView')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var css = require('./styles/analysis-tab-styles') var css = require('./styles/analysis-tab-styles')
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
class AnalysisTab extends ApiFactory { const profile = {
name: 'solidityStaticAnalysis',
displayName: 'Solidity static analysis',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMjA0OCIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMjA0OCAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0yMDQ4IDE1MzZ2MTI4aC0yMDQ4di0xNTM2aDEyOHYxNDA4aDE5MjB6bS0xMjgtMTI0OHY0MzVxMCAyMS0xOS41IDI5LjV0LTM1LjUtNy41bC0xMjEtMTIxLTYzMyA2MzNxLTEwIDEwLTIzIDEwdC0yMy0xMGwtMjMzLTIzMy00MTYgNDE2LTE5Mi0xOTIgNTg1LTU4NXExMC0xMCAyMy0xMHQyMyAxMGwyMzMgMjMzIDQ2NC00NjQtMTIxLTEyMXEtMTYtMTYtNy41LTM1LjV0MjkuNS0xOS41aDQzNXExNCAwIDIzIDl0OSAyM3oiLz48L3N2Zz4=',
description: 'Checks the contract code for security vulnerabilities and bad practices.',
kind: 'analysis',
location: 'swapPanel'
}
class AnalysisTab extends BaseApi {
constructor (registry) { constructor (registry) {
super() super(profile)
this.event = new EventManager() this.event = new EventManager()
this.events = new EventEmitter() this.events = new EventEmitter()
this.registry = registry this.registry = registry
} }
get profile () {
return {
name: 'solidityStaticAnalysis',
displayName: 'solidity static analysis',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMjA0OCIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMjA0OCAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0yMDQ4IDE1MzZ2MTI4aC0yMDQ4di0xNTM2aDEyOHYxNDA4aDE5MjB6bS0xMjgtMTI0OHY0MzVxMCAyMS0xOS41IDI5LjV0LTM1LjUtNy41bC0xMjEtMTIxLTYzMyA2MzNxLTEwIDEwLTIzIDEwdC0yMy0xMGwtMjMzLTIzMy00MTYgNDE2LTE5Mi0xOTIgNTg1LTU4NXExMC0xMCAyMy0xMHQyMyAxMGwyMzMgMjMzIDQ2NC00NjQtMTIxLTEyMXEtMTYtMTYtNy41LTM1LjV0MjkuNS0xOS41aDQzNXExNCAwIDIzIDl0OSAyM3oiLz48L3N2Zz4=',
description: ' - ',
kind: 'analysis',
location: 'swapPanel'
}
}
render () { render () {
var staticanalysis = new StaticAnalysis() var staticanalysis = new StaticAnalysis()
staticanalysis.event.register('staticAnaysisWarning', (count) => { staticanalysis.event.register('staticAnaysisWarning', (count) => {

@ -17,12 +17,26 @@ var css = require('./styles/compile-tab-styles')
const CompileTabLogic = require('./compileTab/compileTab.js') const CompileTabLogic = require('./compileTab/compileTab.js')
const CompilerContainer = require('./compileTab/compilerContainer.js') const CompilerContainer = require('./compileTab/compilerContainer.js')
import { ApiFactory } from 'remix-plugin' import { CompilerApi } from 'remix-plugin'
const profile = {
name: 'solidity',
displayName: 'Solidity compiler',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAASdAAAEnQF8NGuhAAAAB3RJTUUH4wQMDx84DVYryQAAAjBJREFUSMe1102ITmEUB/DfO4aZRuM7otQgqSlCFmIhSRaSJClWs9BY2RAba1tLZTGxmbJWImFsWBhhZONzpJSEaJhhPl6bM/V2ux/vnTtz6untPvc857z/8/E/z61pXnZjHeoFejcwUWSs1qTTFjzHOP7l2PqCAVxuxmAzcgbLsSQH8SQ+YxM6igzOa8LpMvSH4dYI43iK3nDs17AND6oiPt+Qs3qgTso4fjU8r0Z3Fcfd6E0505nYe52olyn0VAn1FaxM2W/HSETgN76l6HREet6URbwPe3LeLw5k73OK7UDZdmrBIJYWROse3hak8gluJ1+0ZhyYwlNsLyCMHjOUvFCfij+Q19vmwjFcy9D5gUdVHDdDmY8xP3HmULDUnCGGswmnL3Oc7sBxsygDUeVDaMvR68fDnKItPSROBNo+/M3QOYwtwWq9s4n6ajBWltyJqAziVQbjlZpO03IzZ8CfDpab7vmJGKP3q14E8mQR7qaAaMdJvKiS4zw5lxG5MVyoWlxZshFHI8RpazP2lgl1DRdjAmVxdR070VVAUIM4Uqa4PuFg6LSlrFVRQJ3hIG2NY1fZUH/Asxy0a+L3a07H9M20nYZjmE8mnK5omNWTWJgCYhTHZup4BAuwPjHDNyT0/iTuYbXo7XdVqvpWA/fWI7dpF4exhufvwWSVmGv66ro10HdlVPpokEkd+/FzNobEQKBY23AuuWpx4xzCxyKDrSXI4nrkPO+DrA2XmjH2H8KUd4MWwdIJAAAAAElFTkSuQmCC',
description: 'Compile solidity contracts',
kind: 'compile',
permission: true,
location: 'swapPanel'
}
// EditorApi:
// - events: ['compilationFinished'],
// - methods: ['getCompilationResult']
class CompileTab extends ApiFactory { class CompileTab extends CompilerApi {
constructor (editor, config, renderer, swarmfileProvider, fileManager, fileProviders, pluginManager) { constructor (editor, config, renderer, swarmfileProvider, fileManager, fileProviders, pluginManager) {
super() super(profile)
this.events = new EventEmitter() this.events = new EventEmitter()
this._view = { this._view = {
el: null, el: null,
@ -57,20 +71,6 @@ class CompileTab extends ApiFactory {
) )
} }
get profile () {
return {
displayName: 'solidity compiler',
name: 'solidity',
methods: ['getCompilationResult'],
events: ['compilationFinished'],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMywgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zOnNrZXRjaD0iaHR0cDovL3d3dy5ib2hlbWlhbmNvZGluZy5jb20vc2tldGNoL25zIgoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTMwMHB4IiBoZWlnaHQ9IjEzMDBweCIKCSB2aWV3Qm94PSIwIDAgMTMwMCAxMzAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMzAwIDEzMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8dGl0bGU+VmVjdG9yIDE8L3RpdGxlPgo8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KPGcgaWQ9IlBhZ2UtMSIgc2tldGNoOnR5cGU9Ik1TUGFnZSI+Cgk8ZyBpZD0ic29saWRpdHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQwMi4wMDAwMDAsIDExOC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNMYXllckdyb3VwIj4KCQk8ZyBpZD0iR3JvdXAiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPgoJCQk8cGF0aCBpZD0iU2hhcGUiIG9wYWNpdHk9IjAuNDUiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNMzcxLjc3MiwxMzUuMzA4TDI0MS4wNjgsMzY3LjYxSC0yMC4xNThsMTMwLjYxNC0yMzIuMzAyCgkJCQlIMzcxLjc3MiIvPgoJCQk8cGF0aCBpZD0iU2hhcGVfMV8iIG9wYWNpdHk9IjAuNiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0yNDEuMDY4LDM2Ny42MWgyNjEuMzE4TDM3MS43NzIsMTM1LjMwOEgxMTAuNDU2CgkJCQlMMjQxLjA2OCwzNjcuNjF6Ii8+CgkJCTxwYXRoIGlkPSJTaGFwZV8yXyIgb3BhY2l0eT0iMC44IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3ICAgICIgZD0iTTExMC40NTYsNTk5LjgyMkwyNDEuMDY4LDM2Ny42MUwxMTAuNDU2LDEzNS4zMDgKCQkJCUwtMjAuMTU4LDM2Ny42MUwxMTAuNDU2LDU5OS44MjJ6Ii8+CgkJCTxwYXRoIGlkPSJTaGFwZV8zXyIgb3BhY2l0eT0iMC40NSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0xMTEuNzIxLDk0OC4yNzVsMTMwLjcwNC0yMzIuMzAzaDI2MS4zMThMMzczLjAzOCw5NDguMjc1CgkJCQlIMTExLjcyMSIvPgoJCQk8cGF0aCBpZD0iU2hhcGVfNF8iIG9wYWNpdHk9IjAuNiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0yNDIuNDI0LDcxNS45NzNILTE4Ljg5M2wxMzAuNjEzLDIzMi4zMDNoMjYxLjMxNwoJCQkJTDI0Mi40MjQsNzE1Ljk3M3oiLz4KCQkJPHBhdGggaWQ9IlNoYXBlXzVfIiBvcGFjaXR5PSIwLjgiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNMzczLjAzOCw0ODMuNzYxTDI0Mi40MjQsNzE1Ljk3M2wxMzAuNjE0LDIzMi4zMDMKCQkJCWwxMzAuNzA0LTIzMi4zMDNMMzczLjAzOCw0ODMuNzYxeiIvPgoJCTwvZz4KCTwvZz4KPC9nPgo8L3N2Zz4K',
description: 'compile solidity contracts',
kind: 'compile',
permission: true,
location: 'swapPanel'
}
}
/************ /************
* EVENTS * EVENTS
*/ */
@ -166,7 +166,7 @@ class CompileTab extends ApiFactory {
<!-- Select Compiler Version --> <!-- Select Compiler Version -->
<header class="navbar navbar-light bg-light input-group mb-3 ${css.compilerArticle}"> <header class="navbar navbar-light bg-light input-group mb-3 ${css.compilerArticle}">
<div class="input-group-prepend"> <div class="input-group-prepend">
<label class="input-group-text" for="compiledContracts">Contract</label> <label class="border-0 input-group-text" for="compiledContracts">Contract</label>
</div> </div>
<select onchange="${e => this.selectContract(e.target.value)}" onload="${e => { this.selectedContract = e.value }}" id="compiledContracts" class="custom-select"> <select onchange="${e => this.selectContract(e.target.value)}" onload="${e => { this.selectedContract = e.value }}" id="compiledContracts" class="custom-select">
${contractList.map((name) => yo`<option value="${name}">${name}</option>`)} ${contractList.map((name) => yo`<option value="${name}">${name}</option>`)}
@ -181,11 +181,8 @@ class CompileTab extends ApiFactory {
Compilation Details Compilation Details
</button> </button>
<!-- Copy to Clipboard --> <!-- Copy to Clipboard -->
<div class="${css.contractHelperButtons}"> <div class="${css.contractHelperButtons} btn-secondary">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Copy to clipboard</span>
</div>
<div class="btn-group" role="group" aria-label="Copy to Clipboard"> <div class="btn-group" role="group" aria-label="Copy to Clipboard">
<button class="btn btn-secondary" title="Copy ABI to clipboard" onclick="${() => { this.copyABI() }}"> <button class="btn btn-secondary" title="Copy ABI to clipboard" onclick="${() => { this.copyABI() }}">
<i class="${css.copyIcon} fa fa-clipboard" aria-hidden="true"></i> <i class="${css.copyIcon} fa fa-clipboard" aria-hidden="true"></i>

@ -141,7 +141,7 @@ class CompilerContainer {
<article> <article>
<header class="navbar navbar-light bg-light input-group mb-3"> <header class="navbar navbar-light bg-light input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<label class="input-group-text" for="versionSelector">Compiler</label> <label class="input-group-text border-0" for="versionSelector">Compiler</label>
</div> </div>
${this._view.versionSelector} ${this._view.versionSelector}
</header> </header>

@ -3,28 +3,26 @@ var css = require('./styles/debugger-tab-styles')
var DebuggerUI = require('../debugger/debuggerUI') var DebuggerUI = require('../debugger/debuggerUI')
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
const profile = {
name: 'debugger',
displayName: 'Debugger',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDk2MHEwIDI2LTE5IDQ1dC00NSAxOWgtMjI0cTAgMTcxLTY3IDI5MGwyMDggMjA5cTE5IDE5IDE5IDQ1dC0xOSA0NXEtMTggMTktNDUgMTl0LTQ1LTE5bC0xOTgtMTk3cS01IDUtMTUgMTN0LTQyIDI4LjUtNjUgMzYuNS04MiAyOS05NyAxM3YtODk2aC0xMjh2ODk2cS01MSAwLTEwMS41LTEzLjV0LTg3LTMzLTY2LTM5LTQzLjUtMzIuNWwtMTUtMTQtMTgzIDIwN3EtMjAgMjEtNDggMjEtMjQgMC00My0xNi0xOS0xOC0yMC41LTQ0LjV0MTUuNS00Ni41bDIwMi0yMjdxLTU4LTExNC01OC0yNzRoLTIyNHEtMjYgMC00NS0xOXQtMTktNDUgMTktNDUgNDUtMTloMjI0di0yOTRsLTE3My0xNzNxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5IDQ1IDE5bDE3MyAxNzNoODQ0bDE3My0xNzNxMTktMTkgNDUtMTl0NDUgMTkgMTkgNDUtMTkgNDVsLTE3MyAxNzN2Mjk0aDIyNHEyNiAwIDQ1IDE5dDE5IDQ1em0tNDgwLTU3NmgtNjQwcTAtMTMzIDkzLjUtMjI2LjV0MjI2LjUtOTMuNSAyMjYuNSA5My41IDkzLjUgMjI2LjV6Ii8+PC9zdmc+',
description: 'Debug transactions',
kind: 'debugging',
location: 'swapPanel'
}
class DebuggerTab extends ApiFactory { class DebuggerTab extends BaseApi {
constructor () { constructor () {
super() super(profile)
this.el = null this.el = null
} }
get profile () {
return {
displayName: 'debugger',
name: 'debugger',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDk2MHEwIDI2LTE5IDQ1dC00NSAxOWgtMjI0cTAgMTcxLTY3IDI5MGwyMDggMjA5cTE5IDE5IDE5IDQ1dC0xOSA0NXEtMTggMTktNDUgMTl0LTQ1LTE5bC0xOTgtMTk3cS01IDUtMTUgMTN0LTQyIDI4LjUtNjUgMzYuNS04MiAyOS05NyAxM3YtODk2aC0xMjh2ODk2cS01MSAwLTEwMS41LTEzLjV0LTg3LTMzLTY2LTM5LTQzLjUtMzIuNWwtMTUtMTQtMTgzIDIwN3EtMjAgMjEtNDggMjEtMjQgMC00My0xNi0xOS0xOC0yMC41LTQ0LjV0MTUuNS00Ni41bDIwMi0yMjdxLTU4LTExNC01OC0yNzRoLTIyNHEtMjYgMC00NS0xOXQtMTktNDUgMTktNDUgNDUtMTloMjI0di0yOTRsLTE3My0xNzNxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5IDQ1IDE5bDE3MyAxNzNoODQ0bDE3My0xNzNxMTktMTkgNDUtMTl0NDUgMTkgMTkgNDUtMTkgNDVsLTE3MyAxNzN2Mjk0aDIyNHEyNiAwIDQ1IDE5dDE5IDQ1em0tNDgwLTU3NmgtNjQwcTAtMTMzIDkzLjUtMjI2LjV0MjI2LjUtOTMuNSAyMjYuNSA5My41IDkzLjUgMjI2LjV6Ii8+PC9zdmc+',
description: 'debug transactions',
kind: 'debugging',
location: 'swapPanel'
}
}
render () { render () {
if (this.el) return this.el if (this.el) return this.el

@ -0,0 +1,66 @@
const executionContext = require('../../execution-context')
import { NetworkApi } from 'remix-plugin'
export const profile = {
name: 'network',
description: 'Manage the network (mainnet, ropsten, goerli...) and the provider (web3, vm, injected)'
}
// Network API has :
// - events: ['providerChanged']
// - methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork']
export class NetworkModule extends NetworkApi {
constructor () {
super(profile)
// TODO: See with remix-lib to make sementic coherent
executionContext.event.register('contextChanged', (provider) => {
this.events.emit('providerChanged', provider)
})
/*
// Events that could be implemented later
executionContext.event.register('removeProvider', (provider) => {
this.events.emit('networkRemoved', provider)
})
executionContext.event.register('addProvider', (provider) => {
this.events.emit('networkAdded', provider)
})
executionContext.event.register('web3EndpointChanged', (provider) => {
this.events.emit('web3EndpointChanged', provider)
})
*/
}
/** Return the current network provider (web3, vm, injected) */
getNetworkProvider () {
return executionContext.getProvider()
}
/** Return the current network */
detectNetwork () {
return new Promise((resolve, reject) => {
executionContext.detectNetwork((error, network) => {
error ? reject(error) : resolve(network)
})
})
}
/** Return the url only if network provider is 'web3' */
getEndpoint () {
const provider = executionContext.getProvider()
if (provider !== 'web3') {
throw new Error('no endpoint: current provider is either injected or vm')
}
return provider.web3().currentProvider.host
}
/** Add a custom network to the list of available networks */
addNetwork (customNetwork) {
executionContext.addProvider(customNetwork)
}
/** Remove a network to the list of availble networks */
removeNetwork (name) {
executionContext.removeProvider(name)
}
}

@ -12,12 +12,23 @@ var ContractDropdownUI = require('./runTab/contractDropdown.js')
var Recorder = require('./runTab/model/recorder.js') var Recorder = require('./runTab/model/recorder.js')
var RecorderUI = require('./runTab/recorder.js') var RecorderUI = require('./runTab/recorder.js')
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
const profile = {
name: 'run',
displayName: 'Deploy and run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNTc2IDkyN2wtMTMyOCA3MzhxLTIzIDEzLTM5LjUgM3QtMTYuNS0zNnYtMTQ3MnEwLTI2IDE2LjUtMzZ0MzkuNSAzbDEzMjggNzM4cTIzIDEzIDIzIDMxdC0yMyAzMXoiLz48L3N2Zz4=',
description: 'execute and save transactions',
kind: 'run',
location: 'swapPanel'
}
class RunTab extends ApiFactory { class RunTab extends BaseApi {
constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) { constructor (udapp, udappUI, config, fileManager, editor, logCallback, filePanel, pluginManager, compilersArtefacts) {
super() super(profile)
this.event = new EventManager() this.event = new EventManager()
this.renderInstanceContainer() this.renderInstanceContainer()
@ -28,19 +39,6 @@ class RunTab extends ApiFactory {
this.renderContainer() this.renderContainer()
} }
get profile () {
return {
name: 'run',
displayName: 'run transactions',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNTc2IDkyN2wtMTMyOCA3MzhxLTIzIDEzLTM5LjUgM3QtMTYuNS0zNnYtMTQ3MnEwLTI2IDE2LjUtMzZ0MzkuNSAzbDEzMjggNzM4cTIzIDEzIDIzIDMxdC0yMyAzMXoiLz48L3N2Zz4=',
description: 'execute and save transactions',
kind: 'run',
location: 'swapPanel'
}
}
renderContainer () { renderContainer () {
this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>` this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>`

@ -44,7 +44,7 @@ class ContractDropdownUI {
} }
render () { render () {
this.compFails = yo`<i title="Contract compilation failed. Please check the compile tab for more information." class="fa fa-times-circle ${css.errorIcon}" ></i>` this.compFails = yo`<i title="No contract compiled yet or compilation failed. Please check the compile tab for more information." class="fa fa-times-circle ${css.errorIcon}" ></i>`
var info = yo`<i class="fa fa-info ${css.infoDeployAction}" aria-hidden="true" title="*.sol files allows deploying and accessing contracts. *.abi files only allows accessing contracts."></i>` var info = yo`<i class="fa fa-info ${css.infoDeployAction}" aria-hidden="true" title="*.sol files allows deploying and accessing contracts. *.abi files only allows accessing contracts."></i>`
this.atAddressButtonInput = yo`<input class="${css.input} ${css.ataddressinput} ataddressinput form-control" placeholder="Load contract from Address" title="atAddress" />` this.atAddressButtonInput = yo`<input class="${css.input} ${css.ataddressinput} ataddressinput form-control" placeholder="Load contract from Address" title="atAddress" />`
@ -68,7 +68,7 @@ class ContractDropdownUI {
</div> </div>
` `
this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this)) this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this))
this.setInputParamsPlaceHolder()
return el return el
} }

@ -64,7 +64,7 @@ class SettingsUI {
value="web3" name="executionContext"> Web3 Provider value="web3" name="executionContext"> Web3 Provider
</option> </option>
</select> </select>
<a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md" target="_blank"><i class="${css.icon} fa fa-info"></i></a> <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md" target="_blank"><i class="${css.infoDeployAction} fa fa-info"></i></a>
</div> </div>
</div> </div>
` `
@ -93,13 +93,15 @@ class SettingsUI {
var valueEl = yo` var valueEl = yo`
<div class="${css.crow}"> <div class="${css.crow}">
<div class="${css.col1_1}">Value</div> <div class="${css.col1_1}">Value</div>
<input type="text" class="form-control ${css.gasNval} ${css.col2_1}" id="value" value="0" title="Enter the value and choose the unit"> <div class="${css.gasValueContainer}">
<select name="unit" class="form-control ${css.gasNval} ${css.col2_2}" id="unit"> <input type="text" class="form-control ${css.gasNval} ${css.col2}" id="value" value="0" title="Enter the value and choose the unit">
<option data-unit="wei">wei</option> <select name="unit" class="form-control ${css.gasNvalUnit} ${css.col2_2}" id="unit">
<option data-unit="gwei">gwei</option> <option data-unit="wei">wei</option>
<option data-unit="finney">finney</option> <option data-unit="gwei">gwei</option>
<option data-unit="ether">ether</option> <option data-unit="finney">finney</option>
</select> <option data-unit="ether">ether</option>
</select>
</div>
</div> </div>
` `

@ -4,11 +4,22 @@ var tooltip = require('../ui/tooltip')
var copyToClipboard = require('../ui/copy-to-clipboard') var copyToClipboard = require('../ui/copy-to-clipboard')
var EventManager = require('../../lib/events') var EventManager = require('../../lib/events')
var css = require('./styles/settings-tab-styles') var css = require('./styles/settings-tab-styles')
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
module.exports = class SettingsTab extends ApiFactory { const profile = {
name: 'settings',
displayName: 'Settings',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xMTUyIDg5NnEwLTEwNi03NS0xODF0LTE4MS03NS0xODEgNzUtNzUgMTgxIDc1IDE4MSAxODEgNzUgMTgxLTc1IDc1LTE4MXptNTEyLTEwOXYyMjJxMCAxMi04IDIzdC0yMCAxM2wtMTg1IDI4cS0xOSA1NC0zOSA5MSAzNSA1MCAxMDcgMTM4IDEwIDEyIDEwIDI1dC05IDIzcS0yNyAzNy05OSAxMDh0LTk0IDcxcS0xMiAwLTI2LTlsLTEzOC0xMDhxLTQ0IDIzLTkxIDM4LTE2IDEzNi0yOSAxODYtNyAyOC0zNiAyOGgtMjIycS0xNCAwLTI0LjUtOC41dC0xMS41LTIxLjVsLTI4LTE4NHEtNDktMTYtOTAtMzdsLTE0MSAxMDdxLTEwIDktMjUgOS0xNCAwLTI1LTExLTEyNi0xMTQtMTY1LTE2OC03LTEwLTctMjMgMC0xMiA4LTIzIDE1LTIxIDUxLTY2LjV0NTQtNzAuNXEtMjctNTAtNDEtOTlsLTE4My0yN3EtMTMtMi0yMS0xMi41dC04LTIzLjV2LTIyMnEwLTEyIDgtMjN0MTktMTNsMTg2LTI4cTE0LTQ2IDM5LTkyLTQwLTU3LTEwNy0xMzgtMTAtMTItMTAtMjQgMC0xMCA5LTIzIDI2LTM2IDk4LjUtMTA3LjV0OTQuNS03MS41cTEzIDAgMjYgMTBsMTM4IDEwN3E0NC0yMyA5MS0zOCAxNi0xMzYgMjktMTg2IDctMjggMzYtMjhoMjIycTE0IDAgMjQuNSA4LjV0MTEuNSAyMS41bDI4IDE4NHE0OSAxNiA5MCAzN2wxNDItMTA3cTktOSAyNC05IDEzIDAgMjUgMTAgMTI5IDExOSAxNjUgMTcwIDcgOCA3IDIyIDAgMTItOCAyMy0xNSAyMS01MSA2Ni41dC01NCA3MC41cTI2IDUwIDQxIDk4bDE4MyAyOHExMyAyIDIxIDEyLjV0OCAyMy41eiIvPjwvc3ZnPg==',
description: 'Remix-IDE settings',
kind: 'settings',
location: 'swapPanel'
}
module.exports = class SettingsTab extends BaseApi {
constructor (config, editor, appManager) { constructor (config, editor, appManager) {
super() super(profile)
this.config = config this.config = config
this.editor = editor this.editor = editor
this.appManager = appManager this.appManager = appManager
@ -30,18 +41,7 @@ module.exports = class SettingsTab extends ApiFactory {
initTheme () { initTheme () {
this.currentTheme = this._deps.themeModule.currentTheme() this.currentTheme = this._deps.themeModule.currentTheme()
} }
get profile () {
return {
displayName: 'settings',
name: 'settings',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xMTUyIDg5NnEwLTEwNi03NS0xODF0LTE4MS03NS0xODEgNzUtNzUgMTgxIDc1IDE4MSAxODEgNzUgMTgxLTc1IDc1LTE4MXptNTEyLTEwOXYyMjJxMCAxMi04IDIzdC0yMCAxM2wtMTg1IDI4cS0xOSA1NC0zOSA5MSAzNSA1MCAxMDcgMTM4IDEwIDEyIDEwIDI1dC05IDIzcS0yNyAzNy05OSAxMDh0LTk0IDcxcS0xMiAwLTI2LTlsLTEzOC0xMDhxLTQ0IDIzLTkxIDM4LTE2IDEzNi0yOSAxODYtNyAyOC0zNiAyOGgtMjIycS0xNCAwLTI0LjUtOC41dC0xMS41LTIxLjVsLTI4LTE4NHEtNDktMTYtOTAtMzdsLTE0MSAxMDdxLTEwIDktMjUgOS0xNCAwLTI1LTExLTEyNi0xMTQtMTY1LTE2OC03LTEwLTctMjMgMC0xMiA4LTIzIDE1LTIxIDUxLTY2LjV0NTQtNzAuNXEtMjctNTAtNDEtOTlsLTE4My0yN3EtMTMtMi0yMS0xMi41dC04LTIzLjV2LTIyMnEwLTEyIDgtMjN0MTktMTNsMTg2LTI4cTE0LTQ2IDM5LTkyLTQwLTU3LTEwNy0xMzgtMTAtMTItMTAtMjQgMC0xMCA5LTIzIDI2LTM2IDk4LjUtMTA3LjV0OTQuNS03MS41cTEzIDAgMjYgMTBsMTM4IDEwN3E0NC0yMyA5MS0zOCAxNi0xMzYgMjktMTg2IDctMjggMzYtMjhoMjIycTE0IDAgMjQuNSA4LjV0MTEuNSAyMS41bDI4IDE4NHE0OSAxNiA5MCAzN2wxNDItMTA3cTktOSAyNC05IDEzIDAgMjUgMTAgMTI5IDExOSAxNjUgMTcwIDcgOCA3IDIyIDAgMTItOCAyMy0xNSAyMS01MSA2Ni41dC01NCA3MC41cTI2IDUwIDQxIDk4bDE4MyAyOHExMyAyIDIxIDEyLjV0OCAyMy41eiIvPjwvc3ZnPg==',
description: ' - ',
kind: 'settings',
location: 'swapPanel'
}
}
createThemeCheckies () { createThemeCheckies () {
let themes = this._deps.themeModule.getThemes() let themes = this._deps.themeModule.getThemes()
const onswitchTheme = (event, name) => { const onswitchTheme = (event, name) => {

@ -12,10 +12,11 @@ var css = csjs`
font-size: 12px; font-size: 12px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding-left: 15px;
} }
.settings { .settings {
margin-bottom: 2%; margin-bottom: 2%;
padding: 10px 15px 15px 15px; padding: 10px 0px 15px 15px;
} }
.recorderCount { .recorderCount {
/* margin-right: 30px; */ /* margin-right: 30px; */
@ -38,7 +39,6 @@ var css = csjs`
} }
.col1_1 { .col1_1 {
font-size: 12px; font-size: 12px;
width: 15%;
min-width: 75px; min-width: 75px;
float: left; float: left;
align-self: center; align-self: center;
@ -47,12 +47,13 @@ var css = csjs`
display: flex; display: flex;
align-items: center; align-items: center;
position: relative; position: relative;
width: 259px; width: 100%;
padding-right: 25px;
} }
.account { .account {
display: flex; display: flex;
align-items: center; align-items: center;
width: 266px; width: 90%;
} }
.col2 { .col2 {
border-radius: 3px; border-radius: 3px;
@ -62,12 +63,14 @@ var css = csjs`
min-width: 164px; min-width: 164px;
} }
.col2_2 { .col2_2 {
width: 82px; width: 100px;
min-width: 82px; min-width: 82px;
position: absolute;
margin-left: 100px;
} }
.select { .select {
font-weight: normal; font-weight: normal;
width: 250px; width: 100%;
} }
.instanceContainer { .instanceContainer {
display: flex; display: flex;
@ -85,7 +88,8 @@ var css = csjs`
text-align: center; text-align: center;
} }
.container { .container {
margin-bottom: 2%; margin-bottom: 4%;
padding-left: 15px;
} }
.recorderCollapsedView, .recorderCollapsedView,
.recorderExpandedView { .recorderExpandedView {
@ -141,6 +145,7 @@ var css = csjs`
.noInstancesText { .noInstancesText {
font-style: italic; font-style: italic;
text-align: left; text-align: left;
padding-left: 15px;
} }
.pendingTxsText { .pendingTxsText {
font-style: italic; font-style: italic;
@ -188,7 +193,7 @@ var css = csjs`
color: var(--warning); color: var(--warning);
} }
.errorIcon { .errorIcon {
color: var(--danger); color: var(--warning);
margin-left: 15px; margin-left: 15px;
} }
.failDesc { .failDesc {
@ -204,7 +209,7 @@ var css = csjs`
color: grey; color: grey;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding-right: 28px; padding-right: 53px;
pointer-events: none; pointer-events: none;
} }
.networkItem { .networkItem {
@ -224,12 +229,22 @@ var css = csjs`
.infoDeployAction { .infoDeployAction {
margin-left: 5px; margin-left: 5px;
font-size: 13px; font-size: 13px;
color: var(--danger); color: var(--info);
}
.gasValueContainer {
flex-direction: row;
display: flex;
} }
.gasNval { .gasNval {
transform: scale(0.7); transform: scale(0.7);
transform-origin: left; transform-origin: left;
margin-right: 10px; margin-right: 10px;
width: 120px;
}
.gasNvalUnit {
transform: scale(0.7);
transform-origin: left;
margin-right: 10px;
} }
` `

@ -3,13 +3,23 @@ var async = require('async')
var tooltip = require('../ui/tooltip') var tooltip = require('../ui/tooltip')
var css = require('./styles/test-tab-styles') var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests') var remixTests = require('remix-tests')
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
const TestTabLogic = require('./testTab/testTab') const TestTabLogic = require('./testTab/testTab')
module.exports = class TestTab extends ApiFactory { const profile = {
name: 'solidityUnitTesting',
displayName: 'Solidity unit testing',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMjMwNCIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMjMwNCAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzI4IDQ0OGwtMzg0IDcwNGg3Njh6bS0xMjgwIDBsLTM4NCA3MDRoNzY4em04MjEtMTkycS0xNCA0MC00NS41IDcxLjV0LTcxLjUgNDUuNXYxMjkxaDYwOHExNCAwIDIzIDl0OSAyM3Y2NHEwIDE0LTkgMjN0LTIzIDloLTEzNDRxLTE0IDAtMjMtOXQtOS0yM3YtNjRxMC0xNCA5LTIzdDIzLTloNjA4di0xMjkxcS00MC0xNC03MS41LTQ1LjV0LTQ1LjUtNzEuNWgtNDkxcS0xNCAwLTIzLTl0LTktMjN2LTY0cTAtMTQgOS0yM3QyMy05aDQ5MXEyMS01NyA3MC05Mi41dDExMS0zNS41IDExMSAzNS41IDcwIDkyLjVoNDkxcTE0IDAgMjMgOXQ5IDIzdjY0cTAgMTQtOSAyM3QtMjMgOWgtNDkxem0tMTgxIDE2cTMzIDAgNTYuNS0yMy41dDIzLjUtNTYuNS0yMy41LTU2LjUtNTYuNS0yMy41LTU2LjUgMjMuNS0yMy41IDU2LjUgMjMuNSA1Ni41IDU2LjUgMjMuNXptMTA4OCA4ODBxMCA3My00Ni41IDEzMXQtMTE3LjUgOTEtMTQ0LjUgNDkuNS0xMzkuNSAxNi41LTEzOS41LTE2LjUtMTQ0LjUtNDkuNS0xMTcuNS05MS00Ni41LTEzMXEwLTExIDM1LTgxdDkyLTE3NC41IDEwNy0xOTUuNSAxMDItMTg0IDU2LTEwMHExOC0zMyA1Ni0zM3Q1NiAzM3E0IDcgNTYgMTAwdDEwMiAxODQgMTA3IDE5NS41IDkyIDE3NC41IDM1IDgxem0tMTI4MCAwcTAgNzMtNDYuNSAxMzF0LTExNy41IDkxLTE0NC41IDQ5LjUtMTM5LjUgMTYuNS0xMzkuNS0xNi41LTE0NC41LTQ5LjUtMTE3LjUtOTEtNDYuNS0xMzFxMC0xMSAzNS04MXQ5Mi0xNzQuNSAxMDctMTk1LjUgMTAyLTE4NCA1Ni0xMDBxMTgtMzMgNTYtMzN0NTYgMzNxNCA3IDU2IDEwMHQxMDIgMTg0IDEwNyAxOTUuNSA5MiAxNzQuNSAzNSA4MXoiLz48L3N2Zz4=',
description: 'Fast tool to generate unit tests for your contracts',
location: 'swapPanel'
}
module.exports = class TestTab extends BaseApi {
constructor (fileManager, filePanel, compileTab) { constructor (fileManager, filePanel, compileTab) {
super() super(profile)
this.compileTab = compileTab this.compileTab = compileTab
this._view = { el: null } this._view = { el: null }
this.compileTab = compileTab this.compileTab = compileTab
@ -20,18 +30,6 @@ module.exports = class TestTab extends ApiFactory {
this.testList = yo`<div class=${css.testList}></div>` this.testList = yo`<div class=${css.testList}></div>`
} }
get profile () {
return {
name: 'solidityUnitTesting',
displayName: 'solidity unit testing',
methods: [],
events: [],
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMjMwNCIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMjMwNCAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzI4IDQ0OGwtMzg0IDcwNGg3Njh6bS0xMjgwIDBsLTM4NCA3MDRoNzY4em04MjEtMTkycS0xNCA0MC00NS41IDcxLjV0LTcxLjUgNDUuNXYxMjkxaDYwOHExNCAwIDIzIDl0OSAyM3Y2NHEwIDE0LTkgMjN0LTIzIDloLTEzNDRxLTE0IDAtMjMtOXQtOS0yM3YtNjRxMC0xNCA5LTIzdDIzLTloNjA4di0xMjkxcS00MC0xNC03MS41LTQ1LjV0LTQ1LjUtNzEuNWgtNDkxcS0xNCAwLTIzLTl0LTktMjN2LTY0cTAtMTQgOS0yM3QyMy05aDQ5MXEyMS01NyA3MC05Mi41dDExMS0zNS41IDExMSAzNS41IDcwIDkyLjVoNDkxcTE0IDAgMjMgOXQ5IDIzdjY0cTAgMTQtOSAyM3QtMjMgOWgtNDkxem0tMTgxIDE2cTMzIDAgNTYuNS0yMy41dDIzLjUtNTYuNS0yMy41LTU2LjUtNTYuNS0yMy41LTU2LjUgMjMuNS0yMy41IDU2LjUgMjMuNSA1Ni41IDU2LjUgMjMuNXptMTA4OCA4ODBxMCA3My00Ni41IDEzMXQtMTE3LjUgOTEtMTQ0LjUgNDkuNS0xMzkuNSAxNi41LTEzOS41LTE2LjUtMTQ0LjUtNDkuNS0xMTcuNS05MS00Ni41LTEzMXEwLTExIDM1LTgxdDkyLTE3NC41IDEwNy0xOTUuNSAxMDItMTg0IDU2LTEwMHExOC0zMyA1Ni0zM3Q1NiAzM3E0IDcgNTYgMTAwdDEwMiAxODQgMTA3IDE5NS41IDkyIDE3NC41IDM1IDgxem0tMTI4MCAwcTAgNzMtNDYuNSAxMzF0LTExNy41IDkxLTE0NC41IDQ5LjUtMTM5LjUgMTYuNS0xMzkuNS0xNi41LTE0NC41LTQ5LjUtMTE3LjUtOTEtNDYuNS0xMzFxMC0xMSAzNS04MXQ5Mi0xNzQuNSAxMDctMTk1LjUgMTAyLTE4NCA1Ni0xMDBxMTgtMzMgNTYtMzN0NTYgMzNxNCA3IDU2IDEwMHQxMDIgMTg0IDEwNyAxOTUuNSA5MiAxNzQuNSAzNSA4MXoiLz48L3N2Zz4=',
description: ' - ',
location: 'swapPanel'
}
}
activate () { activate () {
this.listenToEvents() this.listenToEvents()
} }
@ -138,12 +136,14 @@ module.exports = class TestTab extends ApiFactory {
} }
runTest (testFilePath, callback) { runTest (testFilePath, callback) {
this.loading.hidden = false
this.fileManager.fileProviderOf(testFilePath).get(testFilePath, (error, content) => { this.fileManager.fileProviderOf(testFilePath).get(testFilePath, (error, content) => {
if (error) return if (error) return
var runningTest = {} var runningTest = {}
runningTest[testFilePath] = { content } runningTest[testFilePath] = { content }
remixTests.runTestSources(runningTest, (result) => { this.testCallback(result) }, (_err, result, cb) => { this.resultsCallback(_err, result, cb) }, (error, result) => { remixTests.runTestSources(runningTest, (result) => { this.testCallback(result) }, (_err, result, cb) => { this.resultsCallback(_err, result, cb) }, (error, result) => {
this.updateFinalResult(error, result, testFilePath) this.updateFinalResult(error, result, testFilePath)
this.loading.hidden = true
callback(error) callback(error)
}, (url, cb) => { }, (url, cb) => {
return this.compileTab.compileTabLogic.importFileCb(url, cb) return this.compileTab.compileTabLogic.importFileCb(url, cb)
@ -152,6 +152,7 @@ module.exports = class TestTab extends ApiFactory {
} }
runTests () { runTests () {
this.loading.hidden = false
this.testsOutput.innerHTML = '' this.testsOutput.innerHTML = ''
this.testsSummary.innerHTML = '' this.testsSummary.innerHTML = ''
var tests = this.data.selectedTests var tests = this.data.selectedTests
@ -161,7 +162,8 @@ module.exports = class TestTab extends ApiFactory {
render () { render () {
this.testsOutput = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>` this.testsOutput = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
this.testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>` this.testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
this.loading = yo`<span class='text-info ml-1'>Running tests...</span>`
this.loading.hidden = true
var el = yo` var el = yo`
<div class="${css.testTabView} card" id="testView"> <div class="${css.testTabView} card" id="testView">
<div class="${css.infoBox}"> <div class="${css.infoBox}">
@ -173,7 +175,7 @@ module.exports = class TestTab extends ApiFactory {
<br/> <br/>
For more details, see For more details, see
How to test smart contracts guide in our documentation. How to test smart contracts guide in our documentation.
<div class="${css.generateTestFile} btn btn-primary m-1" onclick="${this.testTabLogic.generateTestFile(this)}">Generate test file</div> <div class="${css.generateTestFile} btn btn-primary m-1" onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}">Generate test file</div>
</div> </div>
<div class="${css.tests}"> <div class="${css.tests}">
<div class="${css.buttons}"> <div class="${css.buttons}">
@ -189,7 +191,7 @@ module.exports = class TestTab extends ApiFactory {
</div> </div>
${this.testList} ${this.testList}
<hr> <hr>
<div class="${css.buttons}" ><h6>Results:</h6></div> <div class="${css.buttons}" ><h6>Results:${this.loading}</h6></div>
${this.testsOutput} ${this.testsOutput}
${this.testsSummary} ${this.testsSummary}
</div> </div>

@ -26,7 +26,7 @@ class TestTabLogic {
var tests = [] var tests = []
let files let files
try { try {
files = await this.fileManager.getFilesFromPath(path) files = await this.fileManager.getFolder(path)
} catch (e) { } catch (e) {
cb(e.message) cb(e.message)
} }

@ -1,4 +1,4 @@
import { ApiFactory } from 'remix-plugin' import { BaseApi } from 'remix-plugin'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
const Storage = require('remix-lib').Storage const Storage = require('remix-lib').Storage
@ -13,28 +13,26 @@ const themes = [
{name: 'Yeti', quality: 'light', url: 'https://bootswatch.com/4/yeti/bootstrap.min.css'}, {name: 'Yeti', quality: 'light', url: 'https://bootswatch.com/4/yeti/bootstrap.min.css'},
{name: 'Cyborg', quality: 'dark', url: 'https://bootswatch.com/4/cyborg/bootstrap.min.css'}, {name: 'Cyborg', quality: 'dark', url: 'https://bootswatch.com/4/cyborg/bootstrap.min.css'},
{name: 'Darkly', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/darkly/bootstrap.min.css'}, {name: 'Darkly', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/darkly/bootstrap.min.css'},
{name: 'Slate', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/slate/bootstrap.min.css'}, {name: 'Slate', quality: 'light', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/slate/bootstrap.min.css'},
{name: 'Superhero', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/superhero/bootstrap.min.css'} {name: 'Superhero', quality: 'dark', url: 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/superhero/bootstrap.min.css'}
] ]
export class ThemeModule extends ApiFactory { const profile = {
name: 'theme',
events: ['themeChanged'],
methods: ['switchTheme', 'getThemes', 'currentTheme']
}
export class ThemeModule extends BaseApi {
constructor () { constructor () {
super() super(profile)
this.events = new EventEmitter() this.events = new EventEmitter()
this.storage = new Storage('style:') this.storage = new Storage('style:')
this.themes = themes.reduce((acc, theme) => ({ ...acc, [theme.name]: theme }), {}) this.themes = themes.reduce((acc, theme) => ({ ...acc, [theme.name]: theme }), {})
this.active = this.storage.exists('theme') ? this.storage.get('theme') : 'Cerulean' this.active = this.storage.exists('theme') ? this.storage.get('theme') : 'Cerulean'
} }
get profile () {
return {
name: 'theme',
events: ['themeChanged'],
methods: ['switchTheme', 'getThemes', 'currentTheme']
}
}
/** Return the active theme */ /** Return the active theme */
currentTheme () { currentTheme () {
return this.themes[this.active] return this.themes[this.active]
@ -58,6 +56,7 @@ export class ThemeModule extends ApiFactory {
this.storage.set('theme', next) this.storage.set('theme', next)
document.getElementById('theme-link').setAttribute('href', nextTheme.url) document.getElementById('theme-link').setAttribute('href', nextTheme.url)
document.documentElement.style.setProperty('--theme', nextTheme.quality) document.documentElement.style.setProperty('--theme', nextTheme.quality)
if (themeName) this.active = themeName
this.events.emit('themeChanged', nextTheme.quality) this.events.emit('themeChanged', nextTheme.quality)
} }
} }

@ -1,23 +0,0 @@
import { ApiFactory } from 'remix-plugin'
import { EventEmitter } from 'events'
export class TxListenerModule extends ApiFactory {
constructor (txlistener) {
super()
this.events = new EventEmitter()
txlistener.event.register('newTransaction', (tx) => {
this.events.emit('newTransaction', tx)
})
}
get profile () {
return {
name: 'txListener',
displayName: 'transaction listener',
events: ['newTransaction'],
description: 'service - notify new transactions',
permission: true
}
}
}

@ -23,8 +23,9 @@ class AutoCompletePopup {
var self = this var self = this
self.event = new EventManager() self.event = new EventManager()
self.isOpen = false self.isOpen = false
self.opts = opts
self.data = { self.data = {
_options: opts.options || [] _options: []
} }
self._components = { self._components = {
modal: null modal: null
@ -33,7 +34,9 @@ class AutoCompletePopup {
self._startingElement = 0 self._startingElement = 0
self._elementsToShow = 4 self._elementsToShow = 4
self._selectedElement = 0 self._selectedElement = 0
this.extraCommands = []
this.render() this.render()
this.extendAutocompletion()
} }
render () { render () {
@ -149,18 +152,24 @@ class AutoCompletePopup {
this.isOpen = true this.isOpen = true
this.data._options = [] this.data._options = []
Commands.allPrograms.forEach(item => { Commands.allPrograms.forEach(item => {
let program = getKeyOf(item) const program = getKeyOf(item)
if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) { if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) {
this.data._options.push(item) this.data._options.push(item)
} else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) { } else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) {
Commands.allCommands.forEach(item => { Commands.allCommands.forEach(item => {
let command = getKeyOf(item) const command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) { if (command.includes(autoCompleteInput.trim())) {
this.data._options.push(item) this.data._options.push(item)
} }
}) })
} }
}) })
this.extraCommands.forEach(item => {
const command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) {
this.data._options.push(item)
}
})
if (this.data._options.length === 1 && event.which === 9) { if (this.data._options.length === 1 && event.which === 9) {
// if only one option and tab is pressed, we resolve it // if only one option and tab is pressed, we resolve it
@ -188,6 +197,21 @@ class AutoCompletePopup {
this._selectedElement = 0 this._selectedElement = 0
yo.update(this._view, this.render()) yo.update(this._view, this.render())
} }
extendAutocompletion () {
// TODO: this is not using the appManager interface. Terminal should be put as module
this.opts.appStore.event.on('activate', (id) => {
const profile = this.opts.appStore.getOne(id).profile
if (!profile.methods) return
profile.methods.forEach((method) => {
const key = `remix.call({name: '${id}', key:'${method}', payload: []}).then((result) => { console.log(result) }).catch((error) => { console.log(error) })`
const keyValue = {}
keyValue[key] = `call ${id} - ${method}`
if (this.extraCommands.includes(keyValue)) return
this.extraCommands.push(keyValue)
})
})
}
} }
function getKeyOf (item) { function getKeyOf (item) {

@ -16,17 +16,15 @@ module.exports = class Card {
if (self._view.el) return self._view.el if (self._view.el) return self._view.el
self._view.cardBody = yo`<div class=${css.cardBody}></div>` self._view.cardBody = yo`<div class=${css.cardBody}></div>`
self._view.arrow = yo`<i class="${css.arrow} fa fa-angle-down" self._view.arrow = yo`<i class="${css.arrow} fa fa-angle-down" onclick="${() => trigger(this)}"></i>`
onclick=${(ev) => trigger(ev.target)}></i>`
self._view.expandCollapseButton = yo` self._view.expandCollapseButton = yo`
<div class=${css.expandCollapseButton}>${self._view.arrow}</div>` <div class=${css.expandCollapseButton}>${self._view.arrow}</div>`
self._view.statusBar = yo`<div class=${css.statusBar}>${self._opts.collapsedView}</div>` self._view.statusBar = yo`<div class=${css.statusBar}>${self._opts.collapsedView}</div>`
self._view.cardHeader = yo` self._view.cardHeader = yo`
<div class=${css.cardHeader}> <div class=${css.cardHeader} onclick=${() => trigger(self._view.arrow)}>
<div class=${css.cardTitles}> <div class="p-1 ${css.cardTitles}">
<div class=${css.cardTitle}>${self._opts.title}</div> <div class=${css.cardTitle}>${self._opts.title}</div>
${self._view.statusBar} ${self._view.statusBar}
</div> </div>
@ -36,14 +34,16 @@ module.exports = class Card {
function trigger (el) { function trigger (el) {
var body = self._view.cardBody var body = self._view.cardBody
var status = self._view.statusBar var status = self._view.statusBar
el.classList.toggle('fa-angle-up') if (el.classList) {
var arrow = el.classList.toggle('fa-angle-down') ? 'up' : 'down' el.classList.toggle('fa-angle-up')
self.event.trigger('expandCollapseCard', [arrow, body, status]) var arrow = el.classList.toggle('fa-angle-down') ? 'up' : 'down'
self.event.trigger('expandCollapseCard', [arrow, body, status])
}
} }
// HTML // HTML
self._view.el = yo` self._view.el = yo`
<div class="${css.cardContainer} list-group-item"> <div class="${css.cardContainer} p-2 list-group-item">
${self._view.cardHeader} ${self._view.cardHeader}
${self._view.cardBody} ${self._view.cardBody}
</div>` </div>`
@ -55,7 +55,7 @@ module.exports = class Card {
const css = csjs` const css = csjs`
.cardContainer { .cardContainer {
padding: 10px 15px 15px 0; padding : 10px 15px 15px 0;
margin-bottom : 2%; margin-bottom : 2%;
} }
.cardHeader { .cardHeader {

File diff suppressed because one or more lines are too long

@ -21,7 +21,6 @@ export const defaultWorkspaces = (appManager) => {
appManager.ensureActivated('run') appManager.ensureActivated('run')
appManager.ensureActivated('solidityStaticAnalysis') appManager.ensureActivated('solidityStaticAnalysis')
appManager.ensureActivated('solidityUnitTesting') appManager.ensureActivated('solidityUnitTesting')
globalRegistry.get('filemanager').api.switchFile()
globalRegistry.get('verticalicon').api.select('solidity') globalRegistry.get('verticalicon').api.select('solidity')
}, () => {}), }, () => {}),
new Workspace( new Workspace(
@ -31,7 +30,6 @@ export const defaultWorkspaces = (appManager) => {
() => { () => {
appManager.ensureActivated('vyper') appManager.ensureActivated('vyper')
appManager.ensureActivated('run') appManager.ensureActivated('run')
globalRegistry.get('filemanager').api.switchFile()
globalRegistry.get('verticalicon').api.select('vyper') globalRegistry.get('verticalicon').api.select('vyper')
}, () => {}), }, () => {}),
new Workspace('Debugger', 'Debug transactions with remix', false, () => { new Workspace('Debugger', 'Debug transactions with remix', false, () => {

@ -66,7 +66,7 @@ module.exports = {
function prompt (title, text, hidden, inputValue, ok, cancel, focus) { function prompt (title, text, hidden, inputValue, ok, cancel, focus) {
if (!inputValue) inputValue = '' if (!inputValue) inputValue = ''
var type = hidden ? 'password' : 'text' var type = hidden ? 'password' : 'text'
var input = yo`<input type=${type} name='prompt_text' id='prompt_text' class="${css['prompt_text']}" value='${inputValue}' >` var input = yo`<input type=${type} name='prompt_text' id='prompt_text' class="${css['prompt_text']} form-control" value='${inputValue}' >`
modal(title, yo`<div>${text}<div>${input}</div></div>`, modal(title, yo`<div>${text}<div>${input}</div></div>`,
{ {
fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) } fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) }

@ -8,6 +8,8 @@ var css = csjs`
flex-direction : column; flex-direction : column;
margin-left : 10px; margin-left : 10px;
width : auto; width : auto;
margin-top : 2px;
max-height : 24px;
} }
.selectbox { .selectbox {
display : flex; display : flex;
@ -31,7 +33,7 @@ var css = csjs`
display : flex; display : flex;
flex-direction : column; flex-direction : column;
align-items : end; align-items : end;
top : 32px; top : 23px;
left : 0; left : 0;
width : 245px; width : 245px;
border : 1px solid var(--dark); border : 1px solid var(--dark);

@ -13,5 +13,18 @@ export default {
verticalIconApi.select('fileExplorers') verticalIconApi.select('fileExplorers')
mainPanelApi.showContent('home') mainPanelApi.showContent('home')
document.addEventListener('keypress', (e) => {
if (e.shiftKey && e.ctrlKey) {
if (e.code === 'KeyF') { // Ctrl+Shift+F
verticalIconApi.select('fileExplorers')
} else if (e.code === 'KeyA') { // Ctrl+Shift+A
verticalIconApi.select('pluginManager')
} else if (e.code === 'KeyS') { // Ctrl+Shift+S
verticalIconApi.select('settings')
}
e.preventDefault()
}
})
} }
} }

@ -28,6 +28,7 @@ class CmdInterpreterAPI {
offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api
} }
self.commandHelp = { self.commandHelp = {
'remix.call(message: {name, key, payload})': 'Call a registered plugins',
'remix.getFile(path)': 'Returns the content of the file located at the given path', 'remix.getFile(path)': 'Returns the content of the file located at the given path',
'remix.setFile(path, content)': 'set the content of the file located at the given path', 'remix.setFile(path, content)': 'set the content of the file located at the given path',
'remix.debug(hash)': 'Start debugging a transaction.', 'remix.debug(hash)': 'Start debugging a transaction.',
@ -40,6 +41,9 @@ class CmdInterpreterAPI {
'remix.debugHelp()': 'Display help message for debugging' 'remix.debugHelp()': 'Display help message for debugging'
} }
} }
call (message) {
return this._components.terminal.externalApi.request(message)
}
log () { arguments[0] != null ? this._components.terminal.commands.html(arguments[0]) : this._components.terminal.commands.html(arguments[1]) } log () { arguments[0] != null ? this._components.terminal.commands.html(arguments[0]) : this._components.terminal.commands.html(arguments[1]) }
highlight (rawLocation) { highlight (rawLocation) {
var self = this var self = this

@ -12,7 +12,7 @@ function GistHandler (_window) {
var gistId var gistId
if (params['gist'] === '') { if (params['gist'] === '') {
loadingFromGist = true loadingFromGist = true
modalDialogCustom.prompt(null, 'Enter the URL or ID of the Gist you would like to load.', null, (target) => { modalDialogCustom.prompt(null, 'Enter the ID of the Gist you would like to load.', null, (target) => {
if (target !== '') { if (target !== '') {
gistId = getGistId(target) gistId = getGistId(target)
if (gistId) { if (gistId) {

@ -145,7 +145,7 @@ class MultiParamManager {
var button = yo`<button onclick=${() => { multiOnClick() }} class="${css.instanceButton}"></button>` var button = yo`<button onclick=${() => { multiOnClick() }} class="${css.instanceButton}"></button>`
this.contractActionsContainerMulti = yo`<div class="${css.contractActionsContainerMulti}" > this.contractActionsContainerMulti = yo`<div class="${css.contractActionsContainerMulti}" >
<div class="${css.contractActionsContainerMultiInner}" > <div class="${css.contractActionsContainerMultiInner} text-dark" >
<div onclick=${() => { this.switchMethodViewOff() }} class="${css.multiHeader}"> <div onclick=${() => { this.switchMethodViewOff() }} class="${css.multiHeader}">
<div class="${css.multiTitle}">${title}</div> <div class="${css.multiTitle}">${title}</div>
<i class='fa fa-angle-up ${css.methCaret}'></i> <i class='fa fa-angle-up ${css.methCaret}'></i>

@ -58,8 +58,8 @@ export class RemixAppManager extends AppManagerApi {
plugins () { plugins () {
let vyper = { let vyper = {
displayName: 'vyper',
name: 'vyper', name: 'vyper',
displayName: 'Vyper',
events: ['compilationFinished'], events: ['compilationFinished'],
methods: [], methods: [],
notifications: {}, notifications: {},
@ -70,8 +70,8 @@ export class RemixAppManager extends AppManagerApi {
location: 'swapPanel' location: 'swapPanel'
} }
var pipeline = { var pipeline = {
displayName: 'pipeline',
name: 'pipeline', name: 'pipeline',
displayName: 'Pipeline',
events: [], events: [],
methods: [], methods: [],
notifications: { notifications: {

@ -31,7 +31,9 @@ var css = csjs`
word-break: break-word; word-break: break-word;
min-width: 230px; min-width: 230px;
} }
.inputGroupText {
width: 100%;
}
.title .copy { .title .copy {
color: var(--primary); color: var(--primary);
} }
@ -42,7 +44,6 @@ var css = csjs`
flex-wrap: nowrap; flex-wrap: nowrap;
} }
.instance { .instance {
min-width: 310px;
display: block; display: block;
/* display: flex; */ /* display: flex; */
flex-direction: column; flex-direction: column;
@ -101,13 +102,12 @@ var css = csjs`
.contractProperty { .contractProperty {
overflow: auto; overflow: auto;
margin-bottom: 0.4em; margin-bottom: 0.4em;
margin-top: 1em;
width:100%; width:100%;
} }
.contractProperty.hasArgs input { .contractProperty.hasArgs input {
min-width: 200px;
padding: .36em; padding: .36em;
border-radius: 5px; border-radius: 5px;
width: 70%;
} }
.contractProperty .contractActionsContainerSingle input{ .contractProperty .contractActionsContainerSingle input{
border-top-left-radius: 0; border-top-left-radius: 0;
@ -130,13 +130,15 @@ var css = csjs`
/* background-color:var(--info); */ /* background-color:var(--info); */
min-width: 100px; min-width: 100px;
width: 100px; width: 100px;
font-size: 10px;
margin:0; margin:0;
word-break: inherit; word-break: inherit;
outline: none; outline: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.contractProperty input { .contractProperty input {
width: 75% /* width: 75% */
} }
.contractProperty > .value { .contractProperty > .value {
box-sizing: border-box; box-sizing: border-box;
@ -187,7 +189,7 @@ var css = csjs`
cursor: default; cursor: default;
} }
.contractProperty .contractActionsContainerMultiInner .multiArg label{ .contractProperty .contractActionsContainerMultiInner .multiArg label{
text-align: center; text-align: right;
} }
.multiHeader .methCaret { .multiHeader .methCaret {
float: right; float: right;
@ -205,6 +207,7 @@ var css = csjs`
} }
.multiArg { .multiArg {
margin-bottom: 8px; margin-bottom: 8px;
display: flex;
} }
.multiArg input{ .multiArg input{
padding: 5px; padding: 5px;
@ -214,13 +217,12 @@ var css = csjs`
float: left; float: left;
margin-right: 6px; margin-right: 6px;
font-size: 10px; font-size: 10px;
width: 20%; min-width: 30%;
} }
.multiArg button { .multiArg button {
border-radius: 3px; border-radius: 3px;
float: right; float: right;
margin-right: 5%; margin-right: 2%;
font-size: 10px;
border-width: 1px; border-width: 1px;
width: inherit; width: inherit;
} }
@ -230,6 +232,7 @@ var css = csjs`
} }
.hasArgs .multiArg input { .hasArgs .multiArg input {
border-left: 1px solid #dddddd; border-left: 1px solid #dddddd;
width: 100%;
} }
.hasArgs input { .hasArgs input {
display: block; display: block;
@ -244,7 +247,6 @@ var css = csjs`
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
border-right: 0; border-right: 0;
/* height: 25px; */
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -253,7 +255,7 @@ var css = csjs`
border-radius: 3px; border-radius: 3px;
} }
.contractActionsContainerMultiInner .multiArg i { .contractActionsContainerMultiInner .multiArg i {
padding-right: 26px; padding-right: 15px;
padding-top: 5px; padding-top: 5px;
float: right; float: right;
}, },

@ -7,16 +7,26 @@ var TxRunner = remixLib.execution.txRunner
var txHelper = remixLib.execution.txHelper var txHelper = remixLib.execution.txHelper
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var executionContext = remixLib.execution.executionContext var executionContext = remixLib.execution.executionContext
import { ApiFactory } from 'remix-plugin' import { UdappApi } from 'remix-plugin'
import { EventEmitter } from 'events'
module.exports = class UniversalDApp extends ApiFactory { const profile = {
name: 'udapp',
displayName: 'universal dapp',
description: 'service - run transaction and access account',
permission: true
}
module.exports = class UniversalDApp extends UdappApi {
constructor (registry) { constructor (registry) {
super() super(profile)
this.events = new EventEmitter()
this.event = new EventManager() this.event = new EventManager()
this._deps = { this._deps = {
config: registry.get('config').api config: registry.get('config').api
} }
this._txRunnerAPI = { this._txRunnerAPI = {
config: this._deps.config, config: this._deps.config,
detectNetwork: (cb) => { detectNetwork: (cb) => {
@ -32,13 +42,12 @@ module.exports = class UniversalDApp extends ApiFactory {
executionContext.event.register('contextChanged', this.resetEnvironment.bind(this)) executionContext.event.register('contextChanged', this.resetEnvironment.bind(this))
} }
get profile () { // TODO : event should be triggered by Udapp instead of TxListener
return { /** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */
name: 'udapp', startListening (txlistener) {
displayName: 'universal dapp', txlistener.event.register('newTransaction', (tx) => {
methods: ['runTestTx', 'getAccounts', 'createVMAccount'], this.events.emit('newTransaction', tx)
description: 'service - run transaction and access account' })
}
} }
resetEnvironment () { resetEnvironment () {
@ -242,7 +251,7 @@ module.exports = class UniversalDApp extends ApiFactory {
* *
* @param {Object} tx - transaction. * @param {Object} tx - transaction.
*/ */
runTestTx (tx) { sendTransaction (tx) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
executionContext.detectNetwork((error, network) => { executionContext.detectNetwork((error, network) => {
if (error) return reject(error) if (error) return reject(error)

@ -48,7 +48,7 @@
<input type="button" id="getcontentof">get content of</input> <br> <input type="button" id="getcontentof">get content of</input> <br>
<input type="button" id="getcurrent">get current</input> <br> <input type="button" id="getcurrent">get current</input> <br>
<input type="button" id="sethighlight">set highlight</input> <br> <input type="button" id="sethighlight">set highlight</input> <br>
<input type="button" id="getfilesfrompath">get files from path</input> <br> <input type="button" id="getFolder">get files from path</input> <br>
<input type="button" id="addnetwork">add network</input> <br> <input type="button" id="addnetwork">add network</input> <br>
<input type="button" id="removenetwork">remove network</input> <br> <input type="button" id="removenetwork">remove network</input> <br>
<br> <br>

@ -18,7 +18,7 @@ window.onload = function () {
}) })
setInterval(function () { setInterval(function () {
extension.call('app', 'detectNetWork', [], function (error, result) { extension.call('network', 'detectNetWork', [], function (error, result) {
console.log(error, result) console.log(error, result)
}) })
}, 5000) }, 5000)
@ -74,18 +74,18 @@ window.onload = function () {
function (error, result) { console.log(error, result) }) function (error, result) { console.log(error, result) })
}) })
document.querySelector('input#getfilesfrompath').addEventListener('click', function () { document.querySelector('input#getFolder').addEventListener('click', function () {
extension.call('editor', 'getFilesFromPath', [document.getElementById('filename').value], extension.call('fileManager', 'getFolder', [document.getElementById('filename').value],
function (error, result) { console.log(error, result) }) function (error, result) { console.log(error, result) })
}) })
document.querySelector('input#addnetwork').addEventListener('click', function () { document.querySelector('input#addnetwork').addEventListener('click', function () {
extension.call('app', 'addProvider', [document.getElementById('filename').value, document.getElementById('valuetosend').value], extension.call('network', 'addProvider', [document.getElementById('filename').value, document.getElementById('valuetosend').value],
function (error, result) { console.log(error, result) }) function (error, result) { console.log(error, result) })
}) })
document.querySelector('input#removenetwork').addEventListener('click', function () { document.querySelector('input#removenetwork').addEventListener('click', function () {
extension.call('app', 'removeProvider', [document.getElementById('filename').value], extension.call('network', 'removeProvider', [document.getElementById('filename').value],
function (error, result) { console.log(error, result) }) function (error, result) { console.log(error, result) })
}) })
} }

Loading…
Cancel
Save