diff --git a/extensions/etherscan-general/index.js b/extensions/etherscan-general/index.js index f5a8ffd17c..cb189cdf8d 100644 --- a/extensions/etherscan-general/index.js +++ b/extensions/etherscan-general/index.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 diff --git a/package.json b/package.json index 5a2957f12d..f2e5a76d0a 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/app.js b/src/app.js index a0e6475f79..e4ef25e021 100644 --- a/src/app.js +++ b/src/app.js @@ -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`${txLink}`) }) - 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) => { diff --git a/src/app/components/plugin-manager-component.js b/src/app/components/plugin-manager-component.js index 5be1d9a220..2213eceb3b 100644 --- a/src/app/components/plugin-manager-component.js +++ b/src/app/components/plugin-manager-component.js @@ -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 } diff --git a/src/app/components/swap-panel-component.js b/src/app/components/swap-panel-component.js index 3431fe2cc6..c6e15aae18 100644 --- a/src/app/components/swap-panel-component.js +++ b/src/app/components/swap-panel-component.js @@ -73,7 +73,7 @@ module.exports = SwapPanelComponent const css = csjs` .plugins { - height : 100%; + height : 95%; } .plugItIn { display : none; diff --git a/src/app/components/vertical-icons-component.js b/src/app/components/vertical-icons-component.js index 5d417b8bac..e85322e09a 100644 --- a/src/app/components/vertical-icons-component.js +++ b/src/app/components/vertical-icons-component.js @@ -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`
${name}
` + addIcon ({kind, name, icon, displayName, tooltip}) { + let title = (displayName || name)// + (tooltip ? tooltip : "") + this.icons[name] = yo` +
+ ${name} +
` 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``) + el.appendChild(yo``) + + // 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; + } ` diff --git a/src/app/debugger/debuggerUI/vmDebugger/CodeListView.js b/src/app/debugger/debuggerUI/vmDebugger/CodeListView.js index 60562188e6..deb4545ca7 100644 --- a/src/app/debugger/debuggerUI/vmDebugger/CodeListView.js +++ b/src/app/debugger/debuggerUI/vmDebugger/CodeListView.js @@ -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') diff --git a/src/app/editor/SourceHighlighters.js b/src/app/editor/SourceHighlighters.js index 85c69e2537..e2ea39bb76 100644 --- a/src/app/editor/SourceHighlighters.js +++ b/src/app/editor/SourceHighlighters.js @@ -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 { diff --git a/src/app/editor/editor.js b/src/app/editor/editor.js index 748fd2b096..87ca2faa01 100644 --- a/src/app/editor/editor.js +++ b/src/app/editor/editor.js @@ -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); - } `) @@ -97,10 +78,14 @@ class Editor { // Editor Setup const el = yo`
` 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 diff --git a/src/app/editor/sourceHighlighter.js b/src/app/editor/sourceHighlighter.js index 9d026cda5d..1991e58f84 100644 --- a/src/app/editor/sourceHighlighter.js +++ b/src/app/editor/sourceHighlighter.js @@ -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; diff --git a/src/app/execution/txLogger.js b/src/app/execution/txLogger.js index 010fc46f7e..401c393caa 100644 --- a/src/app/execution/txLogger.js +++ b/src/app/execution/txLogger.js @@ -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 }) } }) diff --git a/src/app/files/browser-files-tree.js b/src/app/files/browser-files-tree.js index 3c8eecf991..34df82b5db 100644 --- a/src/app/files/browser-files-tree.js +++ b/src/app/files/browser-files-tree.js @@ -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 diff --git a/src/app/files/fileManager.js b/src/app/files/fileManager.js index 0ed8a3395d..b3b50868b3 100644 --- a/src/app/files/fileManager.js +++ b/src/app/files/fileManager.js @@ -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) diff --git a/src/app/files/remixd-handle.js b/src/app/files/remixd-handle.js index f0cf7b8162..6787e2104d 100644 --- a/src/app/files/remixd-handle.js +++ b/src/app/files/remixd-handle.js @@ -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) diff --git a/src/app/panels/editor-panel.js b/src/app/panels/editor-panel.js index 29640d36ba..95bd98443d 100644 --- a/src/app/panels/editor-panel.js +++ b/src/app/panels/editor-panel.js @@ -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 diff --git a/src/app/panels/file-panel.js b/src/app/panels/file-panel.js index 5a42d28a2f..9bb56d9def 100644 --- a/src/app/panels/file-panel.js +++ b/src/app/panels/file-panel.js @@ -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' - } - } } diff --git a/src/app/panels/styles/file-panel-styles.js b/src/app/panels/styles/file-panel-styles.js index 4ccec900d7..6effb8a4f1 100644 --- a/src/app/panels/styles/file-panel-styles.js +++ b/src/app/panels/styles/file-panel-styles.js @@ -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 { diff --git a/src/app/panels/styles/terminal-styles.js b/src/app/panels/styles/terminal-styles.js index 22a8a18bf4..8168747186 100644 --- a/src/app/panels/styles/terminal-styles.js +++ b/src/app/panels/styles/terminal-styles.js @@ -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; diff --git a/src/app/panels/tab-proxy.js b/src/app/panels/tab-proxy.js index dbe0183ac4..7464b631c4 100644 --- a/src/app/panels/tab-proxy.js +++ b/src/app/panels/tab-proxy.js @@ -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] } diff --git a/src/app/panels/terminal.js b/src/app/panels/terminal.js index b881d88ab8..f9fb886208 100644 --- a/src/app/panels/terminal.js +++ b/src/app/panels/terminal.js @@ -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`
` -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` ` + class="btn btn-secondary btn-sm align-items-center ${css.toggleTerminal} fa fa-angle-double-down">` self._view.dragbar = yo`
` self._view.dropdown = self._components.dropdown.render() @@ -136,7 +151,7 @@ class Terminal { self._view.bar = yo`
${self._view.dragbar} -
+
${self._view.icon}