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 () {
remix.call('app', 'detectNetWork', [], function (error, result) {
remix.call('network', 'detectNetWork', [], function (error, result) {
if (error) console.log(error)
if (network.innerHTML !== result[0].name + ' - ' + result[0].id) {
currentNetWork = result[0].name

@ -65,7 +65,7 @@
},
"dependencies": {
"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"
},
"repository": {

@ -6,7 +6,6 @@ var yo = require('yo-yo')
var async = require('async')
var request = require('request')
var remixLib = require('remix-lib')
var EventManager = require('./lib/events')
var registry = require('./global/registry')
var UniversalDApp = require('./universal-dapp.js')
var UniversalDAppUI = require('./universal-dapp-ui.js')
@ -54,9 +53,8 @@ import { EntityStore } from './lib/store'
import { RemixAppManager } from './remixAppManager'
import { LandingPage } from './app/ui/landing-page/landing-page'
import framingService from './framingService'
import { ApiFactory } from 'remix-plugin'
import { TxListenerModule } from './app/tabs/txlistener-module'
import { ThemeModule } from './app/tabs/theme-module'
import { NetworkModule } from './app/tabs/network-module'
var css = csjs`
html { box-sizing: border-box; }
@ -116,11 +114,9 @@ var css = csjs`
}
`
class App extends ApiFactory {
class App {
constructor (api = {}, events = {}, opts = {}) {
super()
var self = this
this.event = new EventManager()
self._components = {}
registry.put({api: self, name: 'app'})
@ -167,18 +163,10 @@ class App extends ApiFactory {
init () {
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)
}
get profile () {
return {
name: 'app',
description: 'service - provides information about current context (network).',
methods: ['getExecutionContextProvider', 'getProviderEndpoint', 'detectNetWork', 'addProvider', 'removeProvider']
}
}
render () {
var self = this
if (self._view.el) return self._view.el
@ -250,45 +238,6 @@ class App extends ApiFactory {
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
@ -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'})
// ----------------- UniversalDApp -----------------
var udapp = new UniversalDApp(registry)
const udapp = new UniversalDApp(registry)
// TODO: to remove when possible
registry.put({api: udapp, name: 'udapp'})
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>`)
})
var udappUI = new UniversalDAppUI(udapp, registry)
const udappUI = new UniversalDAppUI(udapp, registry)
// TODO: to remove when possible
registry.put({api: udappUI, name: 'udappUI'})
// ----------------- Tx listener -----------------
var transactionReceiptResolver = new TransactionReceiptResolver()
const transactionReceiptResolver = new TransactionReceiptResolver()
var txlistener = new Txlistener({
const txlistener = new Txlistener({
api: {
contracts: function () {
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
}})
registry.put({api: txlistener, name: 'txlistener'})
udapp.startListening(txlistener)
var eventsDecoder = new EventsDecoder({
const eventsDecoder = new EventsDecoder({
api: {
resolveReceipt: function (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'})
/*
that proxy is used by appManager to broadcast new transaction event
*/
const txListenerModule = new TxListenerModule(txlistener)
txlistener.startListening()
// 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 ----------------------------
self._components.fileManager = new FileManager()
var fileManager = self._components.fileManager
const fileManager = self._components.fileManager
registry.put({api: fileManager, name: 'filemanager'})
// ----------------- Network ----------------------------
const networkModule = new NetworkModule()
registry.put({api: networkModule, name: 'network'})
// ----------------- theme module ----------------------------
const themeModule = new 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' })
// ----------------- Renderer -----------------
var renderer = new Renderer()
const renderer = new Renderer()
registry.put({api: renderer, name: 'renderer'})
// ----------------- 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 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 swapPanelApi = new SwapPanelApi(swapPanelComponent, verticalIconsComponent) // eslint-disable-line
const mainPanelApi = new SwapPanelApi(mainPanelComponent, 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'})
pluginManagerComponent.setApp(appManager)
pluginManagerComponent.setStore(appStore)
self._components.editorpanel.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
appManager.init([
this.api(),
landingPage.api(),
udapp.api(),
fileManager.api(),
sourceHighlighters.api(),
txListenerModule.api(),
filePanel.api(),
// { profile: support.profile(), api: support },
settings.api(),
pluginManagerComponent.api(),
networkModule.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
// parent will send the message upon the "load" event.
var filesToLoad = null
var loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later
let filesToLoad = null
let loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later
window.addEventListener('message', function (ev) {
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)
}
var txLogger = new TxLogger() // eslint-disable-line
const txLogger = new TxLogger() // eslint-disable-line
txLogger.event.register('debuggingRequested', (hash) => {
if (!appStore.isActive('debugger')) appManager.activateOne('debugger')
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)
var queryParams = new QueryParams()
const queryParams = new QueryParams()
var loadingFromGist = self.loadFromGist(queryParams.get())
const loadingFromGist = self.loadFromGist(queryParams.get())
if (!loadingFromGist) {
// insert ballot contract if there are no files to show
self._components.filesProviders['browser'].resolveDirectory('browser', (error, filesList) => {

@ -2,7 +2,7 @@ const yo = require('yo-yo')
const csjs = require('csjs-inject')
const EventEmitter = require('events')
const LocalPlugin = require('./local-plugin')
import { Plugin, ApiFactory } from 'remix-plugin'
import { Plugin, BaseApi } from 'remix-plugin'
const css = csjs`
.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 () {
super()
super(profile)
this.event = new EventEmitter()
this.views = {
root: null,
@ -47,19 +58,6 @@ class PluginManagerComponent extends ApiFactory {
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) {
this.appManager = appManager
}

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

@ -1,6 +1,7 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var helper = require('../../lib/helper')
let globalRegistry = require('../../global/registry')
const EventEmitter = require('events')
@ -32,6 +33,11 @@ class VerticalIconComponent {
})
this.store.event.on('add', (api) => { })
this.store.event.on('remove', (api) => { })
let themeModule = globalRegistry.get('themeModule').api
themeModule.events.on('themeChanged', (type) => {
this.onThemeChanged(type)
})
}
stopListenOnStatus (api) {
@ -56,8 +62,15 @@ class VerticalIconComponent {
* Add an icon to the map
* @param {ModuleProfile} profile The profile of the module
*/
addIcon ({kind, name, icon, displayName}) {
this.icons[name] = yo`<div class="${css.icon}" onclick="${(e) => { this._iconClick(name) }}" plugin="${name}" title="${displayName || name}" ><img src="${icon}" alt="${name}" /></div>`
addIcon ({kind, name, icon, displayName, tooltip}) {
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])
}
@ -69,7 +82,7 @@ class VerticalIconComponent {
setIconStatus (name, status) {
const el = this.icons[name]
if (!el) return
let statusEl = el.querySelector('i')
let statusEl = el.querySelector('span')
if (statusEl) {
el.removeChild(statusEl)
}
@ -77,7 +90,17 @@ class VerticalIconComponent {
let key = helper.checkSpecialChars(status.key) ? '' : status.key
let type = helper.checkSpecialChars(status.type) ? '' : status.type
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])
}
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) {
let currentTitle = currentActive.getAttribute('title')
currentActive.classList.toggle(`${css.active}`)
if (currentTitle !== name) {
let activate = this.view.querySelector(`[plugin="${name}"]`)
if (activate) activate.classList.toggle(`${css.active}`)
}
} else {
let activate = this.view.querySelector(`[plugin="${name}"]`)
if (activate) activate.classList.toggle(`${css.active}`)
currentActive.classList.remove(css.active)
}
}
/**
* Add active for the new activated icon
* @param {string} name Name of profile of the module to activate
*/
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)
}
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) {
this.select(name)
}
@ -173,31 +231,29 @@ const css = csjs`
margin-left: 10px;
margin-top: 15px;
}
.icon {
.icon {
cursor: pointer;
margin-bottom: 12px;
width: 36px;
height: 36px;
padding: 3px;
position: relative;
border-radius: 8px;
}
.icon img {
width: 28px;
height: 28px;
padding: 4px;
filter: invert(0.5);
}
.image {
}
.icon svg {
width: 28px;
height: 28px;
padding: 4px;
}
.icon.active {
border: solid 3px hsla(229, 75%, 87%, 1);
border-radius: 8px;
padding-top: 1px;
padding-left: 1px;
}
.icon[title='settings'] {
.icon[title='Settings'] {
position: absolute;
bottom: 0;
}
@ -206,4 +262,15 @@ const css = csjs`
bottom: 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.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')
if (this.itemSelected.firstChild) {
this.itemSelected.firstChild.setAttribute('style', 'margin-left: 2px')

@ -1,24 +1,24 @@
'use strict'
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 () {
super()
super(profile)
this.highlighters = {}
}
get profile () {
return {
displayName: 'source highlighters',
name: 'sourceHighlighters',
methods: ['highlight', 'discardHighlight'],
description: 'service - highlight source code'
}
}
highlight (lineColumnPos, filePath, hexColor) {
const { from } = this.currentRequest
try {

@ -33,25 +33,6 @@ document.head.appendChild(yo`
.ace_gutter-cell.ace_breakpoint{
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>
`)
@ -97,10 +78,14 @@ class Editor {
// Editor Setup
const el = yo`<div id="input"></div>`
this.editor = ace.edit(el)
ace.acequire('ace/ext/language_tools')
// Unmap ctrl-t & ctrl-f
this.editor.commands.bindKeys({ 'ctrl-t': null })
// Unmap ctrl-l & cmd-l
this.editor.commands.bindKeys({
'ctrl-L': null,
'Command-L': null
})
// shortcuts for "Ctrl-"" and "Ctrl+"" to increase/decrease font size of the editor
this.editor.commands.addCommand({
@ -150,6 +135,13 @@ class Editor {
}
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
// Gutter Mouse down

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

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

@ -2,12 +2,16 @@
var EventManager = require('../../lib/events')
import { ApiFactory } from 'remix-plugin'
class FilesTree extends ApiFactory {
import { BaseApi } from 'remix-plugin'
class FilesTree extends BaseApi {
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.storage = storage
this.type = name
@ -15,15 +19,6 @@ class FilesTree extends ApiFactory {
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) {
cb(null, this._exists(path))
}
@ -114,7 +109,11 @@ class FilesTree extends ApiFactory {
if (!this.storage.rename(unprefixedoldPath, unprefixednewPath)) {
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 false
@ -122,7 +121,7 @@ class FilesTree extends ApiFactory {
resolveDirectory (path, callback) {
if (path[0] === '/') path = path.substring(1)
if (!path) return callback(null, { [this.type]: { } })
if (!path) return callback(null, { [this.type]: {} })
var tree = {}
path = this.removePrefix(path)
@ -143,7 +142,6 @@ class FilesTree extends ApiFactory {
if (path[0] === '/') return path.substring(1)
return path
}
}
module.exports = FilesTree

@ -1,20 +1,31 @@
'use strict'
import { ApiFactory } from 'remix-plugin'
import yo from 'yo-yo'
const EventEmitter = require('events')
var globalRegistry = require('../../global/registry')
var CompilerImport = require('../compiler/compiler-imports')
var toaster = require('../ui/tooltip')
import { FileSystemApi } from 'remix-plugin'
/*
attach to files event (removed renamed)
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) {
super()
super(profile)
this.openedFiles = {} // list all opened files
this.events = new EventEmitter()
this._components = {}
@ -42,18 +53,6 @@ class FileManager extends ApiFactory {
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) {
if (!isFolder) {
this._deps.config.set('currentFile', '')
@ -223,7 +222,7 @@ class FileManager extends ApiFactory {
}
}
getFilesFromPath (path) {
getFolder (path) {
// TODO : Change provider with promise
return new Promise((resolve, reject) => {
const provider = this.fileProviderOf(path)

@ -1,5 +1,5 @@
import { ApiFactory } from 'remix-plugin'
let globalRegistry = require('../../global/registry')
import { BaseApi } from 'remix-plugin'
var yo = require('yo-yo')
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) {
super()
super(profile)
this.fileSystemExplorer = fileSystemExplorer
this.locahostProvider = locahostProvider
}
get profile () {
return {
name: 'remixd',
methods: [],
events: [],
description: 'using Remixd daemon, allow to access file system',
kind: 'other'
}
}
deactivate () {
this.locahostProvider.close((error) => {
if (error) console.log(error)

@ -100,7 +100,8 @@ class EditorPanel {
self._components.contextView = contextView
self._components.terminal = new Terminal({
udapp: self._deps.udapp,
compilers: {}
appStore: self.appStore,
appManager: self.appManager
},
{
getPosition: (event) => {
@ -198,7 +199,7 @@ class EditorPanel {
self._adjustLayout('top', self.data._layout.top.offset)
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

@ -6,7 +6,7 @@ var { RemixdHandle } = require('../files/remixd-handle.js')
var globalRegistry = require('../../global/registry')
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
@ -27,10 +27,21 @@ var canUpload = window.File || window.FileReader || window.FileList || window.Bl
- 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) {
super()
super(profile)
var self = this
self._components = {}
self._components.registry = localRegistry || globalRegistry
@ -132,18 +143,5 @@ module.exports = class Filepanel extends ApiFactory {
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;
position : relative;
width : 100%;
padding-left : 6px;
padding-top : 6px;
}
.gist {
padding : 10px;
@ -50,7 +52,7 @@ var css = csjs`
.uploadFile label {
cursor : pointer;
}
.treeviews {
.treeview {
overflow-y : auto;
}
.dialog {

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

@ -27,7 +27,7 @@ export class TabProxy {
this._view.filetabs.activateTab(file)
return
}
this.addTab(file, () => {
this.addTab(file, '', () => {
this.fileManager.switchFile(file)
this.event.emit('switchFile', file)
},
@ -39,7 +39,7 @@ export class TabProxy {
fileManager.events.on('fileRenamed', (oldName, newName) => {
this.removeTab(oldName)
this.addTab(newName, () => {
this.addTab(newName, '', () => {
this.fileManager.switchFile(newName)
this.event.emit('switchFile', newName)
},
@ -54,6 +54,7 @@ export class TabProxy {
if (profile.location === 'mainPanel') {
this.addTab(
name,
profile.displayName,
() => this.event.emit('switchApp', name),
() => {
this.event.emit('closeApp', name)
@ -104,9 +105,13 @@ export class TabProxy {
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('/')
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({
id: name,
title,
@ -117,7 +122,7 @@ export class TabProxy {
}
removeTab (name) {
this._view.filetabs.closeTab(name)
this._view.filetabs.removeTab(name)
delete this._handlers[name]
}

@ -17,6 +17,7 @@ var AutoCompletePopup = require('../ui/auto-complete-popup')
var csjs = require('csjs-inject')
var css = require('./styles/terminal-styles')
import { BaseApi } from 'remix-plugin'
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>`
class Terminal {
const profile = {
displayName: 'Terminal',
name: 'terminal',
methods: [],
events: [],
description: ' - ',
required: false
}
class Terminal extends BaseApi {
constructor (opts, api) {
super(profile)
var self = this
self.event = new EventManager()
self._api = api
@ -62,12 +73,12 @@ class Terminal {
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) {
let textList = self._view.input.innerText.split(' ')
textList.pop()
textList.push(input)
self._view.input.innerText = `${textList}`.replace(/,/g, ' ')
self._view.input.innerText = textList
self._view.input.focus()
self.putCursor2End(self._view.input)
})
@ -103,14 +114,18 @@ class Terminal {
self._jsSandboxContext = {}
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
register(self)
}
focus () {
if (this._view.input) this._view.input.focus()
}
render () {
var self = this
if (self._view.el) return self._view.el
@ -127,7 +142,7 @@ class Terminal {
`
self._view.icon = yo`
<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`
<div onmousedown=${mousedown} class=${css.dragbarHorizontal}></div>`
self._view.dropdown = self._components.dropdown.render()
@ -136,7 +151,7 @@ class Terminal {
self._view.bar = yo`
<div class="${css.bar}">
${self._view.dragbar}
<div class="${css.menu} bg-light">
<div class="${css.menu} border-top bg-light">
${self._view.icon}
<div class=${css.clear} onclick=${clear}>
<i class="fa fa-ban" aria-hidden="true" title="Clear console"
@ -649,7 +664,6 @@ class Terminal {
function domTerminalFeatures (self, scopedCommands) {
return {
compilers: self._opts.compilers,
swarmgw,
ethers,
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 {
margin-top: 1%;
max-height: 300px;
overflow-y: auto;
}
.buttons {
margin: 1rem 0;

@ -3,31 +3,29 @@ var StaticAnalysis = require('../staticanalysis/staticAnalysisView')
var EventManager = require('../../lib/events')
var css = require('./styles/analysis-tab-styles')
import { ApiFactory } from 'remix-plugin'
import { BaseApi } from 'remix-plugin'
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) {
super()
super(profile)
this.event = new EventManager()
this.events = new EventEmitter()
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 () {
var staticanalysis = new StaticAnalysis()
staticanalysis.event.register('staticAnaysisWarning', (count) => {

@ -17,12 +17,26 @@ var css = require('./styles/compile-tab-styles')
const CompileTabLogic = require('./compileTab/compileTab.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) {
super()
super(profile)
this.events = new EventEmitter()
this._view = {
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
*/
@ -166,7 +166,7 @@ class CompileTab extends ApiFactory {
<!-- Select Compiler Version -->
<header class="navbar navbar-light bg-light input-group mb-3 ${css.compilerArticle}">
<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>
<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>`)}
@ -181,11 +181,8 @@ class CompileTab extends ApiFactory {
Compilation Details
</button>
<!-- Copy to Clipboard -->
<div class="${css.contractHelperButtons}">
<div class="${css.contractHelperButtons} btn-secondary">
<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">
<button class="btn btn-secondary" title="Copy ABI to clipboard" onclick="${() => { this.copyABI() }}">
<i class="${css.copyIcon} fa fa-clipboard" aria-hidden="true"></i>

@ -141,7 +141,7 @@ class CompilerContainer {
<article>
<header class="navbar navbar-light bg-light input-group mb-3">
<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>
${this._view.versionSelector}
</header>

@ -3,28 +3,26 @@ var css = require('./styles/debugger-tab-styles')
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 () {
super()
super(profile)
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 () {
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 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) {
super()
super(profile)
this.event = new EventManager()
this.renderInstanceContainer()
@ -28,19 +39,6 @@ class RunTab extends ApiFactory {
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 () {
this.container = yo`<div class="${css.runTabView}" id="runTabView" ></div>`

@ -44,7 +44,7 @@ class ContractDropdownUI {
}
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>`
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>
`
this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this))
this.setInputParamsPlaceHolder()
return el
}

@ -64,7 +64,7 @@ class SettingsUI {
value="web3" name="executionContext"> Web3 Provider
</option>
</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>
`
@ -93,13 +93,15 @@ class SettingsUI {
var valueEl = yo`
<div class="${css.crow}">
<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">
<select name="unit" class="form-control ${css.gasNval} ${css.col2_2}" id="unit">
<option data-unit="wei">wei</option>
<option data-unit="gwei">gwei</option>
<option data-unit="finney">finney</option>
<option data-unit="ether">ether</option>
</select>
<div class="${css.gasValueContainer}">
<input type="text" class="form-control ${css.gasNval} ${css.col2}" id="value" value="0" title="Enter the value and choose the unit">
<select name="unit" class="form-control ${css.gasNvalUnit} ${css.col2_2}" id="unit">
<option data-unit="wei">wei</option>
<option data-unit="gwei">gwei</option>
<option data-unit="finney">finney</option>
<option data-unit="ether">ether</option>
</select>
</div>
</div>
`

@ -4,11 +4,22 @@ var tooltip = require('../ui/tooltip')
var copyToClipboard = require('../ui/copy-to-clipboard')
var EventManager = require('../../lib/events')
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) {
super()
super(profile)
this.config = config
this.editor = editor
this.appManager = appManager
@ -30,18 +41,7 @@ module.exports = class SettingsTab extends ApiFactory {
initTheme () {
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 () {
let themes = this._deps.themeModule.getThemes()
const onswitchTheme = (event, name) => {

@ -12,10 +12,11 @@ var css = csjs`
font-size: 12px;
display: flex;
justify-content: space-between;
padding-left: 15px;
}
.settings {
margin-bottom: 2%;
padding: 10px 15px 15px 15px;
padding: 10px 0px 15px 15px;
}
.recorderCount {
/* margin-right: 30px; */
@ -38,7 +39,6 @@ var css = csjs`
}
.col1_1 {
font-size: 12px;
width: 15%;
min-width: 75px;
float: left;
align-self: center;
@ -47,12 +47,13 @@ var css = csjs`
display: flex;
align-items: center;
position: relative;
width: 259px;
width: 100%;
padding-right: 25px;
}
.account {
display: flex;
align-items: center;
width: 266px;
width: 90%;
}
.col2 {
border-radius: 3px;
@ -62,12 +63,14 @@ var css = csjs`
min-width: 164px;
}
.col2_2 {
width: 82px;
width: 100px;
min-width: 82px;
position: absolute;
margin-left: 100px;
}
.select {
font-weight: normal;
width: 250px;
width: 100%;
}
.instanceContainer {
display: flex;
@ -85,7 +88,8 @@ var css = csjs`
text-align: center;
}
.container {
margin-bottom: 2%;
margin-bottom: 4%;
padding-left: 15px;
}
.recorderCollapsedView,
.recorderExpandedView {
@ -141,6 +145,7 @@ var css = csjs`
.noInstancesText {
font-style: italic;
text-align: left;
padding-left: 15px;
}
.pendingTxsText {
font-style: italic;
@ -188,7 +193,7 @@ var css = csjs`
color: var(--warning);
}
.errorIcon {
color: var(--danger);
color: var(--warning);
margin-left: 15px;
}
.failDesc {
@ -204,7 +209,7 @@ var css = csjs`
color: grey;
width: 100%;
height: 100%;
padding-right: 28px;
padding-right: 53px;
pointer-events: none;
}
.networkItem {
@ -224,12 +229,22 @@ var css = csjs`
.infoDeployAction {
margin-left: 5px;
font-size: 13px;
color: var(--danger);
color: var(--info);
}
.gasValueContainer {
flex-direction: row;
display: flex;
}
.gasNval {
transform: scale(0.7);
transform-origin: left;
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 css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests')
import { ApiFactory } from 'remix-plugin'
import { BaseApi } from 'remix-plugin'
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) {
super()
super(profile)
this.compileTab = compileTab
this._view = { el: null }
this.compileTab = compileTab
@ -20,18 +30,6 @@ module.exports = class TestTab extends ApiFactory {
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 () {
this.listenToEvents()
}
@ -138,12 +136,14 @@ module.exports = class TestTab extends ApiFactory {
}
runTest (testFilePath, callback) {
this.loading.hidden = false
this.fileManager.fileProviderOf(testFilePath).get(testFilePath, (error, content) => {
if (error) return
var runningTest = {}
runningTest[testFilePath] = { content }
remixTests.runTestSources(runningTest, (result) => { this.testCallback(result) }, (_err, result, cb) => { this.resultsCallback(_err, result, cb) }, (error, result) => {
this.updateFinalResult(error, result, testFilePath)
this.loading.hidden = true
callback(error)
}, (url, cb) => {
return this.compileTab.compileTabLogic.importFileCb(url, cb)
@ -152,6 +152,7 @@ module.exports = class TestTab extends ApiFactory {
}
runTests () {
this.loading.hidden = false
this.testsOutput.innerHTML = ''
this.testsSummary.innerHTML = ''
var tests = this.data.selectedTests
@ -161,7 +162,8 @@ module.exports = class TestTab extends ApiFactory {
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.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`
<div class="${css.testTabView} card" id="testView">
<div class="${css.infoBox}">
@ -173,7 +175,7 @@ module.exports = class TestTab extends ApiFactory {
<br/>
For more details, see
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 class="${css.tests}">
<div class="${css.buttons}">
@ -189,7 +191,7 @@ module.exports = class TestTab extends ApiFactory {
</div>
${this.testList}
<hr>
<div class="${css.buttons}" ><h6>Results:</h6></div>
<div class="${css.buttons}" ><h6>Results:${this.loading}</h6></div>
${this.testsOutput}
${this.testsSummary}
</div>

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

@ -1,4 +1,4 @@
import { ApiFactory } from 'remix-plugin'
import { BaseApi } from 'remix-plugin'
import { EventEmitter } from 'events'
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: '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: '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'}
]
export class ThemeModule extends ApiFactory {
const profile = {
name: 'theme',
events: ['themeChanged'],
methods: ['switchTheme', 'getThemes', 'currentTheme']
}
export class ThemeModule extends BaseApi {
constructor () {
super()
super(profile)
this.events = new EventEmitter()
this.storage = new Storage('style:')
this.themes = themes.reduce((acc, theme) => ({ ...acc, [theme.name]: theme }), {})
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 */
currentTheme () {
return this.themes[this.active]
@ -58,6 +56,7 @@ export class ThemeModule extends ApiFactory {
this.storage.set('theme', next)
document.getElementById('theme-link').setAttribute('href', nextTheme.url)
document.documentElement.style.setProperty('--theme', nextTheme.quality)
if (themeName) this.active = themeName
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
self.event = new EventManager()
self.isOpen = false
self.opts = opts
self.data = {
_options: opts.options || []
_options: []
}
self._components = {
modal: null
@ -33,7 +34,9 @@ class AutoCompletePopup {
self._startingElement = 0
self._elementsToShow = 4
self._selectedElement = 0
this.extraCommands = []
this.render()
this.extendAutocompletion()
}
render () {
@ -149,18 +152,24 @@ class AutoCompletePopup {
this.isOpen = true
this.data._options = []
Commands.allPrograms.forEach(item => {
let program = getKeyOf(item)
const program = getKeyOf(item)
if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) {
this.data._options.push(item)
} else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) {
Commands.allCommands.forEach(item => {
let command = getKeyOf(item)
const command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) {
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 only one option and tab is pressed, we resolve it
@ -188,6 +197,21 @@ class AutoCompletePopup {
this._selectedElement = 0
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) {

@ -16,17 +16,15 @@ module.exports = class Card {
if (self._view.el) return self._view.el
self._view.cardBody = yo`<div class=${css.cardBody}></div>`
self._view.arrow = yo`<i class="${css.arrow} fa fa-angle-down"
onclick=${(ev) => trigger(ev.target)}></i>`
self._view.arrow = yo`<i class="${css.arrow} fa fa-angle-down" onclick="${() => trigger(this)}"></i>`
self._view.expandCollapseButton = yo`
<div class=${css.expandCollapseButton}>${self._view.arrow}</div>`
self._view.statusBar = yo`<div class=${css.statusBar}>${self._opts.collapsedView}</div>`
self._view.cardHeader = yo`
<div class=${css.cardHeader}>
<div class=${css.cardTitles}>
<div class=${css.cardHeader} onclick=${() => trigger(self._view.arrow)}>
<div class="p-1 ${css.cardTitles}">
<div class=${css.cardTitle}>${self._opts.title}</div>
${self._view.statusBar}
</div>
@ -36,14 +34,16 @@ module.exports = class Card {
function trigger (el) {
var body = self._view.cardBody
var status = self._view.statusBar
el.classList.toggle('fa-angle-up')
var arrow = el.classList.toggle('fa-angle-down') ? 'up' : 'down'
self.event.trigger('expandCollapseCard', [arrow, body, status])
if (el.classList) {
el.classList.toggle('fa-angle-up')
var arrow = el.classList.toggle('fa-angle-down') ? 'up' : 'down'
self.event.trigger('expandCollapseCard', [arrow, body, status])
}
}
// HTML
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.cardBody}
</div>`
@ -55,7 +55,7 @@ module.exports = class Card {
const css = csjs`
.cardContainer {
padding: 10px 15px 15px 0;
padding : 10px 15px 15px 0;
margin-bottom : 2%;
}
.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('solidityStaticAnalysis')
appManager.ensureActivated('solidityUnitTesting')
globalRegistry.get('filemanager').api.switchFile()
globalRegistry.get('verticalicon').api.select('solidity')
}, () => {}),
new Workspace(
@ -31,7 +30,6 @@ export const defaultWorkspaces = (appManager) => {
() => {
appManager.ensureActivated('vyper')
appManager.ensureActivated('run')
globalRegistry.get('filemanager').api.switchFile()
globalRegistry.get('verticalicon').api.select('vyper')
}, () => {}),
new Workspace('Debugger', 'Debug transactions with remix', false, () => {

@ -66,7 +66,7 @@ module.exports = {
function prompt (title, text, hidden, inputValue, ok, cancel, focus) {
if (!inputValue) inputValue = ''
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>`,
{
fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) }

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

@ -13,5 +13,18 @@ export default {
verticalIconApi.select('fileExplorers')
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
}
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.setFile(path, content)': 'set the content of the file located at the given path',
'remix.debug(hash)': 'Start debugging a transaction.',
@ -40,6 +41,9 @@ class CmdInterpreterAPI {
'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]) }
highlight (rawLocation) {
var self = this

@ -12,7 +12,7 @@ function GistHandler (_window) {
var gistId
if (params['gist'] === '') {
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 !== '') {
gistId = getGistId(target)
if (gistId) {

@ -145,7 +145,7 @@ class MultiParamManager {
var button = yo`<button onclick=${() => { multiOnClick() }} class="${css.instanceButton}"></button>`
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 class="${css.multiTitle}">${title}</div>
<i class='fa fa-angle-up ${css.methCaret}'></i>

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

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

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

@ -48,7 +48,7 @@
<input type="button" id="getcontentof">get content of</input> <br>
<input type="button" id="getcurrent">get current</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="removenetwork">remove network</input> <br>
<br>

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

Loading…
Cancel
Save