From 25af64043a380813fe9b626a6dce298910b751fd Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 08:13:05 +0100 Subject: [PATCH 01/16] seperate plugins --- apps/remix-ide/src/app.ts | 9 +- .../src/app/plugins/script-runner-bridge.tsx | 361 +++++++++++++++++ .../src/app/tabs/script-runner-ui.tsx | 373 ++---------------- .../scriptrunner/src/lib/script-runner-ui.tsx | 2 +- libs/remix-ui/scriptrunner/src/types/index.ts | 8 + libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx | 2 +- 6 files changed, 406 insertions(+), 349 deletions(-) create mode 100644 apps/remix-ide/src/app/plugins/script-runner-bridge.tsx 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/plugins/script-runner-bridge.tsx b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx new file mode 100644 index 0000000000..08defc2b0c --- /dev/null +++ b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx @@ -0,0 +1,361 @@ +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/script-runner-ui.tsx b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx index 7e108c71ca..cd26947d0a 100644 --- a/apps/remix-ide/src/app/tabs/script-runner-ui.tsx +++ b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx @@ -1,7 +1,7 @@ import { IframePlugin, IframeProfile, ViewPlugin } 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 { customScriptRunnerConfig, IScriptRunnerState, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner' import { Profile } from '@remixproject/plugin-utils' import { Engine, Plugin } from '@remixproject/engine' import axios from 'axios' @@ -9,76 +9,34 @@ import { AppModal } from '@remix-ui/app' import { isArray } from 'lodash' import { PluginViewWrapper } from '@remix-ui/helper' import { CustomRemixApi } from '@remix-api' +import { ScriptRunnerBridgePlugin } from '../plugins/script-runner-bridge' const profile = { - name: 'scriptRunnerBridge', + 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() } @@ -90,310 +48,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/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx index 52692be16f..e24a10e1e0 100644 --- a/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx +++ b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx @@ -32,7 +32,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..6b3faec363 100644 --- a/libs/remix-ui/scriptrunner/src/types/index.ts +++ b/libs/remix-ui/scriptrunner/src/types/index.ts @@ -35,3 +35,11 @@ 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..94fa3ea86b 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,7 @@ 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') + props.plugin.call('manager', 'activatePlugin', 'UIScriptRunner') }} > From 67a8bc881150456f4a03e16586602a715d4db437 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:32:36 +0100 Subject: [PATCH 02/16] fix bug with tabs --- apps/remix-ide/src/app/panels/tab-proxy.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 8c90ca8b8e..f2d58a6aee 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,11 +203,13 @@ export default class TabProxy extends Plugin { } focus (name) { + console.log('focus', name) this.emit('switchApp', name) this.tabsApi.activateTab(name) } switchTab (tabName) { + console.log('switchTab', tabName) if (this._handlers[tabName]) { this._handlers[tabName].switchTo() this.tabsApi.activateTab(tabName) @@ -324,6 +328,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]) From 0360862b78a8085a951820ebe67326b7508be1c5 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:42:12 +0100 Subject: [PATCH 03/16] fix test --- .../src/tests/script-runner.test.ts | 29 ++++++++++++++++--- apps/remix-ide/src/app/panels/tab-proxy.js | 1 - 2 files changed, 25 insertions(+), 5 deletions(-) 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..5a8ed69f8d 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,19 @@ 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="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,7 +62,14 @@ 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"]') @@ -66,12 +78,21 @@ module.exports = { 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/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index f2d58a6aee..0a116d90c9 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -209,7 +209,6 @@ export default class TabProxy extends Plugin { } switchTab (tabName) { - console.log('switchTab', tabName) if (this._handlers[tabName]) { this._handlers[tabName].switchTo() this.tabsApi.activateTab(tabName) From 1426f8b0b5ea7f6394b5db12a4dcd23374c71a5d Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:59:16 +0100 Subject: [PATCH 04/16] manager fix --- apps/remix-ide/src/app/components/plugin-manager-component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() }) From fa0e1be6104bed821f66679b445e3360e31f4926 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:59:58 +0100 Subject: [PATCH 05/16] test --- apps/remix-ide-e2e/src/tests/script-runner.test.ts | 4 ++++ 1 file changed, 4 insertions(+) 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 5a8ed69f8d..ad62dcbec1 100644 --- a/apps/remix-ide-e2e/src/tests/script-runner.test.ts +++ b/apps/remix-ide-e2e/src/tests/script-runner.test.ts @@ -9,8 +9,12 @@ const tests = { }, '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"]') + .pause() .scrollAndClick('*[data-id="pluginManagerComponentActivateButtonUIScriptRunner"]') }, 'Should load default script runner': function (browser: NightwatchBrowser) { From 459c906a5c0b4f58f1dab288a862ce7bb546ea6f Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 10:08:05 +0100 Subject: [PATCH 06/16] fix test --- apps/remix-ide-e2e/src/tests/script-runner.test.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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 ad62dcbec1..4df6de5dc7 100644 --- a/apps/remix-ide-e2e/src/tests/script-runner.test.ts +++ b/apps/remix-ide-e2e/src/tests/script-runner.test.ts @@ -14,7 +14,6 @@ const tests = { .click('*[plugin="pluginManager"]') .waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]') .click('*[data-id="pluginManagerComponentPluginManager"]') - .pause() .scrollAndClick('*[data-id="pluginManagerComponentActivateButtonUIScriptRunner"]') }, 'Should load default script runner': function (browser: NightwatchBrowser) { @@ -68,11 +67,11 @@ const tests = { .modalFooterOKClick('TemplatesSelection') .waitForElementVisible({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .click({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .waitForElementVisible('[data-id="sr-loaded-default"]') .waitForElementVisible('[data-id="dependency-ethers-^5"]') @@ -80,15 +79,14 @@ const tests = { }, 'switch to default workspace that should be on ethers6': function (browser: NightwatchBrowser) { browser - .clickLaunchIcon('filePanel') .switchWorkspace('default_workspace') .waitForElementVisible({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .click({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .waitForElementVisible('[data-id="sr-loaded-ethers6"]') .waitForElementPresent('[data-id="dependency-ethers-^6"]') From 7a9235c70b6e07b3eafd90f9f7a934a9b06183eb Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 10:14:57 +0100 Subject: [PATCH 07/16] focus --- libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 94fa3ea86b..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('manager', 'activatePlugin', 'UIScriptRunner') + await props.plugin.call('manager', 'activatePlugin', 'UIScriptRunner') + await props.plugin.call('tabs', 'focus', 'UIScriptRunner') }} > From 37af06b8a3a76380690e4c396fecefd9599ea502 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 08:13:05 +0100 Subject: [PATCH 08/16] seperate plugins --- apps/remix-ide/src/app.ts | 9 +- .../src/app/plugins/script-runner-bridge.tsx | 361 +++++++++++++++++ .../src/app/tabs/script-runner-ui.tsx | 373 ++---------------- .../scriptrunner/src/lib/script-runner-ui.tsx | 2 +- libs/remix-ui/scriptrunner/src/types/index.ts | 8 + libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx | 2 +- 6 files changed, 406 insertions(+), 349 deletions(-) create mode 100644 apps/remix-ide/src/app/plugins/script-runner-bridge.tsx 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/plugins/script-runner-bridge.tsx b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx new file mode 100644 index 0000000000..08defc2b0c --- /dev/null +++ b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx @@ -0,0 +1,361 @@ +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/script-runner-ui.tsx b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx index 7e108c71ca..cd26947d0a 100644 --- a/apps/remix-ide/src/app/tabs/script-runner-ui.tsx +++ b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx @@ -1,7 +1,7 @@ import { IframePlugin, IframeProfile, ViewPlugin } 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 { customScriptRunnerConfig, IScriptRunnerState, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner' import { Profile } from '@remixproject/plugin-utils' import { Engine, Plugin } from '@remixproject/engine' import axios from 'axios' @@ -9,76 +9,34 @@ import { AppModal } from '@remix-ui/app' import { isArray } from 'lodash' import { PluginViewWrapper } from '@remix-ui/helper' import { CustomRemixApi } from '@remix-api' +import { ScriptRunnerBridgePlugin } from '../plugins/script-runner-bridge' const profile = { - name: 'scriptRunnerBridge', + 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() } @@ -90,310 +48,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/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx index 52692be16f..e24a10e1e0 100644 --- a/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx +++ b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx @@ -32,7 +32,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..6b3faec363 100644 --- a/libs/remix-ui/scriptrunner/src/types/index.ts +++ b/libs/remix-ui/scriptrunner/src/types/index.ts @@ -35,3 +35,11 @@ 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..94fa3ea86b 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,7 @@ 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') + props.plugin.call('manager', 'activatePlugin', 'UIScriptRunner') }} > From 8489ce17a747e997a429a9b07fa6ecc10f94b507 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:32:36 +0100 Subject: [PATCH 09/16] fix bug with tabs --- apps/remix-ide/src/app/panels/tab-proxy.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 8c90ca8b8e..f2d58a6aee 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,11 +203,13 @@ export default class TabProxy extends Plugin { } focus (name) { + console.log('focus', name) this.emit('switchApp', name) this.tabsApi.activateTab(name) } switchTab (tabName) { + console.log('switchTab', tabName) if (this._handlers[tabName]) { this._handlers[tabName].switchTo() this.tabsApi.activateTab(tabName) @@ -324,6 +328,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]) From 06379759744701eef9d127945377207eae04e188 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:42:12 +0100 Subject: [PATCH 10/16] fix test --- .../src/tests/script-runner.test.ts | 29 ++++++++++++++++--- apps/remix-ide/src/app/panels/tab-proxy.js | 1 - 2 files changed, 25 insertions(+), 5 deletions(-) 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..5a8ed69f8d 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,19 @@ 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="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,7 +62,14 @@ 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"]') @@ -66,12 +78,21 @@ module.exports = { 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/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index f2d58a6aee..0a116d90c9 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -209,7 +209,6 @@ export default class TabProxy extends Plugin { } switchTab (tabName) { - console.log('switchTab', tabName) if (this._handlers[tabName]) { this._handlers[tabName].switchTo() this.tabsApi.activateTab(tabName) From 150cb2235166e68c731e8de8b282c9d9c26f9e7f Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:59:16 +0100 Subject: [PATCH 11/16] manager fix --- apps/remix-ide/src/app/components/plugin-manager-component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() }) From eae8c0bfbc59a6aa1452482058336586ff9680b5 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 09:59:58 +0100 Subject: [PATCH 12/16] test --- apps/remix-ide-e2e/src/tests/script-runner.test.ts | 4 ++++ 1 file changed, 4 insertions(+) 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 5a8ed69f8d..ad62dcbec1 100644 --- a/apps/remix-ide-e2e/src/tests/script-runner.test.ts +++ b/apps/remix-ide-e2e/src/tests/script-runner.test.ts @@ -9,8 +9,12 @@ const tests = { }, '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"]') + .pause() .scrollAndClick('*[data-id="pluginManagerComponentActivateButtonUIScriptRunner"]') }, 'Should load default script runner': function (browser: NightwatchBrowser) { From 77c3b99015f3a7b5222e4ea06a57597e17891b35 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 10:08:05 +0100 Subject: [PATCH 13/16] fix test --- apps/remix-ide-e2e/src/tests/script-runner.test.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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 ad62dcbec1..4df6de5dc7 100644 --- a/apps/remix-ide-e2e/src/tests/script-runner.test.ts +++ b/apps/remix-ide-e2e/src/tests/script-runner.test.ts @@ -14,7 +14,6 @@ const tests = { .click('*[plugin="pluginManager"]') .waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]') .click('*[data-id="pluginManagerComponentPluginManager"]') - .pause() .scrollAndClick('*[data-id="pluginManagerComponentActivateButtonUIScriptRunner"]') }, 'Should load default script runner': function (browser: NightwatchBrowser) { @@ -68,11 +67,11 @@ const tests = { .modalFooterOKClick('TemplatesSelection') .waitForElementVisible({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .click({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .waitForElementVisible('[data-id="sr-loaded-default"]') .waitForElementVisible('[data-id="dependency-ethers-^5"]') @@ -80,15 +79,14 @@ const tests = { }, 'switch to default workspace that should be on ethers6': function (browser: NightwatchBrowser) { browser - .clickLaunchIcon('filePanel') .switchWorkspace('default_workspace') .waitForElementVisible({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .click({ locateStrategy: 'xpath', - selector: "//li[@data-id='UIScriptRunner' and @role='tab'" + selector: "//li[@data-id='UIScriptRunner' and @role='tab']" }) .waitForElementVisible('[data-id="sr-loaded-ethers6"]') .waitForElementPresent('[data-id="dependency-ethers-^6"]') From fc9bd59ff2b26bad08337508fd57360c52c1c48e Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 10:14:57 +0100 Subject: [PATCH 14/16] focus --- libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 94fa3ea86b..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('manager', 'activatePlugin', 'UIScriptRunner') + await props.plugin.call('manager', 'activatePlugin', 'UIScriptRunner') + await props.plugin.call('tabs', 'focus', 'UIScriptRunner') }} > From 16616052c1b85c02a76f7172d13fc8e2a1fb9a76 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 16 Jan 2025 10:21:24 +0100 Subject: [PATCH 15/16] linting --- .../src/app/plugins/script-runner-bridge.tsx | 8 +++----- apps/remix-ide/src/app/tabs/script-runner-ui.tsx | 12 +++--------- libs/remix-ui/scriptrunner/src/types/index.ts | 3 +-- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx index 08defc2b0c..9dc6628b4f 100644 --- a/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx +++ b/apps/remix-ide/src/app/plugins/script-runner-bridge.tsx @@ -1,7 +1,7 @@ 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 { customScriptRunnerConfig, IScriptRunnerState, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner' import { Profile } from '@remixproject/plugin-utils' import { Engine, Plugin } from '@remixproject/engine' import axios from 'axios' @@ -48,7 +48,7 @@ export class ScriptRunnerBridgePlugin extends Plugin { } async onActivation() { - if(!this.initialized) { + if (!this.initialized) { this.setListeners() await this.init() this.initialized = true @@ -87,12 +87,10 @@ export class ScriptRunnerBridgePlugin extends Plugin { }) } - async renderComponent() { this.emit('render') } - async selectScriptRunner(config: ProjectConfiguration) { if (await this.loadScriptRunner(config)) await this.saveCustomConfig(this.customConfig) } @@ -262,7 +260,7 @@ export class ScriptRunnerBridgePlugin extends Plugin { } async openCustomConfig() { - + try { await this.plugin.call('fileManager', 'open', '.remix/script.config.json') } catch (e) {} 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 cd26947d0a..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,17 +1,11 @@ -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, 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 { ScriptRunnerBridgePlugin } from '../plugins/script-runner-bridge' -const profile = { +const profile: ViewProfile = { name: 'UIScriptRunner', displayName: 'Script configuration', methods: [], @@ -36,7 +30,7 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { async onActivation() { this.on('scriptRunnerBridge', 'render', () => { this.renderComponent() - }) + }) this.renderComponent() } diff --git a/libs/remix-ui/scriptrunner/src/types/index.ts b/libs/remix-ui/scriptrunner/src/types/index.ts index 6b3faec363..7ebddb9a0c 100644 --- a/libs/remix-ui/scriptrunner/src/types/index.ts +++ b/libs/remix-ui/scriptrunner/src/types/index.ts @@ -35,8 +35,7 @@ export interface ScriptRunnerConfig { customConfig: customScriptRunnerConfig } - - export interface IScriptRunnerState { +export interface IScriptRunnerState { customConfig: customScriptRunnerConfig configurations: ProjectConfiguration[] activeConfig: ProjectConfiguration From 99d7132bd655ac7e8de8f523984994ee178fc334 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Sat, 15 Feb 2025 11:49:29 +0100 Subject: [PATCH 16/16] lint --- apps/remix-ide/src/app/tabs/script-runner-ui.tsx | 1 - 1 file changed, 1 deletion(-) 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 66ee56f81e..10b0f07d71 100644 --- a/apps/remix-ide/src/app/tabs/script-runner-ui.tsx +++ b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx @@ -56,7 +56,6 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { }) } - async activateCustomScriptRunner(config: customScriptRunnerConfig) { this.bridge.activateCustomScriptRunner(config) }