diff --git a/README.md b/README.md
index 57cf2b34b4..b8f700f796 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@ git clone https://github.com/ethereum/remix-project.git
```bash
cd remix-project
npm install
+npm run build:libs // Build remix libs
nx build
nx serve
```
diff --git a/apps/remix-ide-e2e/src/tests/importFromGithub.test.ts b/apps/remix-ide-e2e/src/tests/importFromGithub.test.ts
new file mode 100644
index 0000000000..036d9ceae9
--- /dev/null
+++ b/apps/remix-ide-e2e/src/tests/importFromGithub.test.ts
@@ -0,0 +1,66 @@
+'use strict'
+import { NightwatchBrowser } from 'nightwatch'
+import init from '../helpers/init'
+
+const testData = {
+ validURL: 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol',
+ invalidURL: 'https://github.com/Oppelin/Roles.sol'
+}
+
+module.exports = {
+ before: function (browser: NightwatchBrowser, done: VoidFunction) {
+ init(browser, done)
+ },
+
+ 'Import from GitHub Modal': function (browser: NightwatchBrowser) {
+ browser.clickLaunchIcon('home')
+ .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
+ .clickLaunchIcon('filePanel')
+ .click('div[title="home"]')
+ .waitForElementVisible('button[data-id="landingPageImportFromGitHubButton"]')
+ .pause(1000)
+ .scrollAndClick('button[data-id="landingPageImportFromGitHubButton"]')
+ .waitForElementVisible('*[data-id="homeTabModalDialogModalTitle-react"]')
+ .assert.containsText('*[data-id="homeTabModalDialogModalTitle-react"]', 'Import from Github')
+ .waitForElementVisible('*[data-id="homeTabModalDialogModalBody-react"]')
+ .assert.containsText('*[data-id="homeTabModalDialogModalBody-react"]', 'Enter the github URL you would like to load.')
+ .waitForElementVisible('*[data-id="homeTabModalDialogCustomPromptText"]')
+ .refresh()
+ },
+
+ 'Display Error Message For Invalid GitHub URL Modal': function (browser: NightwatchBrowser) {
+ browser
+ .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
+ .clickLaunchIcon('settings')
+ .clickLaunchIcon('filePanel')
+ .scrollAndClick('*[data-id="landingPageImportFromGitHubButton"]')
+ .waitForElementVisible('input[data-id="homeTabModalDialogCustomPromptText"]')
+ .execute(() => {
+ (document.querySelector('input[data-id="homeTabModalDialogCustomPromptText"]') as any).focus()
+ }, [], () => {})
+ .setValue('input[data-id="homeTabModalDialogCustomPromptText"]', testData.invalidURL)
+ .waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]')
+ .scrollAndClick('[data-id="homeTab-modal-footer-ok-react"]') // submitted
+ .waitForElementVisible('*[data-shared="tooltipPopup"]')
+ .assert.containsText('*[data-shared="tooltipPopup"] span', 'not found ' + testData.invalidURL)
+ },
+
+ 'Import From Github For Valid URL': function (browser: NightwatchBrowser) {
+ browser
+ .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
+ .clickLaunchIcon('settings')
+ .clickLaunchIcon('filePanel')
+ .scrollAndClick('*[data-id="landingPageImportFromGitHubButton"]')
+ .waitForElementVisible('*[data-id="homeTabModalDialogCustomPromptText"]')
+ .clearValue('*[data-id="homeTabModalDialogCustomPromptText"]')
+ .execute(() => {
+ (document.querySelector('input[data-id="homeTabModalDialogCustomPromptText"]') as any).focus()
+ }, [], () => {})
+ .setValue('input[data-id="homeTabModalDialogCustomPromptText"]', testData.validURL)
+ .waitForElementVisible('*[data-id="homeTab-modal-footer-ok-react"]')
+ .scrollAndClick('[data-id="homeTab-modal-footer-ok-react"]')
+ .openFile('github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol')
+ .waitForElementVisible("div[title='default_workspace/github/OpenZeppelin/openzeppelin-solidity/contracts/access/Roles.sol'")
+ .end()
+ }
+}
diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js
index 1fd8172e6e..e569cc5e15 100644
--- a/apps/remix-ide/src/app.js
+++ b/apps/remix-ide/src/app.js
@@ -1,13 +1,7 @@
'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'
@@ -17,14 +11,16 @@ 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 { FramingService } from './framingService'
+
+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 csjs = require('csjs-inject')
-const yo = require('yo-yo')
+
const remixLib = require('@remix-project/remix-lib')
const registry = require('./global/registry')
@@ -33,8 +29,7 @@ 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')
@@ -54,93 +49,13 @@ 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;
- overflow : auto;
- }
- .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 {
+class AppComponent {
constructor (api = {}, events = {}, opts = {}) {
- var self = this
+ const self = this
self.appManager = new RemixAppManager({})
self._components = {}
- self._view = {}
- self._view.splashScreen = yo`
-
- ${basicLogo()}
-
- REMIX IDE
-
-
- `
- document.body.appendChild(self._view.splashScreen)
-
+ self.registry = registry
// setup storage
const configStorage = new Storage('config-v0.8:')
@@ -162,67 +77,22 @@ class App {
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`
-
- ${self._view.iconpanel}
- ${self._view.sidepanel}
- ${self._components.resizeFeature.render()}
- ${self._view.mainpanel}
-
- `
- 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 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)
+
+ const matomoDomains = {
+ 'remix-alpha.ethereum.org': 27,
+ 'remix-beta.ethereum.org': 25,
+ 'remix.ethereum.org': 23
}
+ self.showMatamo = (matomoDomains[window.location.hostname] && !registry.get('config').api.exists('settings/matomo-analytics'))
+ self.walkthroughService = new WalkthroughService(appManager, self.showMatamo)
const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080']
// workaround for Electron support
@@ -233,23 +103,11 @@ class App {
}
}
- // 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)
- })
+ self.themeModule = new ThemeModule(registry)
+ registry.put({ api: self.themeModule, name: 'themeModule' })
+
// ----------------- editor service ----------------------------
const editor = new Editor() // wrapper around ace editor
registry.put({ api: editor, name: 'editor' })
@@ -289,10 +147,10 @@ class App {
{ appManager, blockchain },
{
getPosition: (event) => {
- var limitUp = 36
- var limitDown = 20
- var height = window.innerHeight
- var newpos = (event.pageY < limitUp) ? limitUp : event.pageY
+ const limitUp = 36
+ const limitDown = 20
+ const height = window.innerHeight
+ let newpos = (event.pageY < limitUp) ? limitUp : event.pageY
newpos = (newpos < height - limitDown) ? newpos : height - limitDown
return height - newpos
}
@@ -300,10 +158,10 @@ class App {
)
const contextualListener = new ContextualListener({ editor })
- engine.register([
+ self.engine.register([
blockchain,
contentImport,
- themeModule,
+ self.themeModule,
editor,
fileManager,
compilerMetadataGenerator,
@@ -315,110 +173,44 @@ class App {
web3Provider,
fetchAndCompile,
dGitProvider,
- hardhatProvider
+ hardhatProvider,
+ self.walkthroughService
])
// LAYOUT & SYSTEM VIEWS
- const appPanel = new MainPanel(registry.get('config').api)
- const mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal)
- registry.put({ api: mainview, name: 'mainview' })
+ const appPanel = new MainPanel()
+ self.mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal)
+ registry.put({ api: self.mainview, name: 'mainview' })
- engine.register([
+ self.engine.register([
appPanel,
- mainview.tabProxy
+ self.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)
+ 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, menuicons, fileManager, filePanel, contentImport)
- const settings = new SettingsTab(
+ const landingPage = new LandingPage(appManager, self.menuicons, fileManager, filePanel, contentImport)
+ self.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,
+ self.engine.register([
+ self.menuicons,
landingPage,
- hiddenPanel,
- sidePanel,
+ self.hiddenPanel,
+ self.sidePanel,
filePanel,
pluginManagerComponent,
- settings
+ self.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(
@@ -429,7 +221,7 @@ class App {
filePanel,
registry.get('compilersartefacts').api,
networkModule,
- mainview,
+ self.mainview,
registry.get('fileproviders/browser').api
)
const analysis = new AnalysisTab(registry)
@@ -443,7 +235,7 @@ class App {
contentImport
)
- engine.register([
+ self.engine.register([
compileTab,
run,
debug,
@@ -454,47 +246,55 @@ class App {
filePanel.hardhatHandle,
filePanel.slitherHandle
])
+ }
+
+ async activate () {
+ const queryParams = new QueryParams()
+ const params = queryParams.get()
+ const self = this
if (isElectron()) {
- appManager.activatePlugin('remixd')
+ self.appManager.activatePlugin('remixd')
}
try {
- engine.register(await appManager.registeredPlugins())
+ self.engine.register(await self.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 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'])
+ await self.appManager.activatePlugin(['settings'])
+ await self.appManager.activatePlugin(['walkthrough'])
+
+ self.appManager.on('filePanel', 'workspaceInitializationCompleted', async () => {
+ await self.appManager.registerContextMenuItems()
})
- await appManager.activatePlugin(['filePanel'])
+
+ await self.appManager.activatePlugin(['filePanel'])
// Set workspace after initial activation
- appManager.on('editor', 'editorMounted', () => {
- if (Array.isArray(workspace)) {
- appManager.activatePlugin(workspace).then(async () => {
+ self.appManager.on('editor', 'editorMounted', () => {
+ if (Array.isArray(self.workspace)) {
+ self.appManager.activatePlugin(self.workspace).then(async () => {
try {
if (params.deactivate) {
- await appManager.deactivatePlugin(params.deactivate.split(','))
+ 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
- menuicons.select('solidity')
+ self.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 (self.appManager.pluginLoader.current === 'queryParams' && self.workspace.length > 0) self.menuicons.select(self.workspace[self.workspace.length - 1])
}
if (params.call) {
@@ -502,21 +302,20 @@ class App {
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)
+ self.appManager.call(...callDetails).catch(console.error)
}
}
}).catch(console.error)
}
})
// activate solidity plugin
- appManager.activatePlugin(['solidity', 'udapp'])
-
+ self.appManager.activatePlugin(['solidity', 'udapp'])
// Load and start the service who manager layout and frame
- const framingService = new FramingService(sidePanel, menuicons, mainview, this._components.resizeFeature)
+ const framingService = new FramingService(self.sidePanel, self.menuicons, self.mainview, null)
if (params.embed) framingService.embed()
framingService.start(params)
}
}
-module.exports = App
+export default AppComponent
diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js
index f25b04d21f..295d83fd59 100644
--- a/apps/remix-ide/src/app/editor/editor.js
+++ b/apps/remix-ide/src/app/editor/editor.js
@@ -46,7 +46,8 @@ class Editor extends Plugin {
txt: 'text',
json: 'json',
abi: 'json',
- rs: 'rust'
+ rs: 'rust',
+ cairo: 'cairo'
}
this.activated = false
diff --git a/apps/remix-ide/src/app/tabs/theme-module.js b/apps/remix-ide/src/app/tabs/theme-module.js
index 8558ad4da3..d3f5e9a98c 100644
--- a/apps/remix-ide/src/app/tabs/theme-module.js
+++ b/apps/remix-ide/src/app/tabs/theme-module.js
@@ -2,7 +2,6 @@ import { Plugin } from '@remixproject/engine'
import { EventEmitter } from 'events'
import QueryParams from '../../lib/query-params'
import * as packageJson from '../../../../../package.json'
-import yo from 'yo-yo'
const _paq = window._paq = window._paq || []
const themes = [
@@ -37,10 +36,12 @@ export class ThemeModule extends Plugin {
theme.url = window.location.origin + window.location.pathname + theme.url
return { ...acc, [theme.name]: theme }
}, {})
+ this._paq = _paq
let queryTheme = (new QueryParams()).get().theme
queryTheme = this.themes[queryTheme] ? queryTheme : null
let currentTheme = this._deps.config.get('settings/theme')
currentTheme = this.themes[currentTheme] ? currentTheme : null
+ this.currentThemeState = { queryTheme, currentTheme }
this.active = queryTheme || currentTheme || 'Dark'
this.forced = !!queryTheme
}
@@ -58,11 +59,16 @@ export class ThemeModule extends Plugin {
/**
* Init the theme
*/
- initTheme (callback) {
+ initTheme (callback) { // callback is setTimeOut in app.js which is always passed
+ if (callback) this.initCallback = callback
if (this.active) {
const nextTheme = this.themes[this.active] // Theme
document.documentElement.style.setProperty('--theme', nextTheme.quality)
- const theme = yo``
+
+ const theme = document.createElement('link')
+ theme.setAttribute('rel', 'stylesheet')
+ theme.setAttribute('href', nextTheme.url)
+ theme.setAttribute('id', 'theme-link')
theme.addEventListener('load', () => {
if (callback) callback()
})
@@ -79,12 +85,16 @@ export class ThemeModule extends Plugin {
throw new Error(`Theme ${themeName} doesn't exist`)
}
const next = themeName || this.active // Name
- if (next === this.active) return
+ if (next === this.active) return // --> exit out of this method
_paq.push(['trackEvent', 'themeModule', 'switchTo', next])
const nextTheme = this.themes[next] // Theme
if (!this.forced) this._deps.config.set('settings/theme', next)
document.getElementById('theme-link').remove()
- const theme = yo``
+ const theme = document.createElement('link')
+
+ theme.setAttribute('rel', 'stylesheet')
+ theme.setAttribute('href', nextTheme.url)
+ theme.setAttribute('id', 'theme-link')
theme.addEventListener('load', () => {
this.emit('themeLoaded', nextTheme)
this.events.emit('themeLoaded', nextTheme)
diff --git a/apps/remix-ide/src/app/ui/landing-page/landing-page.js b/apps/remix-ide/src/app/ui/landing-page/landing-page.js
index 744b5c988b..a100c4ea4e 100644
--- a/apps/remix-ide/src/app/ui/landing-page/landing-page.js
+++ b/apps/remix-ide/src/app/ui/landing-page/landing-page.js
@@ -1,110 +1,11 @@
+/* global */
+import React from 'react' // eslint-disable-line
+import ReactDOM from 'react-dom'
import * as packageJson from '../../../../../../package.json'
import { ViewPlugin } from '@remixproject/engine-web'
-import { migrateToWorkspace } from '../../../migrateFileSystem'
-import JSZip from 'jszip'
+import { RemixUiHomeTab } from '@remix-ui/home-tab' // eslint-disable-line
-const yo = require('yo-yo')
-const csjs = require('csjs-inject')
-const globalRegistry = require('../../../global/registry')
-const modalDialogCustom = require('../modal-dialog-custom')
-const modalDialog = require('../modaldialog')
-const tooltip = require('../tooltip')
const GistHandler = require('../../../lib/gist-handler')
-const QueryParams = require('../../../lib/query-params.js')
-const _paq = window._paq = window._paq || []
-
-const css = csjs`
- .text {
- cursor: pointer;
- font-weight: normal;
- max-width: 300px;
- user-select: none;
- }
- .text:hover {
- cursor: pointer;
- text-decoration: underline;
- }
- .homeContainer {
- user-select: none;
- overflow-y: hidden;
- }
- .mainContent {
- overflow-y: auto;
- flex-grow: 3;
- }
- .hpLogoContainer {
- margin: 30px;
- padding-right: 90px;
- }
- .mediaBadge {
- font-size: 2em;
- height: 2em;
- width: 2em;
- }
- .mediaBadge:focus {
- outline: none;
- }
- .image {
- height: 1em;
- width: 1em;
- text-align: center;
- }
- .logoImg {
- height: 10em;
- }
- .hpSections {
- }
- .rightPanel {
- right: 0;
- position: absolute;
- z-index: 3;
- }
- .remixHomeMedia {
- overflow-y: auto;
- overflow-x: hidden;
- max-height: 720px;
- }
- .panels {
- box-shadow: 0px 0px 13px -7px;
- }
- .labelIt {
- margin-bottom: 0;
- }
- .bigLabelSize {
- font-size: 13px;
- }
- .seeAll {
- margin-top: 7px;
- white-space: nowrap;
- }
- .importFrom p {
- margin-right: 10px;
- }
- .logoContainer img{
- height: 150px;
- opacity: 0.7;
- }
- .envLogo {
- height: 16px;
- }
- .cursorStyle {
- cursor: pointer;
- }
- .envButton {
- width: 120px;
- height: 70px;
- }
- .media {
- overflow: hidden;
- width: 400px;
- transition: .5s ease-out;
- z-index: 1000;
- }
- .migrationBtn {
- width: 100px;
- }
-}
-`
const profile = {
name: 'home',
@@ -116,7 +17,6 @@ const profile = {
location: 'mainPanel',
version: packageJson.version
}
-
export class LandingPage extends ViewPlugin {
constructor (appManager, verticalIcons, fileManager, filePanel, contentImport) {
super(profile)
@@ -127,459 +27,22 @@ export class LandingPage extends ViewPlugin {
this.appManager = appManager
this.verticalIcons = verticalIcons
this.gistHandler = new GistHandler()
- const themeQuality = globalRegistry.get('themeModule').api.currentTheme().quality
- window.addEventListener('resize', () => this.adjustMediaPanel())
- window.addEventListener('click', (e) => this.hideMediaPanel(e))
- this.twitterFrame = yo`
-
-
-
-
- `
- this.badgeTwitter = yo``
- this.badgeMedium = yo``
- this.twitterPanel = yo`
-
- `
- this.mediumPanel = yo`
-
- `
- this.adjustMediaPanel()
- globalRegistry.get('themeModule').api.events.on('themeChanged', (theme) => {
- this.onThemeChanged(theme.quality)
- })
- }
-
- adjustMediaPanel () {
- this.twitterPanel.style.maxHeight = Math.max(window.innerHeight - 150, 200) + 'px'
- this.mediumPanel.style.maxHeight = Math.max(window.innerHeight - 150, 200) + 'px'
- }
-
- hideMediaPanel (e) {
- const mediaPanelsTitle = document.getElementById('remixIDEMediaPanelsTitle')
- const mediaPanels = document.getElementById('remixIDEMediaPanels')
- if (!mediaPanelsTitle || !mediaPanels) return
- if (!mediaPanelsTitle.contains(e.target) && !mediaPanels.contains(e.target)) {
- this.mediumPanel.classList.remove('d-block')
- this.mediumPanel.classList.add('d-none')
- this.twitterPanel.classList.remove('d-block')
- this.twitterPanel.classList.add('d-none')
- }
- }
-
- onThemeChanged (themeQuality) {
- const twitterFrame = yo`
-
-
-
-
- `
- yo.update(this.twitterFrame, twitterFrame)
-
- const invertNum = (themeQuality === 'dark') ? 1 : 0
- if (this.solEnv.getElementsByTagName('img')[0]) this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- if (this.optimismEnv.getElementsByTagName('img')[0]) this.optimismEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- if (this.solhintEnv.getElementsByTagName('img')[0]) this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- if (this.learnEthEnv.getElementsByTagName('img')[0]) this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- if (this.sourcifyEnv.getElementsByTagName('img')[0]) this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- if (this.moreEnv.getElementsByTagName('img')[0]) this.moreEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- if (this.websiteIcon) this.websiteIcon.style.filter = `invert(${invertNum})`
- }
-
- showMediaPanel (e) {
- if (e.target.id === 'remixIDEHomeTwitterbtn') {
- this.mediumPanel.classList.remove('d-block')
- this.mediumPanel.classList.add('d-none')
- this.twitterPanel.classList.toggle('d-block')
- _paq.push(['trackEvent', 'pluginManager', 'media', 'twitter'])
- } else {
- this.twitterPanel.classList.remove('d-block')
- this.twitterPanel.classList.add('d-none')
- this.mediumPanel.classList.toggle('d-block')
- _paq.push(['trackEvent', 'pluginManager', 'media', 'medium'])
- }
+ this.el = document.createElement('div')
+ this.el.setAttribute('id', 'landingPageHomeContainer')
+ this.el.setAttribute('class', 'remixui_homeContainer justify-content-between bg-light d-flex')
+ this.el.setAttribute('data-id', 'landingPageHomeContainer')
}
render () {
- const load = (service, item, examples, info) => {
- const contentImport = this.contentImport
- const fileProviders = globalRegistry.get('fileproviders').api
- const msg = yo`
-
-
Enter the ${item} you would like to load.
-
${info}
-
e.g ${examples.map((url) => { return yo`
` })}
-
`
-
- const title = `Import from ${service}`
- modalDialogCustom.prompt(title, msg, null, (target) => {
- if (target !== '') {
- contentImport.import(
- target,
- (loadingMsg) => { tooltip(loadingMsg) },
- (error, content, cleanUrl, type, url) => {
- if (error) {
- modalDialogCustom.alert(title, error.message || error)
- } else {
- try {
- fileProviders.workspace.addExternal(type + '/' + cleanUrl, content, url)
- this.verticalIcons.select('filePanel')
- } catch (e) {
- modalDialogCustom.alert(title, e.message)
- }
- }
- }
- )
- }
- })
- }
-
- const startSolidity = async () => {
- await this.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting'])
- this.verticalIcons.select('solidity')
- _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity'])
- }
- const startOptimism = async () => {
- await this.appManager.activatePlugin('optimism-compiler')
- this.verticalIcons.select('optimism-compiler')
- _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'optimism-compiler'])
- }
- const startSolhint = async () => {
- await this.appManager.activatePlugin(['solidity', 'solhint'])
- this.verticalIcons.select('solhint')
- _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solhint'])
- }
- const startLearnEth = async () => {
- await this.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting'])
- this.verticalIcons.select('LearnEth')
- _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'learnEth'])
- }
- const startSourceVerify = async () => {
- await this.appManager.activatePlugin(['solidity', 'source-verification'])
- this.verticalIcons.select('source-verification')
- _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'source-verification'])
- }
- const startPluginManager = async () => {
- await this.appManager.activatePlugin('pluginManager')
- this.verticalIcons.select('pluginManager')
- }
- const startRestoreBackupZip = async () => {
- await this.appManager.activatePlugin(['restorebackupzip'])
- this.verticalIcons.select('restorebackupzip')
- _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'restorebackupzip'])
- }
-
- const createNewFile = () => {
- this.call('filePanel', 'createNewFile')
- }
-
- const saveAs = (blob, name) => {
- const node = document.createElement('a')
- node.download = name
- node.rel = 'noopener'
- node.href = URL.createObjectURL(blob)
- setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s
- setTimeout(function () {
- try {
- node.dispatchEvent(new MouseEvent('click'))
- } catch (e) {
- var evt = document.createEvent('MouseEvents')
- evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
- 20, false, false, false, false, 0, null)
- node.dispatchEvent(evt)
- }
- }, 0) // 40s
- }
-
- const downloadFiles = async () => {
- try {
- tooltip('preparing files for download, please wait..')
- const fileProviders = globalRegistry.get('fileproviders').api
- const zip = new JSZip()
- await fileProviders.browser.copyFolderToJson('/', ({ path, content }) => {
- zip.file(`remixbackup${path}`, content)
- })
- zip.generateAsync({ type: 'blob' }).then(function (blob) {
- saveAs(blob, 'remixbackup.zip')
- }).catch((e) => {
- tooltip(e.message)
- })
- } catch (e) {
- tooltip(e.message)
- }
- }
-
- const uploadFile = (target) => {
- this.call('filePanel', 'uploadFile', target)
- }
-
- const connectToLocalhost = () => {
- this.appManager.activatePlugin('remixd')
- }
- const importFromGist = () => {
- this.gistHandler.loadFromGist({ gist: '' }, globalRegistry.get('filemanager').api)
- this.verticalIcons.select('filePanel')
- }
-
- globalRegistry.get('themeModule').api.events.on('themeChanged', (theme) => {
- globalRegistry.get('themeModule').api.fixInvert(document.getElementById('remixLogo'))
- globalRegistry.get('themeModule').api.fixInvert(document.getElementById('solidityLogo'))
- globalRegistry.get('themeModule').api.fixInvert(document.getElementById('debuggerLogo'))
- globalRegistry.get('themeModule').api.fixInvert(document.getElementById('learnEthLogo'))
- globalRegistry.get('themeModule').api.fixInvert(document.getElementById('workshopLogo'))
- globalRegistry.get('themeModule').api.fixInvert(document.getElementById('moreLogo'))
- globalRegistry.get('themeModule').api.fixInvert(document.getElementById('solhintLogo'))
- })
-
- const createLargeButton = (imgPath, envID, envText, callback) => {
- return yo`
-
- `
- }
-
- // main
- this.solEnv = createLargeButton('assets/img/solidityLogo.webp', 'solidityLogo', 'Solidity', startSolidity)
- // Featured
- this.optimismEnv = createLargeButton('assets/img/optimismLogo.webp', 'optimismLogo', 'Optimism', startOptimism)
- this.solhintEnv = createLargeButton('assets/img/solhintLogo.png', 'solhintLogo', 'Solhint linter', startSolhint)
- this.learnEthEnv = createLargeButton('assets/img/learnEthLogo.webp', 'learnEthLogo', 'LearnEth', startLearnEth)
- this.sourcifyEnv = createLargeButton('assets/img/sourcifyLogo.webp', 'sourcifyLogo', 'Sourcify', startSourceVerify)
- this.moreEnv = createLargeButton('assets/img/moreLogo.webp', 'moreLogo', 'More', startPluginManager)
- this.websiteIcon = yo``
-
- const themeQuality = globalRegistry.get('themeModule').api.currentTheme().quality
- const invertNum = (themeQuality === 'dark') ? 1 : 0
- this.solEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- this.optimismEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- this.solhintEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- this.learnEthEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- this.sourcifyEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- this.moreEnv.getElementsByTagName('img')[0].style.filter = `invert(${invertNum})`
- this.websiteIcon.style.filter = `invert(${invertNum})`
-
- const switchToPreviousVersion = () => {
- const query = new QueryParams()
- query.update({ appVersion: '0.7.7' })
- _paq.push(['trackEvent', 'LoadingType', 'oldExperience_0.7.7'])
- document.location.reload()
- }
-
- const migrate = async () => {
- try {
- setTimeout(() => {
- tooltip('migrating workspace...')
- }, 500)
- const workspaceName = await migrateToWorkspace(this.fileManager, this.filePanel)
- tooltip('done. ' + workspaceName + ' created.')
- } catch (e) {
- setTimeout(() => {
- tooltip(e.message)
- }, 1000)
- }
- }
- const onAcceptDownloadn = async () => {
- await downloadFiles()
- const el = document.getElementById('modal-dialog')
- el.parentElement.removeChild(el)
- migrate()
- }
-
- const onDownload = () => {
- const el = document.getElementById('modal-dialog')
- el.parentElement.removeChild(el)
- migrate()
- }
-
- const onCancel = () => {
- const el = document.getElementById('modal-dialog')
- el.parentElement.removeChild(el)
- }
-
- const migrateWorkspace = async () => {
- modalDialog(
- 'File system Migration',
- yo`
- Do you want to download your files to local device first?
-
-
-
-
-
- `,
- {
- label: '',
- fn: null
- },
- {
- label: '',
- fn: null
- }
- )
- }
-
- const img = yo``
- const playRemi = async () => { await document.getElementById('remiAudio').play() }
- // to retrieve medium posts
- document.body.appendChild(yo``)
- const container = yo`
-
-
-
-
-
-
- ${img}
-
-
-
Quicklinks
-
Guide for migrating the old File System
-
Migration tools:
-
-
- migrateWorkspace()}>Basic migration
-
-
-
- downloadFiles()}>Download all Files
- as a backup zip
-
-
- startRestoreBackupZip()}>Restore filesfrom backup zip
-
-
Help:
-
- Gitter channel
- Report on Github
-
-
-
-
-
-
-
-
Featured Plugins
-
- ${this.solEnv}
- ${this.optimismEnv}
- ${this.learnEthEnv}
- ${this.solhintEnv}
- ${this.sourcifyEnv}
- ${this.moreEnv}
-
-
-
-
-
File
-
-
- createNewFile()}>New File
-
-
-
-
-
-
-
- connectToLocalhost()}>Connect to Localhost
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${this.badgeTwitter}
- ${this.badgeMedium}
-
-
- ${this.mediumPanel}
- ${this.twitterPanel}
-
-
-
-
-
- `
+ this.renderComponent()
+ return this.el
+ }
- return container
+ renderComponent () {
+ ReactDOM.render(
+
+ , this.el)
}
}
diff --git a/apps/remix-ide/src/assets/img/solhintLogo.png b/apps/remix-ide/src/assets/img/solhintLogo.png
deleted file mode 100644
index eab3cbb63b..0000000000
Binary files a/apps/remix-ide/src/assets/img/solhintLogo.png and /dev/null differ
diff --git a/apps/remix-ide/src/assets/img/solhintLogo.webp b/apps/remix-ide/src/assets/img/solhintLogo.webp
new file mode 100644
index 0000000000..cc9ecb3bea
Binary files /dev/null and b/apps/remix-ide/src/assets/img/solhintLogo.webp differ
diff --git a/apps/remix-ide/src/framingService.js b/apps/remix-ide/src/framingService.js
index 75c217f18a..ed029b7a65 100644
--- a/apps/remix-ide/src/framingService.js
+++ b/apps/remix-ide/src/framingService.js
@@ -8,16 +8,6 @@ export class FramingService {
}
start (params) {
- this.sidePanel.events.on('toggle', () => {
- this.resizeFeature.panel.clientWidth !== 0 ? this.resizeFeature.hidePanel() : this.resizeFeature.showPanel()
- })
- this.sidePanel.events.on('showing', () => {
- if (this.resizeFeature.panel.clientWidth === 0) this.resizeFeature.showPanel()
- })
- this.mainPanel.events.on('toggle', () => {
- this.resizeFeature.showPanel()
- })
-
this.verticalIcons.select('filePanel')
document.addEventListener('keypress', (e) => {
diff --git a/apps/remix-ide/src/index.html b/apps/remix-ide/src/index.html
index ebc8a47c71..94bb974028 100644
--- a/apps/remix-ide/src/index.html
+++ b/apps/remix-ide/src/index.html
@@ -56,6 +56,7 @@
+