From e092445a7e745026f644a2909b6dfdbf4595607f Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Tue, 14 Dec 2021 13:31:43 +0100 Subject: [PATCH] first steps to app ui --- apps/remix-ide/src/_app.js | 521 +++++++++++++++++ apps/remix-ide/src/app-component.js | 307 ++++++++++ apps/remix-ide/src/app.js | 525 +----------------- libs/remix-ui/app/.babelrc | 4 + libs/remix-ui/app/.eslintrc | 248 +++++++++ libs/remix-ui/app/README.md | 7 + libs/remix-ui/app/src/index.ts | 1 + .../app/src/lib/remix-app/remix-app.css | 66 +++ .../app/src/lib/remix-app/remix-app.tsx | 85 +++ libs/remix-ui/app/tsconfig.json | 19 + libs/remix-ui/app/tsconfig.lib.json | 13 + libs/remix-ui/app/tsconfig.spec.json | 15 + nx.json | 3 + package.json | 2 +- tsconfig.base.json | 3 +- workspace.json | 16 + 16 files changed, 1323 insertions(+), 512 deletions(-) create mode 100644 apps/remix-ide/src/_app.js create mode 100644 apps/remix-ide/src/app-component.js create mode 100644 libs/remix-ui/app/.babelrc create mode 100644 libs/remix-ui/app/.eslintrc create mode 100644 libs/remix-ui/app/README.md create mode 100644 libs/remix-ui/app/src/index.ts create mode 100644 libs/remix-ui/app/src/lib/remix-app/remix-app.css create mode 100644 libs/remix-ui/app/src/lib/remix-app/remix-app.tsx create mode 100644 libs/remix-ui/app/tsconfig.json create mode 100644 libs/remix-ui/app/tsconfig.lib.json create mode 100644 libs/remix-ui/app/tsconfig.spec.json diff --git a/apps/remix-ide/src/_app.js b/apps/remix-ide/src/_app.js new file mode 100644 index 0000000000..6c2f40fb25 --- /dev/null +++ b/apps/remix-ide/src/_app.js @@ -0,0 +1,521 @@ +'use strict' +import { basicLogo } from './app/ui/svgLogo' + +import { RunTab, makeUdapp } from './app/udapp' + +import PanelsResize from './lib/panels-resize' +import { RemixEngine } from './remixEngine' +import { RemixAppManager } from './remixAppManager' +import { FramingService } from './framingService' +import { WalkthroughService } from './walkthroughService' +import { MainView } from './app/panels/main-view' +import { ThemeModule } from './app/tabs/theme-module' +import { NetworkModule } from './app/tabs/network-module' +import { Web3ProviderModule } from './app/tabs/web3-provider' +import { SidePanel } from './app/components/side-panel' +import { HiddenPanel } from './app/components/hidden-panel' +import { VerticalIcons } from './app/components/vertical-icons' +import { LandingPage } from './app/ui/landing-page/landing-page' +import { MainPanel } from './app/components/main-panel' + +import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, FetchAndCompile, CompilerImports } from '@remix-project/core-plugin' + +import migrateFileSystem from './migrateFileSystem' + +const isElectron = require('is-electron') +const csjs = require('csjs-inject') +const yo = require('yo-yo') +const remixLib = require('@remix-project/remix-lib') +const registry = require('./global/registry') + +const QueryParams = require('./lib/query-params') +const Storage = remixLib.Storage +const RemixDProvider = require('./app/files/remixDProvider') +const HardhatProvider = require('./app/tabs/hardhat-provider') +const Config = require('./config') +const modalDialogCustom = require('./app/ui/modal-dialog-custom') +const modalDialog = require('./app/ui/modaldialog') +const FileManager = require('./app/files/fileManager') +const FileProvider = require('./app/files/fileProvider') +const DGitProvider = require('./app/files/dgitProvider') +const WorkspaceFileProvider = require('./app/files/workspaceFileProvider') +const toolTip = require('./app/ui/tooltip') + +const Blockchain = require('./blockchain/blockchain.js') + +const PluginManagerComponent = require('./app/components/plugin-manager-component') + +const CompileTab = require('./app/tabs/compile-tab') +const SettingsTab = require('./app/tabs/settings-tab') +const AnalysisTab = require('./app/tabs/analysis-tab') +const { DebuggerTab } = require('./app/tabs/debugger-tab') +const TestTab = require('./app/tabs/test-tab') +const FilePanel = require('./app/panels/file-panel') +const Editor = require('./app/editor/editor') +const Terminal = require('./app/panels/terminal') +const ContextualListener = require('./app/editor/contextualListener') +const _paq = window._paq = window._paq || [] + +const css = csjs` + html { box-sizing: border-box; } + *, *:before, *:after { box-sizing: inherit; } + body { + /* font: 14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; */ + font-size : .8rem; + } + pre { + overflow-x: auto; + } + .remixIDE { + width : 100vw; + height : 100vh; + overflow : hidden; + flex-direction : row; + display : flex; + } + .mainpanel { + display : flex; + flex-direction : column; + overflow : hidden; + flex : 1; + } + .iconpanel { + display : flex; + flex-direction : column; + overflow : hidden; + width : 50px; + user-select : none; + } + .sidepanel { + display : flex; + flex-direction : row-reverse; + width : 320px; + } + .highlightcode { + position : absolute; + z-index : 20; + background-color : var(--info); + } + .highlightcode_fullLine { + position : absolute; + z-index : 20; + background-color : var(--info); + opacity : 0.5; + } + .centered { + position : fixed; + top : 20%; + left : 45%; + width : 200px; + height : 200px; + } + .centered svg path { + fill: var(--secondary); + } + .centered svg polygon { + fill : var(--secondary); + } + .onboarding { + color : var(--text-info); + background-color : var(--info); + } + .matomoBtn { + width : 100px; + } +` + +class App { + constructor (api = {}, events = {}, opts = {}) { + var self = this + self.appManager = new RemixAppManager({}) + self._components = {} + self._view = {} + self._view.splashScreen = yo` +
+ ${basicLogo()} +
+ REMIX IDE +
+
+ ` + document.body.appendChild(self._view.splashScreen) + + // setup storage + const configStorage = new Storage('config-v0.8:') + + // load app config + const config = new Config(configStorage) + registry.put({ api: config, name: 'config' }) + + // load file system + self._components.filesProviders = {} + self._components.filesProviders.browser = new FileProvider('browser') + registry.put({ api: self._components.filesProviders.browser, name: 'fileproviders/browser' }) + self._components.filesProviders.localhost = new RemixDProvider(self.appManager) + registry.put({ api: self._components.filesProviders.localhost, name: 'fileproviders/localhost' }) + self._components.filesProviders.workspace = new WorkspaceFileProvider() + registry.put({ api: self._components.filesProviders.workspace, name: 'fileproviders/workspace' }) + + registry.put({ api: self._components.filesProviders, name: 'fileproviders' }) + + migrateFileSystem(self._components.filesProviders.browser) + } + + init () { + this.run().catch(console.error) + } + + render () { + var self = this + if (self._view.el) return self._view.el + // not resizable + self._view.iconpanel = yo` +
+ ${''} +
+ ` + + // center panel, resizable + self._view.sidepanel = yo` +
+ ${''} +
+ ` + + // handle the editor + terminal + self._view.mainpanel = yo` +
+ ${''} +
+ ` + + self._components.resizeFeature = new PanelsResize(self._view.sidepanel) + + self._view.el = yo` + + ` + return self._view.el + } + + async run () { + var self = this + + // check the origin and warn message + if (window.location.hostname === 'yann300.github.io') { + modalDialogCustom.alert('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.') + } else if (window.location.hostname === 'remix-alpha.ethereum.org' || + (window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)) { + modalDialogCustom.alert('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.') + } else if (window.location.protocol.indexOf('http') === 0 && + window.location.hostname !== 'remix.ethereum.org' && + window.location.hostname !== 'localhost' && + window.location.hostname !== '127.0.0.1') { + modalDialogCustom.alert(`The Remix IDE has moved to http://remix.ethereum.org.\n + This instance of Remix you are visiting WILL NOT BE UPDATED.\n + Please make a backup of your contracts and start using http://remix.ethereum.org`) + } + if (window.location.protocol.indexOf('https') === 0) { + toolTip('You are using an `https` connection. Please switch to `http` if you are using Remix against an `http Web3 provider` or allow Mixed Content in your browser.') + } + + const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080'] + // workaround for Electron support + if (!isElectron() && !hosts.includes(window.location.host)) { + // Oops! Accidentally trigger refresh or bookmark. + window.onbeforeunload = function () { + return 'Are you sure you want to leave?' + } + } + + // APP_MANAGER + const appManager = self.appManager + const pluginLoader = appManager.pluginLoader + const workspace = pluginLoader.get() + const engine = new RemixEngine() + engine.register(appManager) + + // SERVICES + // ----------------- theme service --------------------------------- + const themeModule = new ThemeModule(registry) + registry.put({ api: themeModule, name: 'themeModule' }) + themeModule.initTheme(() => { + setTimeout(() => { + document.body.removeChild(self._view.splashScreen) + self._view.el.style.visibility = 'visible' + }, 1500) + }) + // ----------------- editor service ---------------------------- + const editor = new Editor() // wrapper around ace editor + registry.put({ api: editor, name: 'editor' }) + editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile()) + + // ----------------- fileManager service ---------------------------- + const fileManager = new FileManager(editor, appManager) + registry.put({ api: fileManager, name: 'filemanager' }) + // ----------------- dGit provider --------------------------------- + const dGitProvider = new DGitProvider() + + // ----------------- import content service ------------------------ + const contentImport = new CompilerImports() + + const blockchain = new Blockchain(registry.get('config').api) + + // ----------------- compilation metadata generation service --------- + const compilerMetadataGenerator = new CompilerMetadata() + // ----------------- compilation result service (can keep track of compilation results) ---------------------------- + const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name) + registry.put({ api: compilersArtefacts, name: 'compilersartefacts' }) + + // service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it + const fetchAndCompile = new FetchAndCompile() + // ----------------- network service (resolve network id / name) ----- + const networkModule = new NetworkModule(blockchain) + // ----------------- represent the current selected web3 provider ---- + const web3Provider = new Web3ProviderModule(blockchain) + const hardhatProvider = new HardhatProvider(blockchain) + // ----------------- convert offset to line/column service ----------- + const offsetToLineColumnConverter = new OffsetToLineColumnConverter() + registry.put({ api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter' }) + + // -------------------Terminal---------------------------------------- + makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl)) + const terminal = new Terminal( + { appManager, blockchain }, + { + getPosition: (event) => { + var limitUp = 36 + var limitDown = 20 + var height = window.innerHeight + var newpos = (event.pageY < limitUp) ? limitUp : event.pageY + newpos = (newpos < height - limitDown) ? newpos : height - limitDown + return height - newpos + } + } + ) + const contextualListener = new ContextualListener({ editor }) + + engine.register([ + blockchain, + contentImport, + themeModule, + editor, + fileManager, + compilerMetadataGenerator, + compilersArtefacts, + networkModule, + offsetToLineColumnConverter, + contextualListener, + terminal, + web3Provider, + fetchAndCompile, + dGitProvider, + hardhatProvider + ]) + + // LAYOUT & SYSTEM VIEWS + const appPanel = new MainPanel() + const mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal) + registry.put({ api: mainview, name: 'mainview' }) + + engine.register([ + appPanel, + mainview.tabProxy + ]) + + // those views depend on app_manager + const menuicons = new VerticalIcons(appManager) + const sidePanel = new SidePanel(appManager, menuicons) + const hiddenPanel = new HiddenPanel() + const pluginManagerComponent = new PluginManagerComponent(appManager, engine) + const filePanel = new FilePanel(appManager) + const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel, contentImport) + const settings = new SettingsTab( + registry.get('config').api, + editor, + appManager + ) + + // adding Views to the DOM + self._view.mainpanel.appendChild(mainview.render()) + self._view.iconpanel.appendChild(menuicons.render()) + self._view.sidepanel.appendChild(sidePanel.render()) + document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body + + engine.register([ + menuicons, + landingPage, + hiddenPanel, + sidePanel, + filePanel, + pluginManagerComponent, + settings + ]) + + const queryParams = new QueryParams() + const params = queryParams.get() + + const onAcceptMatomo = () => { + _paq.push(['forgetUserOptOut']) + // @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used + document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' + settings.updateMatomoAnalyticsChoice(true) + const el = document.getElementById('modal-dialog') + el.parentElement.removeChild(el) + startWalkthroughService() + } + const onDeclineMatomo = () => { + settings.updateMatomoAnalyticsChoice(false) + _paq.push(['optUserOut']) + const el = document.getElementById('modal-dialog') + el.parentElement.removeChild(el) + startWalkthroughService() + } + + const startWalkthroughService = () => { + const walkthroughService = new WalkthroughService(localStorage) + if (!params.code && !params.url && !params.minimizeterminal && !params.gist && !params.minimizesidepanel) { + walkthroughService.start() + } + } + + // Ask to opt in to Matomo for remix, remix-alpha and remix-beta + const matomoDomains = { + 'remix-alpha.ethereum.org': 27, + 'remix-beta.ethereum.org': 25, + 'remix.ethereum.org': 23 + } + if (matomoDomains[window.location.hostname] && !registry.get('config').api.exists('settings/matomo-analytics')) { + modalDialog( + 'Help us to improve Remix IDE', + yo` +
+

An Opt-in version of Matomo, an open source data analytics platform is being used to improve Remix IDE.

+

We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.

+

All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: take a look.

+

We do not collect nor store any personally identifiable information (PII).

+

For more info, see: Matomo Analyitcs on Remix iDE.

+

You can change your choice in the Settings panel anytime.

+
+ + +
+
`, + { + label: '', + fn: null + }, + { + label: '', + fn: null + } + ) + } else { + startWalkthroughService() + } + + // CONTENT VIEWS & DEFAULT PLUGINS + const compileTab = new CompileTab(registry.get('config').api, registry.get('filemanager').api) + const run = new RunTab( + blockchain, + registry.get('config').api, + registry.get('filemanager').api, + registry.get('editor').api, + filePanel, + registry.get('compilersartefacts').api, + networkModule, + mainview, + registry.get('fileproviders/browser').api + ) + const analysis = new AnalysisTab(registry) + const debug = new DebuggerTab() + const test = new TestTab( + registry.get('filemanager').api, + registry.get('offsettolinecolumnconverter').api, + filePanel, + compileTab, + appManager, + contentImport + ) + + engine.register([ + compileTab, + run, + debug, + analysis, + test, + filePanel.remixdHandle, + filePanel.gitHandle, + filePanel.hardhatHandle, + filePanel.slitherHandle + ]) + + if (isElectron()) { + appManager.activatePlugin('remixd') + } + + try { + engine.register(await appManager.registeredPlugins()) + } catch (e) { + console.log('couldn\'t register iframe plugins', e.message) + } + + await appManager.activatePlugin(['editor']) + await appManager.activatePlugin(['theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) + await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) + await appManager.activatePlugin(['sidePanel']) // activating host plugin separately + await appManager.activatePlugin(['home']) + await appManager.activatePlugin(['settings']) + await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport']) + + appManager.on('filePanel', 'workspaceInitializationCompleted', async () => { + await appManager.registerContextMenuItems() + }) + await appManager.activatePlugin(['filePanel']) + // Set workspace after initial activation + appManager.on('editor', 'editorMounted', () => { + if (Array.isArray(workspace)) { + appManager.activatePlugin(workspace).then(async () => { + try { + if (params.deactivate) { + await appManager.deactivatePlugin(params.deactivate.split(',')) + } + } catch (e) { + console.log(e) + } + + if (params.code) { + // if code is given in url we focus on solidity plugin + menuicons.select('solidity') + } else { + // If plugins are loaded from the URL params, we focus on the last one. + if (pluginLoader.current === 'queryParams' && workspace.length > 0) menuicons.select(workspace[workspace.length - 1]) + } + + if (params.call) { + const callDetails = params.call.split('//') + if (callDetails.length > 1) { + toolTip(`initiating ${callDetails[0]} ...`) + // @todo(remove the timeout when activatePlugin is on 0.3.0) + appManager.call(...callDetails).catch(console.error) + } + } + }).catch(console.error) + } + }) + // activate solidity plugin + appManager.activatePlugin(['solidity', 'udapp']) + + // Load and start the service who manager layout and frame + const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature) + + if (params.embed) framingService.embed() + framingService.start(params) + } +} + +module.exports = App diff --git a/apps/remix-ide/src/app-component.js b/apps/remix-ide/src/app-component.js new file mode 100644 index 0000000000..f8136cf7c9 --- /dev/null +++ b/apps/remix-ide/src/app-component.js @@ -0,0 +1,307 @@ +'use strict' +import { RunTab, makeUdapp } from './app/udapp' +import { RemixEngine } from './remixEngine' +import { RemixAppManager } from './remixAppManager' +import { MainView } from './app/panels/main-view' +import { ThemeModule } from './app/tabs/theme-module' +import { NetworkModule } from './app/tabs/network-module' +import { Web3ProviderModule } from './app/tabs/web3-provider' +import { SidePanel } from './app/components/side-panel' +import { HiddenPanel } from './app/components/hidden-panel' +import { VerticalIcons } from './app/components/vertical-icons' +import { LandingPage } from './app/ui/landing-page/landing-page' +import { MainPanel } from './app/components/main-panel' + +import { WalkthroughService } from './walkthroughService' + +import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, FetchAndCompile, CompilerImports } from '@remix-project/core-plugin' + +import migrateFileSystem from './migrateFileSystem' + +const isElectron = require('is-electron') + +const remixLib = require('@remix-project/remix-lib') +const registry = require('./global/registry') + +const QueryParams = require('./lib/query-params') +const Storage = remixLib.Storage +const RemixDProvider = require('./app/files/remixDProvider') +const HardhatProvider = require('./app/tabs/hardhat-provider') +const Config = require('./config') + +const FileManager = require('./app/files/fileManager') +const FileProvider = require('./app/files/fileProvider') +const DGitProvider = require('./app/files/dgitProvider') +const WorkspaceFileProvider = require('./app/files/workspaceFileProvider') +const toolTip = require('./app/ui/tooltip') + +const Blockchain = require('./blockchain/blockchain.js') + +const PluginManagerComponent = require('./app/components/plugin-manager-component') + +const CompileTab = require('./app/tabs/compile-tab') +const SettingsTab = require('./app/tabs/settings-tab') +const AnalysisTab = require('./app/tabs/analysis-tab') +const { DebuggerTab } = require('./app/tabs/debugger-tab') +const TestTab = require('./app/tabs/test-tab') +const FilePanel = require('./app/panels/file-panel') +const Editor = require('./app/editor/editor') +const Terminal = require('./app/panels/terminal') +const ContextualListener = require('./app/editor/contextualListener') + +class AppComponent { + constructor (api = {}, events = {}, opts = {}) { + const self = this + self.appManager = new RemixAppManager({}) + self._components = {} + // setup storage + const configStorage = new Storage('config-v0.8:') + + // load app config + const config = new Config(configStorage) + registry.put({ api: config, name: 'config' }) + + // load file system + self._components.filesProviders = {} + self._components.filesProviders.browser = new FileProvider('browser') + registry.put({ api: self._components.filesProviders.browser, name: 'fileproviders/browser' }) + self._components.filesProviders.localhost = new RemixDProvider(self.appManager) + registry.put({ api: self._components.filesProviders.localhost, name: 'fileproviders/localhost' }) + self._components.filesProviders.workspace = new WorkspaceFileProvider() + registry.put({ api: self._components.filesProviders.workspace, name: 'fileproviders/workspace' }) + + registry.put({ api: self._components.filesProviders, name: 'fileproviders' }) + + migrateFileSystem(self._components.filesProviders.browser) + } + + async run () { + const self = this + // APP_MANAGER + const appManager = self.appManager + const pluginLoader = self.appManager.pluginLoader + self.workspace = pluginLoader.get() + self.engine = new RemixEngine() + self.engine.register(appManager) + + // SERVICES + // ----------------- theme service --------------------------------- + const themeModule = new ThemeModule(registry) + registry.put({ api: themeModule, name: 'themeModule' }) + themeModule.initTheme(() => { + setTimeout(() => { + // document.body.removeChild(self._view.splashScreen) + // self._view.el.style.visibility = 'visible' + }, 1500) + }) + // ----------------- editor service ---------------------------- + const editor = new Editor() // wrapper around ace editor + registry.put({ api: editor, name: 'editor' }) + editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile()) + + // ----------------- fileManager service ---------------------------- + const fileManager = new FileManager(editor, appManager) + registry.put({ api: fileManager, name: 'filemanager' }) + // ----------------- dGit provider --------------------------------- + const dGitProvider = new DGitProvider() + + // ----------------- import content service ------------------------ + const contentImport = new CompilerImports() + + const blockchain = new Blockchain(registry.get('config').api) + + // ----------------- compilation metadata generation service --------- + const compilerMetadataGenerator = new CompilerMetadata() + // ----------------- compilation result service (can keep track of compilation results) ---------------------------- + const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name) + registry.put({ api: compilersArtefacts, name: 'compilersartefacts' }) + + // service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it + const fetchAndCompile = new FetchAndCompile() + // ----------------- network service (resolve network id / name) ----- + const networkModule = new NetworkModule(blockchain) + // ----------------- represent the current selected web3 provider ---- + const web3Provider = new Web3ProviderModule(blockchain) + const hardhatProvider = new HardhatProvider(blockchain) + // ----------------- convert offset to line/column service ----------- + const offsetToLineColumnConverter = new OffsetToLineColumnConverter() + registry.put({ api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter' }) + + // -------------------Terminal---------------------------------------- + makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl)) + const terminal = new Terminal( + { appManager, blockchain }, + { + getPosition: (event) => { + var limitUp = 36 + var limitDown = 20 + var height = window.innerHeight + var newpos = (event.pageY < limitUp) ? limitUp : event.pageY + newpos = (newpos < height - limitDown) ? newpos : height - limitDown + return height - newpos + } + } + ) + const contextualListener = new ContextualListener({ editor }) + + self.engine.register([ + blockchain, + contentImport, + themeModule, + editor, + fileManager, + compilerMetadataGenerator, + compilersArtefacts, + networkModule, + offsetToLineColumnConverter, + contextualListener, + terminal, + web3Provider, + fetchAndCompile, + dGitProvider, + hardhatProvider + ]) + + // LAYOUT & SYSTEM VIEWS + const appPanel = new MainPanel() + self.mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal) + registry.put({ api: self.mainview, name: 'mainview' }) + + self.engine.register([ + appPanel, + self.mainview.tabProxy + ]) + + // those views depend on app_manager + self.menuicons = new VerticalIcons(appManager) + self.sidePanel = new SidePanel(appManager, self.menuicons) + self.hiddenPanel = new HiddenPanel() + const pluginManagerComponent = new PluginManagerComponent(appManager, self.engine) + const filePanel = new FilePanel(appManager) + const landingPage = new LandingPage(appManager, self.menuicons, fileManager, filePanel, contentImport) + const settings = new SettingsTab( + registry.get('config').api, + editor, + appManager + ) + + self.engine.register([ + self.menuicons, + landingPage, + self.hiddenPanel, + self.sidePanel, + filePanel, + pluginManagerComponent, + settings + ]) + + // CONTENT VIEWS & DEFAULT PLUGINS + const compileTab = new CompileTab(registry.get('config').api, registry.get('filemanager').api) + const run = new RunTab( + blockchain, + registry.get('config').api, + registry.get('filemanager').api, + registry.get('editor').api, + filePanel, + registry.get('compilersartefacts').api, + networkModule, + self.mainview, + registry.get('fileproviders/browser').api + ) + const analysis = new AnalysisTab(registry) + const debug = new DebuggerTab() + const test = new TestTab( + registry.get('filemanager').api, + registry.get('offsettolinecolumnconverter').api, + filePanel, + compileTab, + appManager, + contentImport + ) + + self.engine.register([ + compileTab, + run, + debug, + analysis, + test, + filePanel.remixdHandle, + filePanel.gitHandle, + filePanel.hardhatHandle, + filePanel.slitherHandle + ]) + } + + async activate () { + const queryParams = new QueryParams() + const params = queryParams.get() + const self = this + + if (isElectron()) { + self.appManager.activatePlugin('remixd') + } + + try { + self.engine.register(await self.appManager.registeredPlugins()) + } catch (e) { + console.log('couldn\'t register iframe plugins', e.message) + } + + await self.appManager.activatePlugin(['editor']) + await self.appManager.activatePlugin(['theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) + await self.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) + await self.appManager.activatePlugin(['sidePanel']) // activating host plugin separately + await self.appManager.activatePlugin(['home']) + await self.appManager.activatePlugin(['settings']) + await self.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport']) + + self.appManager.on('filePanel', 'workspaceInitializationCompleted', async () => { + await self.appManager.registerContextMenuItems() + }) + + const startWalkthroughService = () => { + const walkthroughService = new WalkthroughService(localStorage) + if (!params.code && !params.url && !params.minimizeterminal && !params.gist && !params.minimizesidepanel) { + walkthroughService.start() + } + } + + startWalkthroughService() + + await self.appManager.activatePlugin(['filePanel']) + // Set workspace after initial activation + self.appManager.on('editor', 'editorMounted', () => { + if (Array.isArray(self.workspace)) { + self.appManager.activatePlugin(self.workspace).then(async () => { + try { + if (params.deactivate) { + await self.appManager.deactivatePlugin(params.deactivate.split(',')) + } + } catch (e) { + console.log(e) + } + if (params.code) { + // if code is given in url we focus on solidity plugin + self.menuicons.select('solidity') + } else { + // If plugins are loaded from the URL params, we focus on the last one. + if (self.appManager.pluginLoader.current === 'queryParams' && self.workspace.length > 0) self.menuicons.select(self.workspace[self.workspace.length - 1]) + } + + if (params.call) { + const callDetails = params.call.split('//') + if (callDetails.length > 1) { + toolTip(`initiating ${callDetails[0]} ...`) + // @todo(remove the timeout when activatePlugin is on 0.3.0) + self.appManager.call(...callDetails).catch(console.error) + } + } + }).catch(console.error) + } + }) + // activate solidity plugin + self.appManager.activatePlugin(['solidity', 'udapp']) + } +} + +module.exports = AppComponent diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 6c2f40fb25..86001ec3e4 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -1,520 +1,25 @@ 'use strict' -import { basicLogo } from './app/ui/svgLogo' - -import { RunTab, makeUdapp } from './app/udapp' - -import PanelsResize from './lib/panels-resize' -import { RemixEngine } from './remixEngine' -import { RemixAppManager } from './remixAppManager' -import { FramingService } from './framingService' -import { WalkthroughService } from './walkthroughService' -import { MainView } from './app/panels/main-view' -import { ThemeModule } from './app/tabs/theme-module' -import { NetworkModule } from './app/tabs/network-module' -import { Web3ProviderModule } from './app/tabs/web3-provider' -import { SidePanel } from './app/components/side-panel' -import { HiddenPanel } from './app/components/hidden-panel' -import { VerticalIcons } from './app/components/vertical-icons' -import { LandingPage } from './app/ui/landing-page/landing-page' -import { MainPanel } from './app/components/main-panel' - -import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, FetchAndCompile, CompilerImports } from '@remix-project/core-plugin' - -import migrateFileSystem from './migrateFileSystem' - -const isElectron = require('is-electron') -const csjs = require('csjs-inject') -const yo = require('yo-yo') -const remixLib = require('@remix-project/remix-lib') -const registry = require('./global/registry') - -const QueryParams = require('./lib/query-params') -const Storage = remixLib.Storage -const RemixDProvider = require('./app/files/remixDProvider') -const HardhatProvider = require('./app/tabs/hardhat-provider') -const Config = require('./config') -const modalDialogCustom = require('./app/ui/modal-dialog-custom') -const modalDialog = require('./app/ui/modaldialog') -const FileManager = require('./app/files/fileManager') -const FileProvider = require('./app/files/fileProvider') -const DGitProvider = require('./app/files/dgitProvider') -const WorkspaceFileProvider = require('./app/files/workspaceFileProvider') -const toolTip = require('./app/ui/tooltip') - -const Blockchain = require('./blockchain/blockchain.js') - -const PluginManagerComponent = require('./app/components/plugin-manager-component') - -const CompileTab = require('./app/tabs/compile-tab') -const SettingsTab = require('./app/tabs/settings-tab') -const AnalysisTab = require('./app/tabs/analysis-tab') -const { DebuggerTab } = require('./app/tabs/debugger-tab') -const TestTab = require('./app/tabs/test-tab') -const FilePanel = require('./app/panels/file-panel') -const Editor = require('./app/editor/editor') -const Terminal = require('./app/panels/terminal') -const ContextualListener = require('./app/editor/contextualListener') -const _paq = window._paq = window._paq || [] - -const css = csjs` - html { box-sizing: border-box; } - *, *:before, *:after { box-sizing: inherit; } - body { - /* font: 14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; */ - font-size : .8rem; - } - pre { - overflow-x: auto; - } - .remixIDE { - width : 100vw; - height : 100vh; - overflow : hidden; - flex-direction : row; - display : flex; - } - .mainpanel { - display : flex; - flex-direction : column; - overflow : hidden; - flex : 1; - } - .iconpanel { - display : flex; - flex-direction : column; - overflow : hidden; - width : 50px; - user-select : none; - } - .sidepanel { - display : flex; - flex-direction : row-reverse; - width : 320px; - } - .highlightcode { - position : absolute; - z-index : 20; - background-color : var(--info); - } - .highlightcode_fullLine { - position : absolute; - z-index : 20; - background-color : var(--info); - opacity : 0.5; - } - .centered { - position : fixed; - top : 20%; - left : 45%; - width : 200px; - height : 200px; - } - .centered svg path { - fill: var(--secondary); - } - .centered svg polygon { - fill : var(--secondary); - } - .onboarding { - color : var(--text-info); - background-color : var(--info); - } - .matomoBtn { - width : 100px; - } -` +import React from 'react' // eslint-disable-line +import ReactDOM from 'react-dom' +import { RemixApp } from '@remix-ui/app' +import AppComponent from './app-component' +const appComponent = new AppComponent() +appComponent.run() class App { - constructor (api = {}, events = {}, opts = {}) { - var self = this - self.appManager = new RemixAppManager({}) - self._components = {} - self._view = {} - self._view.splashScreen = yo` -
- ${basicLogo()} -
- REMIX IDE -
-
- ` - document.body.appendChild(self._view.splashScreen) - - // setup storage - const configStorage = new Storage('config-v0.8:') - - // load app config - const config = new Config(configStorage) - registry.put({ api: config, name: 'config' }) - - // load file system - self._components.filesProviders = {} - self._components.filesProviders.browser = new FileProvider('browser') - registry.put({ api: self._components.filesProviders.browser, name: 'fileproviders/browser' }) - self._components.filesProviders.localhost = new RemixDProvider(self.appManager) - registry.put({ api: self._components.filesProviders.localhost, name: 'fileproviders/localhost' }) - self._components.filesProviders.workspace = new WorkspaceFileProvider() - registry.put({ api: self._components.filesProviders.workspace, name: 'fileproviders/workspace' }) - - registry.put({ api: self._components.filesProviders, name: 'fileproviders' }) - - migrateFileSystem(self._components.filesProviders.browser) - } - - init () { - this.run().catch(console.error) - } - render () { - var self = this - if (self._view.el) return self._view.el - // not resizable - self._view.iconpanel = yo` -
- ${''} -
- ` - - // center panel, resizable - self._view.sidepanel = yo` -
- ${''} -
- ` - - // handle the editor + terminal - self._view.mainpanel = yo` -
- ${''} -
- ` - - self._components.resizeFeature = new PanelsResize(self._view.sidepanel) - - self._view.el = yo` - - ` - return self._view.el + if (this.el) return this.el + this.el = document.createElement('div') + return this.el } - async run () { - var self = this - - // check the origin and warn message - if (window.location.hostname === 'yann300.github.io') { - modalDialogCustom.alert('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.') - } else if (window.location.hostname === 'remix-alpha.ethereum.org' || - (window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)) { - modalDialogCustom.alert('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.') - } else if (window.location.protocol.indexOf('http') === 0 && - window.location.hostname !== 'remix.ethereum.org' && - window.location.hostname !== 'localhost' && - window.location.hostname !== '127.0.0.1') { - modalDialogCustom.alert(`The Remix IDE has moved to http://remix.ethereum.org.\n - This instance of Remix you are visiting WILL NOT BE UPDATED.\n - Please make a backup of your contracts and start using http://remix.ethereum.org`) - } - if (window.location.protocol.indexOf('https') === 0) { - toolTip('You are using an `https` connection. Please switch to `http` if you are using Remix against an `http Web3 provider` or allow Mixed Content in your browser.') - } - - const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080'] - // workaround for Electron support - if (!isElectron() && !hosts.includes(window.location.host)) { - // Oops! Accidentally trigger refresh or bookmark. - window.onbeforeunload = function () { - return 'Are you sure you want to leave?' - } - } - - // APP_MANAGER - const appManager = self.appManager - const pluginLoader = appManager.pluginLoader - const workspace = pluginLoader.get() - const engine = new RemixEngine() - engine.register(appManager) - - // SERVICES - // ----------------- theme service --------------------------------- - const themeModule = new ThemeModule(registry) - registry.put({ api: themeModule, name: 'themeModule' }) - themeModule.initTheme(() => { - setTimeout(() => { - document.body.removeChild(self._view.splashScreen) - self._view.el.style.visibility = 'visible' - }, 1500) - }) - // ----------------- editor service ---------------------------- - const editor = new Editor() // wrapper around ace editor - registry.put({ api: editor, name: 'editor' }) - editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile()) - - // ----------------- fileManager service ---------------------------- - const fileManager = new FileManager(editor, appManager) - registry.put({ api: fileManager, name: 'filemanager' }) - // ----------------- dGit provider --------------------------------- - const dGitProvider = new DGitProvider() - - // ----------------- import content service ------------------------ - const contentImport = new CompilerImports() - - const blockchain = new Blockchain(registry.get('config').api) - - // ----------------- compilation metadata generation service --------- - const compilerMetadataGenerator = new CompilerMetadata() - // ----------------- compilation result service (can keep track of compilation results) ---------------------------- - const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name) - registry.put({ api: compilersArtefacts, name: 'compilersartefacts' }) - - // service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it - const fetchAndCompile = new FetchAndCompile() - // ----------------- network service (resolve network id / name) ----- - const networkModule = new NetworkModule(blockchain) - // ----------------- represent the current selected web3 provider ---- - const web3Provider = new Web3ProviderModule(blockchain) - const hardhatProvider = new HardhatProvider(blockchain) - // ----------------- convert offset to line/column service ----------- - const offsetToLineColumnConverter = new OffsetToLineColumnConverter() - registry.put({ api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter' }) - - // -------------------Terminal---------------------------------------- - makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl)) - const terminal = new Terminal( - { appManager, blockchain }, - { - getPosition: (event) => { - var limitUp = 36 - var limitDown = 20 - var height = window.innerHeight - var newpos = (event.pageY < limitUp) ? limitUp : event.pageY - newpos = (newpos < height - limitDown) ? newpos : height - limitDown - return height - newpos - } - } - ) - const contextualListener = new ContextualListener({ editor }) - - engine.register([ - blockchain, - contentImport, - themeModule, - editor, - fileManager, - compilerMetadataGenerator, - compilersArtefacts, - networkModule, - offsetToLineColumnConverter, - contextualListener, - terminal, - web3Provider, - fetchAndCompile, - dGitProvider, - hardhatProvider - ]) - - // LAYOUT & SYSTEM VIEWS - const appPanel = new MainPanel() - const mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal) - registry.put({ api: mainview, name: 'mainview' }) - - engine.register([ - appPanel, - mainview.tabProxy - ]) - - // those views depend on app_manager - const menuicons = new VerticalIcons(appManager) - const sidePanel = new SidePanel(appManager, menuicons) - const hiddenPanel = new HiddenPanel() - const pluginManagerComponent = new PluginManagerComponent(appManager, engine) - const filePanel = new FilePanel(appManager) - const landingPage = new LandingPage(appManager, menuicons, fileManager, filePanel, contentImport) - const settings = new SettingsTab( - registry.get('config').api, - editor, - appManager - ) - - // adding Views to the DOM - self._view.mainpanel.appendChild(mainview.render()) - self._view.iconpanel.appendChild(menuicons.render()) - self._view.sidepanel.appendChild(sidePanel.render()) - document.body.appendChild(hiddenPanel.render()) // Hidden Panel is display none, it can be directly on body - - engine.register([ - menuicons, - landingPage, - hiddenPanel, - sidePanel, - filePanel, - pluginManagerComponent, - settings - ]) - - const queryParams = new QueryParams() - const params = queryParams.get() - - const onAcceptMatomo = () => { - _paq.push(['forgetUserOptOut']) - // @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used - document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' - settings.updateMatomoAnalyticsChoice(true) - const el = document.getElementById('modal-dialog') - el.parentElement.removeChild(el) - startWalkthroughService() - } - const onDeclineMatomo = () => { - settings.updateMatomoAnalyticsChoice(false) - _paq.push(['optUserOut']) - const el = document.getElementById('modal-dialog') - el.parentElement.removeChild(el) - startWalkthroughService() - } - - const startWalkthroughService = () => { - const walkthroughService = new WalkthroughService(localStorage) - if (!params.code && !params.url && !params.minimizeterminal && !params.gist && !params.minimizesidepanel) { - walkthroughService.start() - } - } - - // Ask to opt in to Matomo for remix, remix-alpha and remix-beta - const matomoDomains = { - 'remix-alpha.ethereum.org': 27, - 'remix-beta.ethereum.org': 25, - 'remix.ethereum.org': 23 - } - if (matomoDomains[window.location.hostname] && !registry.get('config').api.exists('settings/matomo-analytics')) { - modalDialog( - 'Help us to improve Remix IDE', - yo` -
-

An Opt-in version of Matomo, an open source data analytics platform is being used to improve Remix IDE.

-

We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.

-

All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: take a look.

-

We do not collect nor store any personally identifiable information (PII).

-

For more info, see: Matomo Analyitcs on Remix iDE.

-

You can change your choice in the Settings panel anytime.

-
- - -
-
`, - { - label: '', - fn: null - }, - { - label: '', - fn: null - } - ) - } else { - startWalkthroughService() - } - - // CONTENT VIEWS & DEFAULT PLUGINS - const compileTab = new CompileTab(registry.get('config').api, registry.get('filemanager').api) - const run = new RunTab( - blockchain, - registry.get('config').api, - registry.get('filemanager').api, - registry.get('editor').api, - filePanel, - registry.get('compilersartefacts').api, - networkModule, - mainview, - registry.get('fileproviders/browser').api - ) - const analysis = new AnalysisTab(registry) - const debug = new DebuggerTab() - const test = new TestTab( - registry.get('filemanager').api, - registry.get('offsettolinecolumnconverter').api, - filePanel, - compileTab, - appManager, - contentImport - ) - - engine.register([ - compileTab, - run, - debug, - analysis, - test, - filePanel.remixdHandle, - filePanel.gitHandle, - filePanel.hardhatHandle, - filePanel.slitherHandle - ]) - - if (isElectron()) { - appManager.activatePlugin('remixd') - } - - try { - engine.register(await appManager.registeredPlugins()) - } catch (e) { - console.log('couldn\'t register iframe plugins', e.message) - } - - await appManager.activatePlugin(['editor']) - await appManager.activatePlugin(['theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) - await appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) - await appManager.activatePlugin(['sidePanel']) // activating host plugin separately - await appManager.activatePlugin(['home']) - await appManager.activatePlugin(['settings']) - await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport']) - - appManager.on('filePanel', 'workspaceInitializationCompleted', async () => { - await appManager.registerContextMenuItems() - }) - await appManager.activatePlugin(['filePanel']) - // Set workspace after initial activation - appManager.on('editor', 'editorMounted', () => { - if (Array.isArray(workspace)) { - appManager.activatePlugin(workspace).then(async () => { - try { - if (params.deactivate) { - await appManager.deactivatePlugin(params.deactivate.split(',')) - } - } catch (e) { - console.log(e) - } - - if (params.code) { - // if code is given in url we focus on solidity plugin - menuicons.select('solidity') - } else { - // If plugins are loaded from the URL params, we focus on the last one. - if (pluginLoader.current === 'queryParams' && workspace.length > 0) menuicons.select(workspace[workspace.length - 1]) - } - - if (params.call) { - const callDetails = params.call.split('//') - if (callDetails.length > 1) { - toolTip(`initiating ${callDetails[0]} ...`) - // @todo(remove the timeout when activatePlugin is on 0.3.0) - appManager.call(...callDetails).catch(console.error) - } - } - }).catch(console.error) - } - }) - // activate solidity plugin - appManager.activatePlugin(['solidity', 'udapp']) - - // Load and start the service who manager layout and frame - const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature) + renderComponent () { + return ReactDOM.render( + , this.el) + } - if (params.embed) framingService.embed() - framingService.start(params) + init () { + this.renderComponent() } } diff --git a/libs/remix-ui/app/.babelrc b/libs/remix-ui/app/.babelrc new file mode 100644 index 0000000000..09d67939cc --- /dev/null +++ b/libs/remix-ui/app/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@nrwl/react/babel"], + "plugins": [] +} diff --git a/libs/remix-ui/app/.eslintrc b/libs/remix-ui/app/.eslintrc new file mode 100644 index 0000000000..977f139a09 --- /dev/null +++ b/libs/remix-ui/app/.eslintrc @@ -0,0 +1,248 @@ +{ + "rules": { + "array-callback-return": "warn", + "dot-location": ["warn", "property"], + "eqeqeq": ["warn", "smart"], + "new-parens": "warn", + "no-caller": "warn", + "no-cond-assign": ["warn", "except-parens"], + "no-const-assign": "warn", + "no-control-regex": "warn", + "no-delete-var": "warn", + "no-dupe-args": "warn", + "no-dupe-keys": "warn", + "no-duplicate-case": "warn", + "no-empty-character-class": "warn", + "no-empty-pattern": "warn", + "no-eval": "warn", + "no-ex-assign": "warn", + "no-extend-native": "warn", + "no-extra-bind": "warn", + "no-extra-label": "warn", + "no-fallthrough": "warn", + "no-func-assign": "warn", + "no-implied-eval": "warn", + "no-invalid-regexp": "warn", + "no-iterator": "warn", + "no-label-var": "warn", + "no-labels": ["warn", { "allowLoop": true, "allowSwitch": false }], + "no-lone-blocks": "warn", + "no-loop-func": "warn", + "no-mixed-operators": [ + "warn", + { + "groups": [ + ["&", "|", "^", "~", "<<", ">>", ">>>"], + ["==", "!=", "===", "!==", ">", ">=", "<", "<="], + ["&&", "||"], + ["in", "instanceof"] + ], + "allowSamePrecedence": false + } + ], + "no-multi-str": "warn", + "no-native-reassign": "warn", + "no-negated-in-lhs": "warn", + "no-new-func": "warn", + "no-new-object": "warn", + "no-new-symbol": "warn", + "no-new-wrappers": "warn", + "no-obj-calls": "warn", + "no-octal": "warn", + "no-octal-escape": "warn", + "no-redeclare": "warn", + "no-regex-spaces": "warn", + "no-restricted-syntax": ["warn", "WithStatement"], + "no-script-url": "warn", + "no-self-assign": "warn", + "no-self-compare": "warn", + "no-sequences": "warn", + "no-shadow-restricted-names": "warn", + "no-sparse-arrays": "warn", + "no-template-curly-in-string": "warn", + "no-this-before-super": "warn", + "no-throw-literal": "warn", + "no-restricted-globals": [ + "error", + "addEventListener", + "blur", + "close", + "closed", + "confirm", + "defaultStatus", + "defaultstatus", + "event", + "external", + "find", + "focus", + "frameElement", + "frames", + "history", + "innerHeight", + "innerWidth", + "length", + "location", + "locationbar", + "menubar", + "moveBy", + "moveTo", + "name", + "onblur", + "onerror", + "onfocus", + "onload", + "onresize", + "onunload", + "open", + "opener", + "opera", + "outerHeight", + "outerWidth", + "pageXOffset", + "pageYOffset", + "parent", + "print", + "removeEventListener", + "resizeBy", + "resizeTo", + "screen", + "screenLeft", + "screenTop", + "screenX", + "screenY", + "scroll", + "scrollbars", + "scrollBy", + "scrollTo", + "scrollX", + "scrollY", + "self", + "status", + "statusbar", + "stop", + "toolbar", + "top" + ], + "no-unexpected-multiline": "warn", + "no-unreachable": "warn", + "no-unused-expressions": [ + "error", + { + "allowShortCircuit": true, + "allowTernary": true, + "allowTaggedTemplates": true + } + ], + "no-unused-labels": "warn", + "no-useless-computed-key": "warn", + "no-useless-concat": "warn", + "no-useless-escape": "warn", + "no-useless-rename": [ + "warn", + { + "ignoreDestructuring": false, + "ignoreImport": false, + "ignoreExport": false + } + ], + "no-with": "warn", + "no-whitespace-before-property": "warn", + "react-hooks/exhaustive-deps": "warn", + "require-yield": "warn", + "rest-spread-spacing": ["warn", "never"], + "strict": ["warn", "never"], + "unicode-bom": ["warn", "never"], + "use-isnan": "warn", + "valid-typeof": "warn", + "no-restricted-properties": [ + "error", + { + "object": "require", + "property": "ensure", + "message": "Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting" + }, + { + "object": "System", + "property": "import", + "message": "Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting" + } + ], + "getter-return": "warn", + "import/first": "error", + "import/no-amd": "error", + "import/no-webpack-loader-syntax": "error", + "react/forbid-foreign-prop-types": ["warn", { "allowInPropTypes": true }], + "react/jsx-no-comment-textnodes": "warn", + "react/jsx-no-duplicate-props": "warn", + "react/jsx-no-target-blank": "warn", + "react/jsx-no-undef": "error", + "react/jsx-pascal-case": ["warn", { "allowAllCaps": true, "ignore": [] }], + "react/jsx-uses-react": "warn", + "react/jsx-uses-vars": "warn", + "react/no-danger-with-children": "warn", + "react/no-direct-mutation-state": "warn", + "react/no-is-mounted": "warn", + "react/no-typos": "error", + "react/react-in-jsx-scope": "error", + "react/require-render-return": "error", + "react/style-prop-object": "warn", + "react/jsx-no-useless-fragment": "warn", + "jsx-a11y/accessible-emoji": "warn", + "jsx-a11y/alt-text": "warn", + "jsx-a11y/anchor-has-content": "warn", + "jsx-a11y/anchor-is-valid": [ + "warn", + { "aspects": ["noHref", "invalidHref"] } + ], + "jsx-a11y/aria-activedescendant-has-tabindex": "warn", + "jsx-a11y/aria-props": "warn", + "jsx-a11y/aria-proptypes": "warn", + "jsx-a11y/aria-role": "warn", + "jsx-a11y/aria-unsupported-elements": "warn", + "jsx-a11y/heading-has-content": "warn", + "jsx-a11y/iframe-has-title": "warn", + "jsx-a11y/img-redundant-alt": "warn", + "jsx-a11y/no-access-key": "warn", + "jsx-a11y/no-distracting-elements": "warn", + "jsx-a11y/no-redundant-roles": "warn", + "jsx-a11y/role-has-required-aria-props": "warn", + "jsx-a11y/role-supports-aria-props": "warn", + "jsx-a11y/scope": "warn", + "react-hooks/rules-of-hooks": "error", + "default-case": "off", + "no-dupe-class-members": "off", + "no-undef": "off", + "@typescript-eslint/consistent-type-assertions": "warn", + "no-array-constructor": "off", + "@typescript-eslint/no-array-constructor": "warn", + "@typescript-eslint/no-namespace": "error", + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": [ + "warn", + { + "functions": false, + "classes": false, + "variables": false, + "typedefs": false + } + ], + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { "args": "none", "ignoreRestSiblings": true } + ], + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": "warn" + }, + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "jest": true, + "node": true + }, + "settings": { "react": { "version": "detect" } }, + "plugins": ["import", "jsx-a11y", "react", "react-hooks"], + "extends": ["../../../.eslintrc"], + "ignorePatterns": ["!**/*"] +} diff --git a/libs/remix-ui/app/README.md b/libs/remix-ui/app/README.md new file mode 100644 index 0000000000..25094197a8 --- /dev/null +++ b/libs/remix-ui/app/README.md @@ -0,0 +1,7 @@ +# remix-ui-clipboard + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test remix-ui-clipboard` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/remix-ui/app/src/index.ts b/libs/remix-ui/app/src/index.ts new file mode 100644 index 0000000000..1c6a12f776 --- /dev/null +++ b/libs/remix-ui/app/src/index.ts @@ -0,0 +1 @@ +export * from './lib/remix-app/remix-app' diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.css b/libs/remix-ui/app/src/lib/remix-app/remix-app.css new file mode 100644 index 0000000000..4530b4c233 --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.css @@ -0,0 +1,66 @@ +html { box-sizing: border-box; } +*, *:before, *:after { box-sizing: inherit; } +body { + /* font: 14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; */ + font-size : .8rem; +} +pre { + overflow-x: auto; +} +.remixIDE { + width : 100vw; + height : 100vh; + overflow : hidden; + flex-direction : row; + display : flex; +} +.mainpanel { + display : flex; + flex-direction : column; + overflow : hidden; + flex : 1; + min-width : 320px; +} +.iconpanel { + display : flex; + flex-direction : column; + overflow : hidden; + width : 50px; + user-select : none; +} +.sidepanel { + display : flex; + flex-direction : row-reverse; + width : 320px; +} +.highlightcode { + position : absolute; + z-index : 20; + background-color : var(--info); +} +.highlightcode_fullLine { + position : absolute; + z-index : 20; + background-color : var(--info); + opacity : 0.5; +} +.centered { + position : fixed; + top : 20%; + left : 45%; + width : 200px; + height : 200px; +} +.centered svg path { + fill: var(--secondary); +} +.centered svg polygon { + fill : var(--secondary); +} +.onboarding { + color : var(--text-info); + background-color : var(--info); +} +.matomoBtn { + width : 100px; +} \ No newline at end of file diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx new file mode 100644 index 0000000000..7891d44dd3 --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx @@ -0,0 +1,85 @@ +import React, { useEffect, useRef, useState } from 'react' +import { ModalDialog } from '@remix-ui/modal-dialog' +import './remix-app.css' + +interface IRemixAppUi { + app: any +} +export const RemixApp = (props: IRemixAppUi) => { + const [visible, setVisible] = useState(false) + const sidePanelRef = useRef(null) + const mainPanelRef = useRef(null) + const iconPanelRef = useRef(null) + const hiddenPanelRef = useRef(null) + + useEffect(() => { + + }) + + useEffect(() => { + console.log('mounting app') + if (sidePanelRef.current) { + if (props.app.sidePanel) { + sidePanelRef.current.appendChild(props.app.sidePanel.render()) + } + } + if (mainPanelRef.current) { + if (props.app.mainview) { + mainPanelRef.current.appendChild(props.app.mainview.render()) + } + } + if (iconPanelRef.current) { + if (props.app.menuicons) { + iconPanelRef.current.appendChild(props.app.menuicons.render()) + } + } + if (hiddenPanelRef.current) { + if (props.app.hiddenPanel) { + hiddenPanelRef.current.appendChild(props.app.hiddenPanel.render()) + } + } + if (props.app) { + console.log('app', props.app) + props.app.activate() + } + }, []) + + const components = { + iconPanel:
, + sidePanel:
, + mainPanel:
, + hiddenPanel:
+ } + + const handleModalOkClick = async () => { + + } + + const closeModal = async () => { + + } + + return ( + <> + + test + {components.hiddenPanel} + + + ) +} + +export default RemixApp diff --git a/libs/remix-ui/app/tsconfig.json b/libs/remix-ui/app/tsconfig.json new file mode 100644 index 0000000000..a7180ef589 --- /dev/null +++ b/libs/remix-ui/app/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/remix-ui/app/tsconfig.lib.json b/libs/remix-ui/app/tsconfig.lib.json new file mode 100644 index 0000000000..b560bc4dec --- /dev/null +++ b/libs/remix-ui/app/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/remix-ui/app/tsconfig.spec.json b/libs/remix-ui/app/tsconfig.spec.json new file mode 100644 index 0000000000..1798b378a9 --- /dev/null +++ b/libs/remix-ui/app/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.spec.js", + "**/*.spec.jsx", + "**/*.d.ts" + ] +} diff --git a/nx.json b/nx.json index 9f4c399b8e..05040804bc 100644 --- a/nx.json +++ b/nx.json @@ -130,6 +130,9 @@ "remix-ui-editor": { "tags": [] }, + "remix-ui-app": { + "tags": [] + }, "remix-ui-helper": { "tags": [] }, diff --git a/package.json b/package.json index 4cd3cb70db..d6620ecdc5 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "workspace-schematic": "nx workspace-schematic", "dep-graph": "nx dep-graph", "help": "nx help", - "lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-helper,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,remix-ui-plugin-manager,remix-ui-terminal,remix-ui-editor,remix-ui-tabs", + "lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-helper,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,remix-ui-plugin-manager,remix-ui-terminal,remix-ui-editor,remix-ui-app,remix-ui-tabs", "build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd", "test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd", "publish:libs": "npm run build:libs && lerna publish --skip-git && npm run bumpVersion:libs", diff --git a/tsconfig.base.json b/tsconfig.base.json index c0f4b6f093..b442f1049f 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -66,7 +66,8 @@ "@remix-ui/plugin-manager": ["libs/remix-ui/plugin-manager/src/index.ts"], "@remix-ui/editor": ["libs/remix-ui/editor/src/index.ts"], "@remix-ui/tabs": ["libs/remix-ui/tabs/src/index.ts"], - "@remix-ui/helper": ["libs/remix-ui/helper/src/index.ts"] + "@remix-ui/helper": ["libs/remix-ui/helper/src/index.ts"], + "@remix-ui/app": ["libs/remix-ui/app/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] diff --git a/workspace.json b/workspace.json index 217c170d9d..146d9755d9 100644 --- a/workspace.json +++ b/workspace.json @@ -1023,6 +1023,22 @@ } } }, + "remix-ui-app": { + "root": "libs/remix-ui/app", + "sourceRoot": "libs/remix-ui/app/src", + "projectType": "library", + "schematics": {}, + "architect": { + "lint": { + "builder": "@nrwl/linter:lint", + "options": { + "linter": "eslint", + "tsConfig": ["libs/remix-ui/app/tsconfig.lib.json"], + "exclude": ["**/node_modules/**", "!libs/remix-ui/app/**/*"] + } + } + } + }, "remix-ui-helper": { "root": "libs/remix-ui/helper", "sourceRoot": "libs/remix-ui/helper/src",