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 b20cf820a3..2064b2de8c 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, Dependency, ProjectConfiguration, ScriptRunnerUI } from '@remix-scriptrunner' // eslint-disable-line +import { customScriptRunnerConfig, Dependency, 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' @@ -25,50 +25,62 @@ const profile = { const configFileName = 'script.config.json' +let baseUrl = 'http://localhost:3000' +let customBuildUrl = 'http://localhost:4000/build' + +interface IScriptRunnerState { + customConfig: customScriptRunnerConfig + configurations: ProjectConfiguration[] + activeConfig: ProjectConfiguration + enableCustomScriptRunner: boolean +} + export class ScriptRunnerUIPlugin extends ViewPlugin { engine: Engine - current: string - currentTemplate: string dispatch: React.Dispatch = () => { } workspaceScriptRunnerDefaults: Record - customConfig: customScriptRunnerConfig + customConfig: ScriptRunnerConfig configurations: ProjectConfiguration[] + activeConfig: ProjectConfiguration + enableCustomScriptRunner: boolean plugin: Plugin + scriptRunnerProfileName: string constructor(engine: Engine) { super(profile) console.log('ScriptRunnerUIPlugin', this) this.engine = engine this.workspaceScriptRunnerDefaults = {} this.plugin = this + this.enableCustomScriptRunner = false } async onActivation() { - console.log('onActivation', this) - - console.log('onActivation', this.customConfig) this.on('filePanel', 'setWorkspace', async (workspace: string) => { - console.log('setWorkspace', workspace, this) - this.customConfig = { - baseConfiguration: 'default', - dependencies: [] + console.log('setWorkspace', workspace) + this.activeConfig = null + this.customConfig = + { + defaultConfig: 'default', + customConfig: { + baseConfiguration: 'default', + dependencies: [] + } } await this.loadCustomConfig() - this.loadConfigurations() + await this.loadConfigurations() this.renderComponent() - console.log('setWorkspace', this.customConfig) }) - this.plugin.on('fileManager','fileSaved', async (file: string) =>{ - console.log(file) - if(file === configFileName) { + this.plugin.on('fileManager', 'fileSaved', async (file: string) => { + + if (file === configFileName && this.enableCustomScriptRunner) { await this.loadCustomConfig() this.renderComponent() } }) - await this.loadCustomConfig() - this.loadConfigurations() + await this.loadConfigurations() this.renderComponent() } @@ -89,69 +101,137 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { this.dispatch({ customConfig: this.customConfig, configurations: this.configurations, + activeConfig: this.activeConfig, + enableCustomScriptRunner: this.enableCustomScriptRunner }) } - updateComponent(state: any) { - console.log('updateComponent', state) + updateComponent(state: IScriptRunnerState) { return ( + loadScriptRunner={this.selectScriptRunner.bind(this)} /> ) } - async loadScriptRunner(name: string) { - console.log('loadScriptRunner', name) + async selectScriptRunner(config: ProjectConfiguration) { + console.log('selectScriptRunner', config) + await this.loadScriptRunner(config) + await this.saveCustomConfig(this.customConfig) + } + + async loadScriptRunner(config: ProjectConfiguration): Promise { + console.log('loadScriptRunner', config) 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 baseUrl = 'http://localhost:3000' - let url = `${baseUrl}?template=${name}×tamp=${Date.now()}` + + 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=${name}×tamp=${Date.now()}` + url = `${baseUrl}?template=${config.name}×tamp=${Date.now()}` } } - + console.log('loadScriptRunner', profile) const newProfile: IframeProfile = { ...profile, - name: profile.name + name, - location: 'hidden', + name: profile.name + config.name, + location: 'hiddenPanel', url: url } console.log('loadScriptRunner', newProfile) + let result = null try { + this.setIsLoading(config.name, true) const plugin: IframePlugin = new IframePlugin(newProfile) - await this.engine.register(plugin) + if (!this.engine.isRegistered(newProfile.name)) { + console.log('registering plugin', plugin) + await this.engine.register(plugin) + } await this.plugin.call('manager', 'activatePlugin', newProfile.name) - this.current = newProfile.name - this.currentTemplate = 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) { - this.current = newProfile.name - this.currentTemplate = name - console.log('Already loaded') + + this.engine.remove(newProfile.name) + console.log('is registered', newProfile.name, this.engine.isRegistered(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) { - if (!this.current) await this.loadScriptRunner('default') - console.log('execute', this.current) - await this.call(this.current, 'execute', script, filePath) + console.log('is registered', `${this.scriptRunnerProfileName}${this.activeConfig.name}`, this.engine.isRegistered(`${this.scriptRunnerProfileName}${this.activeConfig.name}`)) + if (!this.scriptRunnerProfileName || !this.engine.isRegistered(`${this.scriptRunnerProfileName}${this.activeConfig.name}`)) { + if (!await this.loadScriptRunner(this.activeConfig)) { + console.error('Error loading script runner') + return + } + } + console.log('execute', this.activeConfig) + try { + await this.call(`${this.scriptRunnerProfileName}${this.activeConfig.name}`, 'execute', script, filePath) + } catch (e) { + console.error('Error executing script', e) + } + + } + + async setErrorStatus(name: string, status: boolean, error: string) { + console.log('setLoadingStatus', name, status, error) + this.configurations.forEach((config) => { + if (config.name === name) { + config.errorStatus = status + config.error = error + } + }) + this.renderComponent() + } + + async setIsLoading(name: string, status: boolean) { + console.log('setLoadingStatus', name, status) + 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) { @@ -202,40 +282,58 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { console.log('buildScriptRunner', dependencies) } - async loadCustomConfig(): Promise { + async loadCustomConfig(): Promise { console.log('loadCustomConfig') //await this.plugin.call('fileManager', 'open', 'script.config.json') try { const content = await this.plugin.call('fileManager', 'readFile', configFileName) + console.log('loadCustomConfig', content) const parsed = JSON.parse(content) this.customConfig = parsed + console.log('loadCustomConfig', this.customConfig) } catch (e) { return { - baseConfiguration: 'default', - dependencies: [] + defaultConfig: 'default', + customConfig: { + baseConfiguration: 'default', + dependencies: [] + } } } + } async openCustomConfig() { - try { - await this.plugin.call('fileManager', 'open', 'script.config.json') - }catch(e){ + try { + await this.plugin.call('fileManager', 'open', 'script.config.json') + } catch (e) { - } + } } async loadConfigurations() { try { - const response = await axios.get('http://localhost:3000/projects.json?timestamp=' + Date.now()); + 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) => { + console.log('loadConfigurations', config.name, this.customConfig.defaultConfig) + if (config.name === (this.customConfig.defaultConfig)) { + this.activeConfig = config; + console.log('loadConfigurations found', this.activeConfig) + } + }); + if (!this.activeConfig) { + this.activeConfig = this.configurations[0]; + } + console.log('active config', this.configurations, this.activeConfig) } catch (error) { console.error("Error fetching the projects data:", error); } } - async saveCustomConfig(content: customScriptRunnerConfig) { + async saveCustomConfig(content: ScriptRunnerConfig) { console.log('saveCustomConfig', content) await this.plugin.call('fileManager', 'writeFile', 'script.config.json', JSON.stringify(content, null, 2)) } @@ -244,9 +342,23 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { console.log('activateCustomScriptRunner', config) // post config to localhost:4000 using axios try { - const result = await axios.post('http://localhost:4000/build', config) + const result = await axios.post(customBuildUrl, config) console.log(result) 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 diff --git a/apps/remix-ide/src/assets/list.json b/apps/remix-ide/src/assets/list.json index 82f8fe17bb..3d7795ee81 100644 --- a/apps/remix-ide/src/assets/list.json +++ b/apps/remix-ide/src/assets/list.json @@ -1033,9 +1033,21 @@ "urls": [ "dweb:/ipfs/QmVTALD1WUQwRvEL19jgwrEFyBJMQmy9z32zvT6TAtYPY1" ] + }, + { + "path": "soljson-v0.8.28+commit.7893614a.js", + "version": "0.8.28", + "build": "commit.7893614a", + "longVersion": "0.8.28+commit.7893614a", + "keccak256": "0x8e01bd0cafb8a8bab060453637101a88e4ab6d41c32645a26eaca541fb169c8e", + "sha256": "0x72ef580a6ec5943130028e5294313f24e9435520acc89f8c9dbfd0139d9ae146", + "urls": [ + "dweb:/ipfs/QmVtdNYdUC4aX6Uk5LrxDT55B7NgGLnLcA2wTecF5xUbSS" + ] } ], "releases": { + "0.8.28": "soljson-v0.8.28+commit.7893614a.js", "0.8.27": "soljson-v0.8.27+commit.40a35a09.js", "0.8.26": "soljson-v0.8.26+commit.8a97fa7a.js", "0.8.25": "soljson-v0.8.25+commit.b61c2a91.js", @@ -1131,5 +1143,5 @@ "0.4.0": "soljson-v0.4.0+commit.acd334c9.js", "0.3.6": "soljson-v0.3.6+commit.3fc68da5.js" }, - "latestRelease": "0.8.27" + "latestRelease": "0.8.28" } \ No newline at end of file diff --git a/libs/remix-ui/scriptrunner/src/lib/custom-script-runner.tsx b/libs/remix-ui/scriptrunner/src/lib/custom-script-runner.tsx index 51e611edc3..8c0cac1c67 100644 --- a/libs/remix-ui/scriptrunner/src/lib/custom-script-runner.tsx +++ b/libs/remix-ui/scriptrunner/src/lib/custom-script-runner.tsx @@ -13,8 +13,7 @@ export interface ScriptRunnerUIProps { publishedConfigurations: ProjectConfiguration[]; openCustomConfig: () => any; saveCustomConfig(content: customScriptRunnerConfig): void; - activateCustomScriptRunner(config: customScriptRunnerConfig): string; - addCustomConfig(config: ProjectConfiguration): void; + activateCustomScriptRunner(config: customScriptRunnerConfig): Promise; customConfig: customScriptRunnerConfig; } @@ -82,18 +81,7 @@ export const CustomScriptRunner = (props: ScriptRunnerUIProps) => { console.log(customConfig); setLoading(true); try { - const loadedConfig = await props.activateCustomScriptRunner(customConfig); - console.log(loadedConfig); - const newConfig: ProjectConfiguration = { - name: loadedConfig, - publish: true, - description: `Extension of ${baseConfig}`, - dependencies: dependencies, - replacements: {} - }; - console.log(newConfig); - props.addCustomConfig(newConfig); - + await props.activateCustomScriptRunner(customConfig); } catch (e) { console.log(e) } finally { 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 7455aa60b1..40877b1716 100644 --- a/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx +++ b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx @@ -3,55 +3,36 @@ import { Accordion, Card, Button } from "react-bootstrap"; import axios from "axios"; import { customScriptRunnerConfig, Dependency, ProjectConfiguration } from "../types"; import { FormattedMessage } from "react-intl"; -import { faCheck, faToggleOn } from "@fortawesome/free-solid-svg-icons"; +import { faCheck, faExclamationCircle, faToggleOn } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Profile } from "@remixproject/plugin-utils"; import { IframeProfile, ViewProfile } from "@remixproject/engine-web"; import { Plugin } from "@remixproject/engine"; import { CustomScriptRunner } from "./custom-script-runner"; +import { CustomTooltip } from "@remix-ui/helper"; export interface ScriptRunnerUIProps { // Add your props here - loadScriptRunner: (name: string) => void; + loadScriptRunner: (config: ProjectConfiguration) => void; // build custom script runner buildScriptRunner: (dependencies: Dependency[]) => void; openCustomConfig: () => any; saveCustomConfig(content: customScriptRunnerConfig): void; - activateCustomScriptRunner(config: customScriptRunnerConfig): string; + activateCustomScriptRunner(config: customScriptRunnerConfig): Promise; customConfig: customScriptRunnerConfig; configurations: ProjectConfiguration[]; + activeConfig: ProjectConfiguration; + enableCustomScriptRunner: boolean; } export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => { - const { loadScriptRunner, configurations } = props; + const { loadScriptRunner, configurations, activeConfig, enableCustomScriptRunner } = props; const [activeKey, setActiveKey] = useState('default'); - const [activeConfig, setActiveConfig] = useState('default'); - - useEffect(() => { - // Fetch the JSON data from the localhost server using Axios - - }, []); // Empty array ensures this effect runs once when the component mounts - - const handleSelect = (key) => { - console.log("Selected key:", key, activeKey); - setActiveConfig(key); - console.log(loadScriptRunner) - loadScriptRunner(key) - }; - - const addCustomConfig = (config: ProjectConfiguration) => { - if(configurations.find((c) => c.name === config.name)) { - return; - } - //setConfigurations([...configurations, config]); - setActiveConfig(config.name); - } if (!configurations) { return
Loading...
; } - return (
@@ -64,12 +45,24 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => { textOverflow: 'ellipsis' }} > -
{config.name}
+
{config.title || config.name}
-
handleSelect(config.name)} className="pointer px-2"> - {activeConfig !== config.name ? - : - +
+ {config.isLoading &&
+ +
} + {config.errorStatus && config.error &&
+ + + +
} + {!config.isLoading && +
loadScriptRunner(config)} className="pointer px-2"> + {activeConfig && activeConfig.name !== config.name ? + : + + } +
}
@@ -88,14 +81,14 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => {
))} - config.publish)} - buildScriptRunner={props.buildScriptRunner} /> + {enableCustomScriptRunner && + config.publish)} + buildScriptRunner={props.buildScriptRunner} />} ); }; diff --git a/libs/remix-ui/scriptrunner/src/types/index.ts b/libs/remix-ui/scriptrunner/src/types/index.ts index b474249ca6..99e453bd37 100644 --- a/libs/remix-ui/scriptrunner/src/types/index.ts +++ b/libs/remix-ui/scriptrunner/src/types/index.ts @@ -1,3 +1,5 @@ +import { defaultConfig } from "@web3modal/ethers5/react"; + export interface Dependency { version: string; name: string; @@ -17,9 +19,19 @@ export interface Dependency { description: string; dependencies: Dependency[]; replacements: Replacements; + title: string; + errorStatus: boolean; + error: string; + isLoading: boolean; } export interface customScriptRunnerConfig { baseConfiguration: string; dependencies: Dependency[]; - } \ No newline at end of file + } + + export interface ScriptRunnerConfig { + defaultConfig: string, + customConfig: customScriptRunnerConfig + } +