diff --git a/apps/circuit-compiler/src/app/actions/index.ts b/apps/circuit-compiler/src/app/actions/index.ts index 894ef998b5..ef403fe541 100644 --- a/apps/circuit-compiler/src/app/actions/index.ts +++ b/apps/circuit-compiler/src/app/actions/index.ts @@ -117,7 +117,7 @@ export const generateProof = async (plugin: CircomPluginClient, appState: AppSta const r1csBuffer = await plugin.call('fileManager', 'readFile', r1csPath, { encoding: null }) // @ts-ignore const r1cs = new Uint8Array(r1csBuffer) - const wtnsPath = isElectron() ? extractParentFromKey(appState.filePath) + "/.bin/" + fileName.replace('.circom', '_js') + "/" + fileName.replace('.circom', '.wtn') : r1csPath.replace('.r1cs', '.wtn') + const wtnsPath = extractParentFromKey(appState.filePath) + "/.bin/" + fileName.replace('.circom', '_js') + "/" + fileName.replace('.circom', '.wtn') // @ts-ignore const wtnsBuffer = await plugin.call('fileManager', 'readFile', wtnsPath, { encoding: null }) // @ts-ignore diff --git a/apps/remix-ide-e2e/src/tests/remixd.test.ts b/apps/remix-ide-e2e/src/tests/remixd.test.ts index 6a0dedd582..c145a2934b 100644 --- a/apps/remix-ide-e2e/src/tests/remixd.test.ts +++ b/apps/remix-ide-e2e/src/tests/remixd.test.ts @@ -527,7 +527,7 @@ async function installFoundry(): Promise { server.stdout.on('data', function (data) { console.log(data.toString()) if ( - data.toString().includes("foundryup: use - chisel 0.3.0") + data.toString().includes("foundryup: use - chisel 1.0.0-stable") ) { console.log('resolving') resolve() diff --git a/apps/remix-ide-e2e/src/tests/script-runner.test.ts b/apps/remix-ide-e2e/src/tests/script-runner.test.ts index 493466983d..4df6de5dc7 100644 --- a/apps/remix-ide-e2e/src/tests/script-runner.test.ts +++ b/apps/remix-ide-e2e/src/tests/script-runner.test.ts @@ -2,14 +2,22 @@ import { NightwatchBrowser } from 'nightwatch' import init from '../helpers/init' -module.exports = { +const tests = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', false) }, + 'Should activate plugins': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('*[data-id="remixIdeSidePanel"]') + .waitForElementVisible('*[plugin="pluginManager"]') + .click('*[plugin="pluginManager"]') + .waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]') + .click('*[data-id="pluginManagerComponentPluginManager"]') + .scrollAndClick('*[data-id="pluginManagerComponentActivateButtonUIScriptRunner"]') + }, 'Should load default script runner': function (browser: NightwatchBrowser) { browser - .clickLaunchIcon('scriptRunnerBridge') .waitForElementVisible('[data-id="sr-loaded-default"]') .waitForElementVisible('[data-id="dependency-ethers-^5"]') .waitForElementVisible('[data-id="sr-toggle-ethers6"]') @@ -57,21 +65,36 @@ module.exports = { .waitForElementPresent('*[data-id="create-semaphore"]') .scrollAndClick('*[data-id="create-semaphore"]') .modalFooterOKClick('TemplatesSelection') - .clickLaunchIcon('scriptRunnerBridge') + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" + }) + .click({ + locateStrategy: 'xpath', + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" + }) .waitForElementVisible('[data-id="sr-loaded-default"]') .waitForElementVisible('[data-id="dependency-ethers-^5"]') .waitForElementVisible('[data-id="sr-toggle-ethers6"]') }, 'switch to default workspace that should be on ethers6': function (browser: NightwatchBrowser) { browser - .clickLaunchIcon('filePanel') .switchWorkspace('default_workspace') - .clickLaunchIcon('scriptRunnerBridge') + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" + }) + .click({ + locateStrategy: 'xpath', + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" + }) .waitForElementVisible('[data-id="sr-loaded-ethers6"]') .waitForElementPresent('[data-id="dependency-ethers-^6"]') } } +module.exports = tests + const deployWithEthersJs = ` import { ethers } from 'ethers' diff --git a/apps/remix-ide/src/app.ts b/apps/remix-ide/src/app.ts index 86eb8697a2..d9c1959445 100644 --- a/apps/remix-ide/src/app.ts +++ b/apps/remix-ide/src/app.ts @@ -78,7 +78,7 @@ import * as remixLib from '@remix-project/remix-lib' import { QueryParams } from '@remix-project/remix-lib' import { SearchPlugin } from './app/tabs/search' -import { ScriptRunnerUIPlugin } from './app/tabs/script-runner-ui' +import { ScriptRunnerBridgePlugin } from './app/plugins/script-runner-bridge' import { ElectronProvider } from './app/files/electronProvider' const Storage = remixLib.Storage @@ -103,6 +103,7 @@ import Filepanel from './app/panels/file-panel' import Editor from './app/editor/editor' import Terminal from './app/panels/terminal' import TabProxy from './app/panels/tab-proxy.js' +import { any } from 'async' const _paq = (window._paq = window._paq || []) @@ -281,7 +282,7 @@ class AppComponent { const search = new SearchPlugin() //---------------- Script Runner UI Plugin ------------------------- - const scriptRunnerUI = new ScriptRunnerUIPlugin(this.engine) + const scriptRunnerUI = new ScriptRunnerBridgePlugin(this.engine) //---- templates const templates = new TemplatesPlugin() @@ -677,7 +678,9 @@ class AppComponent { this.appManager.call('sidePanel', 'pinView', JSON.parse(lastPinned)) } }) - .catch(console.error) + .catch((e) => { + console.error(e) + }) } const loadedElement = document.createElement('span') loadedElement.setAttribute('data-id', 'apploaded') diff --git a/apps/remix-ide/src/app/components/plugin-manager-component.js b/apps/remix-ide/src/app/components/plugin-manager-component.js index e4471bbed2..0fba359315 100644 --- a/apps/remix-ide/src/app/components/plugin-manager-component.js +++ b/apps/remix-ide/src/app/components/plugin-manager-component.js @@ -136,7 +136,7 @@ export default class PluginManagerComponent extends ViewPlugin { } listenOnEvent () { - this.engine.event.on('onRegistration', () => this.renderComponent()) + this.engine.event.on('onRegistration', () => this.getAndFilterPlugins()) this.appManager.event.on('activate', () => { this.getAndFilterPlugins() }) diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 8c90ca8b8e..0a116d90c9 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -164,7 +164,9 @@ export default class TabProxy extends Plugin { }) this.on('manager', 'pluginActivated', ({ name, location, displayName, icon, description }) => { + if (location === 'mainPanel') { + console.log('pluginActivated', name, location, displayName, icon, description) this.addTab( name, displayName, @@ -201,6 +203,7 @@ export default class TabProxy extends Plugin { } focus (name) { + console.log('focus', name) this.emit('switchApp', name) this.tabsApi.activateTab(name) } @@ -324,6 +327,7 @@ export default class TabProxy extends Plugin { removeTab (name, currentFileTab) { delete this._handlers[name] let previous = currentFileTab + if(!this.loadedTabs.find(tab => tab.name === name)) return // prevent removing tab that doesn't exist this.loadedTabs = this.loadedTabs.filter((tab, index) => { if (!previous && tab.name === name) { if(index - 1 >= 0 && this.loadedTabs[index - 1]) diff --git a/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx new file mode 100644 index 0000000000..9dc6628b4f --- /dev/null +++ b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx @@ -0,0 +1,359 @@ +import { IframePlugin, IframeProfile, ViewPlugin } from '@remixproject/engine-web' +import * as packageJson from '../../../../../package.json' +import React from 'react' // eslint-disable-line +import { customScriptRunnerConfig, IScriptRunnerState, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner' +import { Profile } from '@remixproject/plugin-utils' +import { Engine, Plugin } from '@remixproject/engine' +import axios from 'axios' +import { AppModal } from '@remix-ui/app' +import { isArray } from 'lodash' +import { PluginViewWrapper } from '@remix-ui/helper' +import { CustomRemixApi } from '@remix-api' +import { ScriptRunnerUIPlugin } from '../tabs/script-runner-ui' + +const profile = { + name: 'scriptRunnerBridge', + displayName: 'Script configuration', + methods: ['execute'], + events: ['log', 'info', 'warn', 'error'], + icon: 'assets/img/solid-gear-circle-play.svg', + description: 'Configure the dependencies for running scripts.', + kind: '', + version: packageJson.version, + maintainedBy: 'Remix', +} + +const configFileName = '.remix/script.config.json' + +let baseUrl = 'https://remix-project-org.github.io/script-runner-generator' +const customBuildUrl = 'http://localhost:4000/build' // this will be used when the server is ready + +export class ScriptRunnerBridgePlugin extends Plugin { + engine: Engine + dispatch: React.Dispatch = () => {} + workspaceScriptRunnerDefaults: Record + customConfig: ScriptRunnerConfig + configurations: ProjectConfiguration[] + activeConfig: ProjectConfiguration + enableCustomScriptRunner: boolean + plugin: Plugin + scriptRunnerProfileName: string + initialized: boolean = false + constructor(engine: Engine) { + super(profile) + this.engine = engine + this.workspaceScriptRunnerDefaults = {} + this.plugin = this + this.enableCustomScriptRunner = false // implement this later + } + + async onActivation() { + if (!this.initialized) { + this.setListeners() + await this.init() + this.initialized = true + } + this.renderComponent() + } + + async init() { + await this.loadCustomConfig() + await this.loadConfigurations() + const ui: ScriptRunnerUIPlugin = new ScriptRunnerUIPlugin(this) + this.engine.register(ui) + + } + + setListeners() { + this.on('filePanel', 'setWorkspace', async (workspace: string) => { + this.activeConfig = null + this.customConfig = { + defaultConfig: 'default', + customConfig: { + baseConfiguration: 'default', + dependencies: [], + }, + } + await this.loadCustomConfig() + await this.loadConfigurations() + this.renderComponent() + }) + + this.plugin.on('fileManager', 'fileSaved', async (file: string) => { + if (file === configFileName && this.enableCustomScriptRunner) { + await this.loadCustomConfig() + this.renderComponent() + } + }) + } + + async renderComponent() { + this.emit('render') + } + + async selectScriptRunner(config: ProjectConfiguration) { + if (await this.loadScriptRunner(config)) await this.saveCustomConfig(this.customConfig) + } + + async loadScriptRunner(config: ProjectConfiguration): Promise { + const profile: Profile = await this.plugin.call('manager', 'getProfile', 'scriptRunner') + this.scriptRunnerProfileName = profile.name + const testPluginName = localStorage.getItem('test-plugin-name') + const testPluginUrl = localStorage.getItem('test-plugin-url') + + let url = `${baseUrl}?template=${config.name}×tamp=${Date.now()}` + if (testPluginName === 'scriptRunner') { + // if testpluginurl has template specified only use that + if (testPluginUrl.indexOf('template') > -1) { + url = testPluginUrl + } else { + baseUrl = `//${new URL(testPluginUrl).host}` + url = `${baseUrl}?template=${config.name}×tamp=${Date.now()}` + } + } + //console.log('loadScriptRunner', profile) + const newProfile: IframeProfile = { + ...profile, + name: profile.name + config.name, + location: 'hiddenPanel', + url: url, + } + + let result = null + try { + this.setIsLoading(config.name, true) + const plugin: IframePlugin = new IframePlugin(newProfile) + if (!this.engine.isRegistered(newProfile.name)) { + await this.engine.register(plugin) + } + await this.plugin.call('manager', 'activatePlugin', newProfile.name) + + this.activeConfig = config + this.on(newProfile.name, 'log', this.log.bind(this)) + this.on(newProfile.name, 'info', this.info.bind(this)) + this.on(newProfile.name, 'warn', this.warn.bind(this)) + this.on(newProfile.name, 'error', this.error.bind(this)) + this.on(newProfile.name, 'dependencyError', this.dependencyError.bind(this)) + this.customConfig.defaultConfig = config.name + this.setErrorStatus(config.name, false, '') + result = true + } catch (e) { + console.log('Error loading script runner: ', newProfile.name, e) + const iframe = document.getElementById(`plugin-${newProfile.name}`) + if (iframe) { + await this.call('hiddenPanel', 'removeView', newProfile) + } + + delete (this.engine as any).manager.profiles[newProfile.name] + delete (this.engine as any).plugins[newProfile.name] + console.log('Error loading script runner: ', newProfile.name, e) + this.setErrorStatus(config.name, true, e) + result = false + } + + this.setIsLoading(config.name, false) + this.renderComponent() + return result + } + + async execute(script: string, filePath: string) { + this.call('terminal', 'log', { value: `running ${filePath} ...`, type: 'info' }) + if (!this.scriptRunnerProfileName || !this.engine.isRegistered(`${this.scriptRunnerProfileName}${this.activeConfig.name}`)) { + console.error('Script runner not loaded') + if (!(await this.loadScriptRunner(this.activeConfig))) { + console.error('Error loading script runner') + return + } + } + try { + this.setIsLoading(this.activeConfig.name, true) + await this.call(`${this.scriptRunnerProfileName}${this.activeConfig.name}`, 'execute', script, filePath) + } catch (e) { + console.error('Error executing script', e) + } + this.setIsLoading(this.activeConfig.name, false) + } + + async setErrorStatus(name: string, status: boolean, error: string) { + this.configurations.forEach((config) => { + if (config.name === name) { + config.errorStatus = status + config.error = error + } + }) + this.renderComponent() + } + + async setIsLoading(name: string, status: boolean) { + if (status) { + this.emit('statusChanged', { + key: 'loading', + type: 'info', + title: 'loading...', + }) + } else { + this.emit('statusChanged', { + key: 'none', + }) + } + this.configurations.forEach((config) => { + if (config.name === name) { + config.isLoading = status + } + }) + this.renderComponent() + } + + async dependencyError(data: any) { + console.log('Script runner dependency error: ', data) + let message = `Error loading dependencies: ` + if (isArray(data.data)) { + data.data.forEach((data: any) => { + message += `${data}` + }) + } + + const modal: AppModal = { + id: 'TemplatesSelection', + title: 'Missing dependencies', + message: `${message} \n\n You may need to setup a script engine for this workspace to load the correct dependencies. Do you want go to setup now?`, + okLabel: window._intl.formatMessage({ id: 'filePanel.ok' }), + cancelLabel: 'ignore', + } + const modalResult = await this.plugin.call('notification' as any, 'modal', modal) + if (modalResult) { + // await this.plugin.call('menuicons', 'select', 'scriptRunnerBridge') + } else { + } + } + + async log(data: any) { + this.emit('log', data) + } + + async warn(data: any) { + this.emit('warn', data) + } + + async error(data: any) { + this.emit('error', data) + } + + async info(data: any) { + this.emit('info', data) + } + + async loadCustomConfig(): Promise { + try { + const content = await this.plugin.call('fileManager', 'readFile', configFileName) + const parsed = JSON.parse(content) + this.customConfig = parsed + } catch (e) { + this.customConfig = { + defaultConfig: 'default', + customConfig: { + baseConfiguration: 'default', + dependencies: [], + }, + } + } + } + + async openCustomConfig() { + + try { + await this.plugin.call('fileManager', 'open', '.remix/script.config.json') + } catch (e) {} + } + + async loadConfigurations() { + try { + const response = await axios.get(`${baseUrl}/projects.json?timestamp=${Date.now()}`) + this.configurations = response.data + // find the default otherwise pick the first one as the active + this.configurations.forEach((config) => { + if (config.name === this.customConfig.defaultConfig) { + this.activeConfig = config + } + }) + if (!this.activeConfig) { + this.activeConfig = this.configurations[0] + } + } catch (error) { + console.error('Error fetching the projects data:', error) + } + } + + async saveCustomConfig(content: ScriptRunnerConfig) { + if (content.customConfig.dependencies.length === 0 && content.defaultConfig === 'default') { + try { + const exists = await this.plugin.call('fileManager', 'exists', '.remix/script.config.json') + if (exists) { + await this.plugin.call('fileManager', 'remove', '.remix/script.config.json') + } + } catch (e) {} + return + } + await this.plugin.call('fileManager', 'writeFile', '.remix/script.config.json', JSON.stringify(content, null, 2)) + } + + async activateCustomScriptRunner(config: customScriptRunnerConfig) { + try { + const result = await axios.post(customBuildUrl, config) + if (result.data.hash) { + const newConfig: ProjectConfiguration = { + name: result.data.hash, + title: 'Custom configuration', + publish: true, + description: `Extension of ${config.baseConfiguration}`, + dependencies: config.dependencies, + replacements: {}, + errorStatus: false, + error: '', + isLoading: false, + } + this.configurations.push(newConfig) + this.renderComponent() + await this.loadScriptRunner(result.data.hash) + } + return result.data.hash + } catch (error) { + let message + if (error.response) { + // The request was made and the server responded with a status code + // that falls out of the range of 2xx + console.log('Error status:', error.response.status) + console.log('Error data:', error.response.data) // This should give you the output being sent + console.log('Error headers:', error.response.headers) + + if (error.response.data.error) { + if (isArray(error.response.data.error)) { + const message = `${error.response.data.error[0]}` + this.plugin.call('notification', 'alert', { + id: 'scriptalert', + message, + title: 'Error', + }) + throw new Error(message) + } + message = `${error.response.data.error}` + } + message = `Uknown error: ${error.response.data}` + this.plugin.call('notification', 'alert', { + id: 'scriptalert', + message, + title: 'Error', + }) + throw new Error(message) + } else if (error.request) { + // The request was made but no response was received + console.log('No response received:', error.request) + throw new Error('No response received') + } else { + // Something happened in setting up the request that triggered an Error + console.log('Error message:', error.message) + throw new Error(error.message) + } + } + } +} diff --git a/apps/remix-ide/src/app/tabs/locales/en/git.json b/apps/remix-ide/src/app/tabs/locales/en/git.json index ea64a5b02d..1d32bc1cb6 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/git.json +++ b/apps/remix-ide/src/app/tabs/locales/en/git.json @@ -1,21 +1,21 @@ { - "git.push": "push", - "git.pull": "pull", - "git.commit": "commit", - "git.sync": "sync", - "git.syncchanges": "sync changes", - "git.publish": "publish", - "git.ignore": "ignore", - "git.createBranch": "create branch", - "git.deleteBranch": "delete branch", - "git.mergeBranch": "merge branch", - "git.rebaseBranch": "rebase branch", - "git.checkout": "checkout", - "git.fetch": "fetch", - "git.refresh": "refresh", - "git.unstageall": "unstage all", - "git.stageall": "stage all", - "git.noremote": "this repo has no remotes", + "git.push": "Push", + "git.pull": "Pull", + "git.commit": "Commit", + "git.sync": "Sync", + "git.syncchanges": "Sync changes", + "git.publish": "Publish", + "git.ignore": "Ignore", + "git.createBranch": "Create branch", + "git.deleteBranch": "Delete branch", + "git.mergeBranch": "Merge branch", + "git.rebaseBranch": "Rebase branch", + "git.checkout": "Checkout", + "git.fetch": "Fetch", + "git.refresh": "Refresh", + "git.unstageall": "Unstage all", + "git.stageall": "Stage all", + "git.noremote": "This repo has no remotes", "git.init": "Initialize repository", "git.setup": "Setup git" } \ No newline at end of file diff --git a/apps/remix-ide/src/app/tabs/script-runner-ui.tsx b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx index dd79bc79e5..10b0f07d71 100644 --- a/apps/remix-ide/src/app/tabs/script-runner-ui.tsx +++ b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx @@ -1,83 +1,36 @@ -import { IframePlugin, IframeProfile, ViewPlugin } from '@remixproject/engine-web' +import { ViewPlugin, ViewProfile } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' import React from 'react' // eslint-disable-line -import { customScriptRunnerConfig, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner' // eslint-disable-line -import { Profile } from '@remixproject/plugin-utils' -import { Engine, Plugin } from '@remixproject/engine' -import axios from 'axios' -import { AppModal } from '@remix-ui/app' -import { isArray } from 'lodash' +import { customScriptRunnerConfig, IScriptRunnerState, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner' import { PluginViewWrapper } from '@remix-ui/helper' -import { CustomRemixApi } from '@remix-api' +import { ScriptRunnerBridgePlugin } from '../plugins/script-runner-bridge' -const profile = { - name: 'scriptRunnerBridge', +const profile: ViewProfile = { + name: 'UIScriptRunner', displayName: 'Script configuration', - methods: ['execute'], - events: ['log', 'info', 'warn', 'error'], + methods: [], + events: [], icon: 'assets/img/solid-gear-circle-play.svg', description: 'Configure the dependencies for running scripts.', kind: '', - location: 'sidePanel', + location: 'mainPanel', version: packageJson.version, - maintainedBy: 'Remix' -} - -const configFileName = '.remix/script.config.json' - -let baseUrl = 'https://remix-project-org.github.io/script-runner-generator' -const customBuildUrl = 'http://localhost:4000/build' // this will be used when the server is ready - -interface IScriptRunnerState { - customConfig: customScriptRunnerConfig - configurations: ProjectConfiguration[] - activeConfig: ProjectConfiguration - enableCustomScriptRunner: boolean + maintainedBy: 'Remix', } export class ScriptRunnerUIPlugin extends ViewPlugin { - engine: Engine - dispatch: React.Dispatch = () => { } - workspaceScriptRunnerDefaults: Record - customConfig: ScriptRunnerConfig - configurations: ProjectConfiguration[] - activeConfig: ProjectConfiguration - enableCustomScriptRunner: boolean - plugin: Plugin - scriptRunnerProfileName: string - constructor(engine: Engine) { + dispatch: React.Dispatch = () => {} + state: IScriptRunnerState + bridge: ScriptRunnerBridgePlugin + constructor(plugin: ScriptRunnerBridgePlugin) { super(profile) - this.engine = engine - this.workspaceScriptRunnerDefaults = {} - this.plugin = this - this.enableCustomScriptRunner = false // implement this later + this.bridge = plugin } async onActivation() { - - this.on('filePanel', 'setWorkspace', async (workspace: string) => { - this.activeConfig = null - this.customConfig = - { - defaultConfig: 'default', - customConfig: { - baseConfiguration: 'default', - dependencies: [] - } - } - await this.loadCustomConfig() - await this.loadConfigurations() + this.on('scriptRunnerBridge', 'render', () => { this.renderComponent() }) - - this.plugin.on('fileManager', 'fileSaved', async (file: string) => { - if (file === configFileName && this.enableCustomScriptRunner) { - await this.loadCustomConfig() - this.renderComponent() - } - }) - await this.loadCustomConfig() - await this.loadConfigurations() this.renderComponent() } @@ -89,309 +42,37 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { ) } - setDispatch(dispatch: React.Dispatch) { + setDispatch(dispatch: React.Dispatch) { this.dispatch = dispatch this.renderComponent() } renderComponent() { this.dispatch({ - customConfig: this.customConfig, - configurations: this.configurations, - activeConfig: this.activeConfig, - enableCustomScriptRunner: this.enableCustomScriptRunner - }) - } - - updateComponent(state: IScriptRunnerState) { - return ( - - ) - } - - async selectScriptRunner(config: ProjectConfiguration) { - if (await this.loadScriptRunner(config)) - await this.saveCustomConfig(this.customConfig) - } - - async loadScriptRunner(config: ProjectConfiguration): Promise { - - const profile: Profile = await this.plugin.call('manager', 'getProfile', 'scriptRunner') - this.scriptRunnerProfileName = profile.name - const testPluginName = localStorage.getItem('test-plugin-name') - const testPluginUrl = localStorage.getItem('test-plugin-url') - - let url = `${baseUrl}?template=${config.name}×tamp=${Date.now()}` - if (testPluginName === 'scriptRunner') { - // if testpluginurl has template specified only use that - if (testPluginUrl.indexOf('template') > -1) { - url = testPluginUrl - } else { - baseUrl = `//${new URL(testPluginUrl).host}` - url = `${baseUrl}?template=${config.name}×tamp=${Date.now()}` - } - } - //console.log('loadScriptRunner', profile) - const newProfile: IframeProfile = { - ...profile, - name: profile.name + config.name, - location: 'hiddenPanel', - url: url - } - - let result = null - try { - this.setIsLoading(config.name, true) - const plugin: IframePlugin = new IframePlugin(newProfile) - if (!this.engine.isRegistered(newProfile.name)) { - - await this.engine.register(plugin) - } - await this.plugin.call('manager', 'activatePlugin', newProfile.name) - - this.activeConfig = config - this.on(newProfile.name, 'log', this.log.bind(this)) - this.on(newProfile.name, 'info', this.info.bind(this)) - this.on(newProfile.name, 'warn', this.warn.bind(this)) - this.on(newProfile.name, 'error', this.error.bind(this)) - this.on(newProfile.name, 'dependencyError', this.dependencyError.bind(this)) - this.customConfig.defaultConfig = config.name - this.setErrorStatus(config.name, false, '') - result = true - } catch (e) { - console.log('Error loading script runner: ', newProfile.name, e) - const iframe = document.getElementById(`plugin-${newProfile.name}`); - if (iframe) { - await this.call('hiddenPanel', 'removeView', newProfile) - } - - delete (this.engine as any).manager.profiles[newProfile.name] - delete (this.engine as any).plugins[newProfile.name] - console.log('Error loading script runner: ', newProfile.name, e) - this.setErrorStatus(config.name, true, e) - result = false - } - - this.setIsLoading(config.name, false) - this.renderComponent() - return result - } - - async execute(script: string, filePath: string) { - this.call('terminal', 'log', { value: `running ${filePath} ...`, type: 'info' }) - if (!this.scriptRunnerProfileName || !this.engine.isRegistered(`${this.scriptRunnerProfileName}${this.activeConfig.name}`)) { - if (!await this.loadScriptRunner(this.activeConfig)) { - console.error('Error loading script runner') - return - } - } - try { - this.setIsLoading(this.activeConfig.name, true) - await this.call(`${this.scriptRunnerProfileName}${this.activeConfig.name}`, 'execute', script, filePath) - } catch (e) { - console.error('Error executing script', e) - } - this.setIsLoading(this.activeConfig.name, false) - - } - - async setErrorStatus(name: string, status: boolean, error: string) { - this.configurations.forEach((config) => { - if (config.name === name) { - config.errorStatus = status - config.error = error - } + customConfig: this.bridge.customConfig.customConfig, + configurations: this.bridge.configurations, + activeConfig: this.bridge.activeConfig, + enableCustomScriptRunner: this.bridge.enableCustomScriptRunner, }) - this.renderComponent() - } - - async setIsLoading(name: string, status: boolean) { - if (status) { - this.emit('statusChanged', { - key: 'loading', - type: 'info', - title: 'loading...' - }) - } else { - this.emit('statusChanged', { - key: 'none' - }) - } - this.configurations.forEach((config) => { - if (config.name === name) { - config.isLoading = status - } - }) - this.renderComponent() - } - - async dependencyError(data: any) { - console.log('Script runner dependency error: ', data) - let message = `Error loading dependencies: ` - if (isArray(data.data)) { - data.data.forEach((data: any) => { - message += `${data}` - }) - } - - const modal: AppModal = { - id: 'TemplatesSelection', - title: 'Missing dependencies', - message: `${message} \n\n You may need to setup a script engine for this workspace to load the correct dependencies. Do you want go to setup now?`, - okLabel: window._intl.formatMessage({ id: 'filePanel.ok' }), - cancelLabel: 'ignore' - } - const modalResult = await this.plugin.call('notification' as any, 'modal', modal) - if (modalResult) { - await this.plugin.call('menuicons', 'select', 'scriptRunnerBridge') - } else { - - } - } - - async log(data: any) { - this.emit('log', data) - } - - async warn(data: any) { - this.emit('warn', data) - } - - async error(data: any) { - this.emit('error', data) } - async info(data: any) { - this.emit('info', data) + async activateCustomScriptRunner(config: customScriptRunnerConfig) { + this.bridge.activateCustomScriptRunner(config) } - async loadCustomConfig(): Promise { - try { - const content = await this.plugin.call('fileManager', 'readFile', configFileName) - const parsed = JSON.parse(content) - this.customConfig = parsed - } catch (e) { - this.customConfig = { - defaultConfig: 'default', - customConfig: { - baseConfiguration: 'default', - dependencies: [] - } - } - } + async saveCustomConfig(content: ScriptRunnerConfig) { + this.bridge.saveCustomConfig(content) } async openCustomConfig() { - try { - await this.plugin.call('fileManager', 'open', '.remix/script.config.json') - } catch (e) { - - } - } - - async loadConfigurations() { - try { - const response = await axios.get(`${baseUrl}/projects.json?timestamp=${Date.now()}`); - this.configurations = response.data; - // find the default otherwise pick the first one as the active - this.configurations.forEach((config) => { - if (config.name === (this.customConfig.defaultConfig)) { - this.activeConfig = config; - } - }); - if (!this.activeConfig) { - this.activeConfig = this.configurations[0]; - } - } catch (error) { - console.error("Error fetching the projects data:", error); - } - + this.bridge.openCustomConfig() } - async saveCustomConfig(content: ScriptRunnerConfig) { - if (content.customConfig.dependencies.length === 0 && content.defaultConfig === 'default') { - try { - const exists = await this.plugin.call('fileManager', 'exists', '.remix/script.config.json') - if (exists) { - await this.plugin.call('fileManager', 'remove', '.remix/script.config.json') - } - } catch (e) { - } - return - } - await this.plugin.call('fileManager', 'writeFile', '.remix/script.config.json', JSON.stringify(content, null, 2)) + async selectScriptRunner(config: ProjectConfiguration) { + this.bridge.selectScriptRunner(config) } - async activateCustomScriptRunner(config: customScriptRunnerConfig) { - try { - const result = await axios.post(customBuildUrl, config) - if (result.data.hash) { - - const newConfig: ProjectConfiguration = { - name: result.data.hash, - title: 'Custom configuration', - publish: true, - description: `Extension of ${config.baseConfiguration}`, - dependencies: config.dependencies, - replacements: {}, - errorStatus: false, - error: '', - isLoading: false - }; - this.configurations.push(newConfig) - this.renderComponent() - await this.loadScriptRunner(result.data.hash) - } - return result.data.hash - } catch (error) { - let message - if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - console.log('Error status:', error.response.status); - console.log('Error data:', error.response.data); // This should give you the output being sent - console.log('Error headers:', error.response.headers); - - if (error.response.data.error) { - - if (isArray(error.response.data.error)) { - const message = `${error.response.data.error[0]}` - this.plugin.call('notification', 'alert', { - id: 'scriptalert', - message, - title: 'Error' - }) - throw new Error(message) - } - message = `${error.response.data.error}` - } - message = `Uknown error: ${error.response.data}` - this.plugin.call('notification', 'alert', { - id: 'scriptalert', - message, - title: 'Error' - }) - throw new Error(message) - } else if (error.request) { - // The request was made but no response was received - console.log('No response received:', error.request); - throw new Error('No response received') - } else { - // Something happened in setting up the request that triggered an Error - console.log('Error message:', error.message); - throw new Error(error.message) - } - - } + updateComponent(state: IScriptRunnerState) { + return } - -} \ No newline at end of file +} diff --git a/apps/remixdesktop/test/tests/app/foundry.test.ts b/apps/remixdesktop/test/tests/app/foundry.test.ts index f4d2615f93..a1d733d09e 100644 --- a/apps/remixdesktop/test/tests/app/foundry.test.ts +++ b/apps/remixdesktop/test/tests/app/foundry.test.ts @@ -89,7 +89,7 @@ async function installFoundry(): Promise { server.stdout.on('data', function (data) { console.log(data.toString()) if ( - data.toString().includes("foundryup: use - chisel 0.3.0") + data.toString().includes("foundryup: use - chisel 1.0.0-stable") ) { console.log('resolving') resolve() diff --git a/apps/remixdesktop/test/tests/app/github_3.test.ts b/apps/remixdesktop/test/tests/app/github_3.test.ts index 12542bb86f..f2c8f1132b 100644 --- a/apps/remixdesktop/test/tests/app/github_3.test.ts +++ b/apps/remixdesktop/test/tests/app/github_3.test.ts @@ -131,7 +131,7 @@ const tests = { .elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) { console.log('Number of commit-summary elements:', (result.value as any).length); browser.assert.ok((result.value as any).length > commitCount) - }) + }).pause(10000) }, 'load more branches from remote #group3': function (browser: NightwatchBrowser) { @@ -155,15 +155,34 @@ const tests = { if (useIsoGit) { - browser.waitForElementVisible('*[data-id="remote-sync-origin"]') + const branchSelector = '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]'; + + browser + .waitForElementVisible('*[data-id="remote-sync-origin"]') .click('*[data-id="remote-sync-origin"]') .waitForElementVisible('*[data-id="loader-indicator"]') - .waitForElementNotPresent('*[data-id="loader-indicator"]') - .pause(2000) - .elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) { - console.log('Number of branches elements:', (result.value as any).length); - browser.assert.ok((result.value as any).length > branchCount) - }) + + browser.perform(function (done) { + function checkElements() { + browser.execute( + function (xpath) { + return document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength; + }, + [branchSelector], + function (result) { + if ((result.value as number) > 1) { + console.log('Number of loaded branches elements:', result.value); + done(); + } else { + browser.pause(1000); // Wait and check again + checkElements(); + } + } + ); + } + + checkElements(); + }); } else { browser.waitForElementVisible('*[data-id="show-more-branches-on-remote"]') .click('*[data-id="show-more-branches-on-remote"]') diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx index e3779944b9..d51e5f10d5 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx @@ -46,7 +46,7 @@ const iconButtons: HometabIconSection[] = [ { textToolip: , matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'discord'], - urlLink: 'https://discord.com/invite/nfv6ZYjAeP', + urlLink: 'https://discord.gg/zUNteAzJs3', iconClass: 'fa-discord', placement: 'top' } diff --git a/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx b/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx index 4ef9c6f216..b891e2c995 100644 --- a/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx @@ -32,14 +32,14 @@ function PluginButton({ imgPath, envID, envText, callback, l2, description, main {l2 && } { maintainedBy?.toLowerCase() === 'remix' ? ( }> - + ) : maintainedBy ? ( - + ) : (}> - + ) } diff --git a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx index 64a9c912b2..8974b538f2 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx @@ -50,14 +50,14 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
{ plugin?.profile?.maintainedBy?.toLowerCase() === 'remix' ? ( }> - + ) : plugin?.profile?.maintainedBy ? ( - + ) : (}> - + ) }
diff --git a/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx b/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx index a83e177312..23a960bccd 100644 --- a/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx +++ b/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx @@ -26,7 +26,7 @@ function ActivePluginCard({ profile, buttonText, deactivatePlugin }: PluginCardP tooltipClasses="text-nowrap" tooltipText={} > - + ) : profile?.maintainedBy ? ( - + ) : (} > - + ) } {profile.documentation && ( diff --git a/libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx b/libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx index 992205d28c..d664a83041 100644 --- a/libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx +++ b/libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx @@ -43,7 +43,7 @@ function InactivePluginCard({ profile, buttonText, activatePlugin }: PluginCardP tooltipClasses="text-nowrap" tooltipText={} > - + ) : profile?.maintainedBy ? ( - + ) : (} > - + ) } {profile.documentation && ( diff --git a/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx index a241f2713d..f7a4948f12 100644 --- a/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx +++ b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx @@ -33,7 +33,7 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => { } return ( -
+
{configurations.filter((config) => config.publish).map((config: ProjectConfiguration, index) => (
diff --git a/libs/remix-ui/scriptrunner/src/types/index.ts b/libs/remix-ui/scriptrunner/src/types/index.ts index d675fa2084..7ebddb9a0c 100644 --- a/libs/remix-ui/scriptrunner/src/types/index.ts +++ b/libs/remix-ui/scriptrunner/src/types/index.ts @@ -35,3 +35,10 @@ export interface ScriptRunnerConfig { customConfig: customScriptRunnerConfig } +export interface IScriptRunnerState { + customConfig: customScriptRunnerConfig + configurations: ProjectConfiguration[] + activeConfig: ProjectConfiguration + enableCustomScriptRunner: boolean + } + diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx index 0d279961a3..f20ea4111c 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx @@ -243,7 +243,8 @@ export const TabsUI = (props: TabsUIProps) => { data-id="script-config" className="btn text-dark border-left ml-2 pr-0 py-0 d-flex" onClick={async () => { - props.plugin.call('menuicons', 'select', 'scriptRunnerBridge') + await props.plugin.call('manager', 'activatePlugin', 'UIScriptRunner') + await props.plugin.call('tabs', 'focus', 'UIScriptRunner') }} >