pull/5323/head
bunsenstraat 1 month ago
parent 54f82017c1
commit 4983d741ea
  1. 208
      apps/remix-ide/src/app/tabs/script-runner-ui.tsx
  2. 14
      apps/remix-ide/src/assets/list.json
  3. 16
      libs/remix-ui/scriptrunner/src/lib/custom-script-runner.tsx
  4. 71
      libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx
  5. 12
      libs/remix-ui/scriptrunner/src/types/index.ts

@ -1,7 +1,7 @@
import { IframePlugin, IframeProfile, ViewPlugin } from '@remixproject/engine-web' import { IframePlugin, IframeProfile, ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line 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 { Profile } from '@remixproject/plugin-utils'
import { Engine, Plugin } from '@remixproject/engine' import { Engine, Plugin } from '@remixproject/engine'
import axios from 'axios' import axios from 'axios'
@ -25,50 +25,62 @@ const profile = {
const configFileName = 'script.config.json' 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 { export class ScriptRunnerUIPlugin extends ViewPlugin {
engine: Engine engine: Engine
current: string
currentTemplate: string
dispatch: React.Dispatch<any> = () => { } dispatch: React.Dispatch<any> = () => { }
workspaceScriptRunnerDefaults: Record<string, string> workspaceScriptRunnerDefaults: Record<string, string>
customConfig: customScriptRunnerConfig customConfig: ScriptRunnerConfig
configurations: ProjectConfiguration[] configurations: ProjectConfiguration[]
activeConfig: ProjectConfiguration
enableCustomScriptRunner: boolean
plugin: Plugin<any, CustomRemixApi> plugin: Plugin<any, CustomRemixApi>
scriptRunnerProfileName: string
constructor(engine: Engine) { constructor(engine: Engine) {
super(profile) super(profile)
console.log('ScriptRunnerUIPlugin', this) console.log('ScriptRunnerUIPlugin', this)
this.engine = engine this.engine = engine
this.workspaceScriptRunnerDefaults = {} this.workspaceScriptRunnerDefaults = {}
this.plugin = this this.plugin = this
this.enableCustomScriptRunner = false
} }
async onActivation() { async onActivation() {
console.log('onActivation', this)
console.log('onActivation', this.customConfig)
this.on('filePanel', 'setWorkspace', async (workspace: string) => { this.on('filePanel', 'setWorkspace', async (workspace: string) => {
console.log('setWorkspace', workspace, this) console.log('setWorkspace', workspace)
this.customConfig = { this.activeConfig = null
baseConfiguration: 'default', this.customConfig =
dependencies: [] {
defaultConfig: 'default',
customConfig: {
baseConfiguration: 'default',
dependencies: []
}
} }
await this.loadCustomConfig() await this.loadCustomConfig()
this.loadConfigurations() await this.loadConfigurations()
this.renderComponent() this.renderComponent()
console.log('setWorkspace', this.customConfig)
}) })
this.plugin.on('fileManager','fileSaved', async (file: string) =>{ this.plugin.on('fileManager', 'fileSaved', async (file: string) => {
console.log(file)
if(file === configFileName) { if (file === configFileName && this.enableCustomScriptRunner) {
await this.loadCustomConfig() await this.loadCustomConfig()
this.renderComponent() this.renderComponent()
} }
}) })
await this.loadCustomConfig() await this.loadCustomConfig()
this.loadConfigurations() await this.loadConfigurations()
this.renderComponent() this.renderComponent()
} }
@ -89,69 +101,137 @@ export class ScriptRunnerUIPlugin extends ViewPlugin {
this.dispatch({ this.dispatch({
customConfig: this.customConfig, customConfig: this.customConfig,
configurations: this.configurations, configurations: this.configurations,
activeConfig: this.activeConfig,
enableCustomScriptRunner: this.enableCustomScriptRunner
}) })
} }
updateComponent(state: any) { updateComponent(state: IScriptRunnerState) {
console.log('updateComponent', state)
return ( return (
<ScriptRunnerUI <ScriptRunnerUI
customConfig={state.customConfig} customConfig={state.customConfig}
configurations={state.configurations} configurations={state.configurations}
activeConfig={state.activeConfig}
enableCustomScriptRunner={state.enableCustomScriptRunner}
activateCustomScriptRunner={this.activateCustomScriptRunner.bind(this)} activateCustomScriptRunner={this.activateCustomScriptRunner.bind(this)}
saveCustomConfig={this.saveCustomConfig.bind(this)} saveCustomConfig={this.saveCustomConfig.bind(this)}
openCustomConfig={this.openCustomConfig.bind(this)} openCustomConfig={this.openCustomConfig.bind(this)}
buildScriptRunner={this.buildScriptRunner.bind(this)} buildScriptRunner={this.buildScriptRunner.bind(this)}
loadScriptRunner={this.loadScriptRunner.bind(this)} /> loadScriptRunner={this.selectScriptRunner.bind(this)} />
) )
} }
async loadScriptRunner(name: string) { async selectScriptRunner(config: ProjectConfiguration) {
console.log('loadScriptRunner', name) console.log('selectScriptRunner', config)
await this.loadScriptRunner(config)
await this.saveCustomConfig(this.customConfig)
}
async loadScriptRunner(config: ProjectConfiguration): Promise<boolean> {
console.log('loadScriptRunner', config)
const profile: Profile = await this.plugin.call('manager', 'getProfile', 'scriptRunner') const profile: Profile = await this.plugin.call('manager', 'getProfile', 'scriptRunner')
this.scriptRunnerProfileName = profile.name
const testPluginName = localStorage.getItem('test-plugin-name') const testPluginName = localStorage.getItem('test-plugin-name')
const testPluginUrl = localStorage.getItem('test-plugin-url') const testPluginUrl = localStorage.getItem('test-plugin-url')
let baseUrl = 'http://localhost:3000'
let url = `${baseUrl}?template=${name}&timestamp=${Date.now()}` let url = `${baseUrl}?template=${config.name}&timestamp=${Date.now()}`
if (testPluginName === 'scriptRunner') { if (testPluginName === 'scriptRunner') {
// if testpluginurl has template specified only use that // if testpluginurl has template specified only use that
if (testPluginUrl.indexOf('template') > -1) { if (testPluginUrl.indexOf('template') > -1) {
url = testPluginUrl url = testPluginUrl
} else { } else {
baseUrl = `//${new URL(testPluginUrl).host}` baseUrl = `//${new URL(testPluginUrl).host}`
url = `${baseUrl}?template=${name}&timestamp=${Date.now()}` url = `${baseUrl}?template=${config.name}&timestamp=${Date.now()}`
} }
} }
console.log('loadScriptRunner', profile)
const newProfile: IframeProfile = { const newProfile: IframeProfile = {
...profile, ...profile,
name: profile.name + name, name: profile.name + config.name,
location: 'hidden', location: 'hiddenPanel',
url: url url: url
} }
console.log('loadScriptRunner', newProfile) console.log('loadScriptRunner', newProfile)
let result = null
try { try {
this.setIsLoading(config.name, true)
const plugin: IframePlugin = new IframePlugin(newProfile) 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) 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, 'log', this.log.bind(this))
this.on(newProfile.name, 'info', this.info.bind(this)) this.on(newProfile.name, 'info', this.info.bind(this))
this.on(newProfile.name, 'warn', this.warn.bind(this)) this.on(newProfile.name, 'warn', this.warn.bind(this))
this.on(newProfile.name, 'error', this.error.bind(this)) this.on(newProfile.name, 'error', this.error.bind(this))
this.on(newProfile.name, 'dependencyError', this.dependencyError.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) { } catch (e) {
this.current = newProfile.name
this.currentTemplate = name this.engine.remove(newProfile.name)
console.log('Already loaded') 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) { async execute(script: string, filePath: string) {
if (!this.current) await this.loadScriptRunner('default') console.log('is registered', `${this.scriptRunnerProfileName}${this.activeConfig.name}`, this.engine.isRegistered(`${this.scriptRunnerProfileName}${this.activeConfig.name}`))
console.log('execute', this.current) if (!this.scriptRunnerProfileName || !this.engine.isRegistered(`${this.scriptRunnerProfileName}${this.activeConfig.name}`)) {
await this.call(this.current, 'execute', script, filePath) 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) { async dependencyError(data: any) {
@ -202,40 +282,58 @@ export class ScriptRunnerUIPlugin extends ViewPlugin {
console.log('buildScriptRunner', dependencies) console.log('buildScriptRunner', dependencies)
} }
async loadCustomConfig(): Promise<customScriptRunnerConfig> { async loadCustomConfig(): Promise<ScriptRunnerConfig> {
console.log('loadCustomConfig') console.log('loadCustomConfig')
//await this.plugin.call('fileManager', 'open', 'script.config.json') //await this.plugin.call('fileManager', 'open', 'script.config.json')
try { try {
const content = await this.plugin.call('fileManager', 'readFile', configFileName) const content = await this.plugin.call('fileManager', 'readFile', configFileName)
console.log('loadCustomConfig', content)
const parsed = JSON.parse(content) const parsed = JSON.parse(content)
this.customConfig = parsed this.customConfig = parsed
console.log('loadCustomConfig', this.customConfig)
} catch (e) { } catch (e) {
return { return {
baseConfiguration: 'default', defaultConfig: 'default',
dependencies: [] customConfig: {
baseConfiguration: 'default',
dependencies: []
}
} }
} }
} }
async openCustomConfig() { async openCustomConfig() {
try { try {
await this.plugin.call('fileManager', 'open', 'script.config.json') await this.plugin.call('fileManager', 'open', 'script.config.json')
}catch(e){ } catch (e) {
} }
} }
async loadConfigurations() { async loadConfigurations() {
try { 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; 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) { } catch (error) {
console.error("Error fetching the projects data:", error); console.error("Error fetching the projects data:", error);
} }
} }
async saveCustomConfig(content: customScriptRunnerConfig) { async saveCustomConfig(content: ScriptRunnerConfig) {
console.log('saveCustomConfig', content) console.log('saveCustomConfig', content)
await this.plugin.call('fileManager', 'writeFile', 'script.config.json', JSON.stringify(content, null, 2)) 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) console.log('activateCustomScriptRunner', config)
// post config to localhost:4000 using axios // post config to localhost:4000 using axios
try { try {
const result = await axios.post('http://localhost:4000/build', config) const result = await axios.post(customBuildUrl, config)
console.log(result) console.log(result)
if (result.data.hash) { 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) await this.loadScriptRunner(result.data.hash)
} }
return result.data.hash return result.data.hash

@ -1033,9 +1033,21 @@
"urls": [ "urls": [
"dweb:/ipfs/QmVTALD1WUQwRvEL19jgwrEFyBJMQmy9z32zvT6TAtYPY1" "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": { "releases": {
"0.8.28": "soljson-v0.8.28+commit.7893614a.js",
"0.8.27": "soljson-v0.8.27+commit.40a35a09.js", "0.8.27": "soljson-v0.8.27+commit.40a35a09.js",
"0.8.26": "soljson-v0.8.26+commit.8a97fa7a.js", "0.8.26": "soljson-v0.8.26+commit.8a97fa7a.js",
"0.8.25": "soljson-v0.8.25+commit.b61c2a91.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.4.0": "soljson-v0.4.0+commit.acd334c9.js",
"0.3.6": "soljson-v0.3.6+commit.3fc68da5.js" "0.3.6": "soljson-v0.3.6+commit.3fc68da5.js"
}, },
"latestRelease": "0.8.27" "latestRelease": "0.8.28"
} }

@ -13,8 +13,7 @@ export interface ScriptRunnerUIProps {
publishedConfigurations: ProjectConfiguration[]; publishedConfigurations: ProjectConfiguration[];
openCustomConfig: () => any; openCustomConfig: () => any;
saveCustomConfig(content: customScriptRunnerConfig): void; saveCustomConfig(content: customScriptRunnerConfig): void;
activateCustomScriptRunner(config: customScriptRunnerConfig): string; activateCustomScriptRunner(config: customScriptRunnerConfig): Promise<string>;
addCustomConfig(config: ProjectConfiguration): void;
customConfig: customScriptRunnerConfig; customConfig: customScriptRunnerConfig;
} }
@ -82,18 +81,7 @@ export const CustomScriptRunner = (props: ScriptRunnerUIProps) => {
console.log(customConfig); console.log(customConfig);
setLoading(true); setLoading(true);
try { try {
const loadedConfig = await props.activateCustomScriptRunner(customConfig); 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);
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} finally { } finally {

@ -3,55 +3,36 @@ import { Accordion, Card, Button } from "react-bootstrap";
import axios from "axios"; import axios from "axios";
import { customScriptRunnerConfig, Dependency, ProjectConfiguration } from "../types"; import { customScriptRunnerConfig, Dependency, ProjectConfiguration } from "../types";
import { FormattedMessage } from "react-intl"; 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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Profile } from "@remixproject/plugin-utils"; import { Profile } from "@remixproject/plugin-utils";
import { IframeProfile, ViewProfile } from "@remixproject/engine-web"; import { IframeProfile, ViewProfile } from "@remixproject/engine-web";
import { Plugin } from "@remixproject/engine"; import { Plugin } from "@remixproject/engine";
import { CustomScriptRunner } from "./custom-script-runner"; import { CustomScriptRunner } from "./custom-script-runner";
import { CustomTooltip } from "@remix-ui/helper";
export interface ScriptRunnerUIProps { export interface ScriptRunnerUIProps {
// Add your props here // Add your props here
loadScriptRunner: (name: string) => void; loadScriptRunner: (config: ProjectConfiguration) => void;
// build custom script runner // build custom script runner
buildScriptRunner: (dependencies: Dependency[]) => void; buildScriptRunner: (dependencies: Dependency[]) => void;
openCustomConfig: () => any; openCustomConfig: () => any;
saveCustomConfig(content: customScriptRunnerConfig): void; saveCustomConfig(content: customScriptRunnerConfig): void;
activateCustomScriptRunner(config: customScriptRunnerConfig): string; activateCustomScriptRunner(config: customScriptRunnerConfig): Promise<string>;
customConfig: customScriptRunnerConfig; customConfig: customScriptRunnerConfig;
configurations: ProjectConfiguration[]; configurations: ProjectConfiguration[];
activeConfig: ProjectConfiguration;
enableCustomScriptRunner: boolean;
} }
export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => { export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => {
const { loadScriptRunner, configurations } = props; const { loadScriptRunner, configurations, activeConfig, enableCustomScriptRunner } = props;
const [activeKey, setActiveKey] = useState('default'); 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) { if (!configurations) {
return <div>Loading...</div>; return <div>Loading...</div>;
} }
return ( return (
<div className="px-1"> <div className="px-1">
<Accordion defaultActiveKey="default"> <Accordion defaultActiveKey="default">
@ -64,12 +45,24 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => {
textOverflow: 'ellipsis' textOverflow: 'ellipsis'
}} }}
> >
<div className="pl-2">{config.name}</div> <div className="pl-2">{config.title || config.name}</div>
</Accordion.Toggle> </Accordion.Toggle>
<div onClick={() => handleSelect(config.name)} className="pointer px-2"> <div className="d-flex align-items-baseline">
{activeConfig !== config.name ? {config.isLoading && <div className="">
<FontAwesomeIcon icon={faToggleOn}></FontAwesomeIcon> : <i className="fas fa-spinner fa-spin mr-1"></i>
<FontAwesomeIcon className="text-success" icon={faCheck}></FontAwesomeIcon> </div>}
{config.errorStatus && config.error && <div className="text-danger">
<CustomTooltip tooltipText={config.error}>
<FontAwesomeIcon icon={faExclamationCircle}></FontAwesomeIcon>
</CustomTooltip>
</div>}
{!config.isLoading &&
<div onClick={() => loadScriptRunner(config)} className="pointer px-2">
{activeConfig && activeConfig.name !== config.name ?
<FontAwesomeIcon icon={faToggleOn}></FontAwesomeIcon> :
<FontAwesomeIcon className="text-success" icon={faCheck}></FontAwesomeIcon>
}
</div>
} }
</div> </div>
</div> </div>
@ -88,14 +81,14 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => {
</ul></> </ul></>
</Accordion.Collapse></div>))} </Accordion.Collapse></div>))}
</Accordion> </Accordion>
<CustomScriptRunner {enableCustomScriptRunner &&
customConfig={props.customConfig} <CustomScriptRunner
addCustomConfig={addCustomConfig} customConfig={props.customConfig}
activateCustomScriptRunner={props.activateCustomScriptRunner} activateCustomScriptRunner={props.activateCustomScriptRunner}
saveCustomConfig={props.saveCustomConfig} saveCustomConfig={props.saveCustomConfig}
openCustomConfig={props.openCustomConfig} openCustomConfig={props.openCustomConfig}
publishedConfigurations={configurations.filter((config) => config.publish)} publishedConfigurations={configurations.filter((config) => config.publish)}
buildScriptRunner={props.buildScriptRunner} /> buildScriptRunner={props.buildScriptRunner} />}
</div> </div>
); );
}; };

@ -1,3 +1,5 @@
import { defaultConfig } from "@web3modal/ethers5/react";
export interface Dependency { export interface Dependency {
version: string; version: string;
name: string; name: string;
@ -17,9 +19,19 @@ export interface Dependency {
description: string; description: string;
dependencies: Dependency[]; dependencies: Dependency[];
replacements: Replacements; replacements: Replacements;
title: string;
errorStatus: boolean;
error: string;
isLoading: boolean;
} }
export interface customScriptRunnerConfig { export interface customScriptRunnerConfig {
baseConfiguration: string; baseConfiguration: string;
dependencies: Dependency[]; dependencies: Dependency[];
} }
export interface ScriptRunnerConfig {
defaultConfig: string,
customConfig: customScriptRunnerConfig
}

Loading…
Cancel
Save