add script runner bridge

pull/5167/head
bunsenstraat 2 months ago
parent 64bec7b787
commit d892a15f87
  1. 9
      apps/remix-ide/src/app.js
  2. 8
      apps/remix-ide/src/app/panels/terminal.tsx
  3. 2
      apps/remix-ide/src/app/plugins/matomo.ts
  4. 2
      apps/remix-ide/src/app/tabs/compile-and-run.ts
  5. 87
      apps/remix-ide/src/app/tabs/script-runner-ui.tsx
  6. 14
      apps/remix-ide/src/assets/list.json
  7. 10
      apps/remix-ide/src/remixAppManager.js
  8. 1
      libs/remix-ui/scriptrunner/src/index.ts
  9. 83
      libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx
  10. 20
      libs/remix-ui/scriptrunner/src/lib/types.ts
  11. 2
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  12. 9
      libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
  13. 2
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  14. 2
      libs/remix-ui/workspace/src/lib/actions/index.ts
  15. 5
      tsconfig.paths.json

@ -69,6 +69,7 @@ const remixLib = require('@remix-project/remix-lib')
import { QueryParams } from '@remix-project/remix-lib' import { QueryParams } from '@remix-project/remix-lib'
import { SearchPlugin } from './app/tabs/search' import { SearchPlugin } from './app/tabs/search'
import { ScriptRunnerUIPlugin } from './app/tabs/script-runner-ui'
import { ElectronProvider } from './app/files/electronProvider' import { ElectronProvider } from './app/files/electronProvider'
const Storage = remixLib.Storage const Storage = remixLib.Storage
@ -221,6 +222,9 @@ class AppComponent {
//----- search //----- search
const search = new SearchPlugin() const search = new SearchPlugin()
//---------------- Script Runner UI Plugin -------------------------
const scriptRunnerUI = new ScriptRunnerUIPlugin(this.engine)
//---- templates //---- templates
const templates = new TemplatesPlugin() const templates = new TemplatesPlugin()
@ -371,7 +375,8 @@ class AppComponent {
git, git,
pluginStateLogger, pluginStateLogger,
matomo, matomo,
templateSelection templateSelection,
scriptRunnerUI
]) ])
//---- fs plugin //---- fs plugin
@ -611,7 +616,7 @@ class AppComponent {
}) })
// activate solidity plugin // activate solidity plugin
this.appManager.activatePlugin(['solidity', 'udapp', 'deploy-libraries', 'link-libraries', 'openzeppelin-proxy']) this.appManager.activatePlugin(['solidity', 'udapp', 'deploy-libraries', 'link-libraries', 'openzeppelin-proxy', 'scriptRunnerBridge'])
} }
} }

@ -117,10 +117,10 @@ class Terminal extends Plugin {
} }
onDeactivation() { onDeactivation() {
this.off('scriptRunner', 'log') this.off('scriptRunnerBridge', 'log')
this.off('scriptRunner', 'info') this.off('scriptRunnerBridge', 'info')
this.off('scriptRunner', 'warn') this.off('scriptRunnerBridge', 'warn')
this.off('scriptRunner', 'error') this.off('scriptRunnerBridge', 'error')
} }
logHtml(html) { logHtml(html) {

@ -11,7 +11,7 @@ const profile = {
version: '1.0.0' version: '1.0.0'
} }
const allowedPlugins = ['LearnEth', 'etherscan', 'vyper', 'circuit-compiler', 'doc-gen', 'doc-viewer', 'solhint', 'walletconnect', 'scriptRunner', 'dgit'] const allowedPlugins = ['LearnEth', 'etherscan', 'vyper', 'circuit-compiler', 'doc-gen', 'doc-viewer', 'solhint', 'walletconnect', 'scriptRunner', 'scriptRunnerBridge', 'dgit']
export class Matomo extends Plugin { export class Matomo extends Plugin {

@ -62,7 +62,7 @@ export class CompileAndRun extends Plugin {
if (clearAllInstances) { if (clearAllInstances) {
await this.call('udapp', 'clearAllInstances') await this.call('udapp', 'clearAllInstances')
} }
await this.call('scriptRunner', 'execute', content, fileName) await this.call('scriptRunnerBridge', 'execute', content, fileName)
} catch (e) { } catch (e) {
this.call('notification', 'toast', e.message || e) this.call('notification', 'toast', e.message || e)
} }

@ -0,0 +1,87 @@
import { IframePlugin, IframeProfile, ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { ScriptRunnerUI } from '@remix-scriptrunner' // eslint-disable-line
import { Profile } from '@remixproject/plugin-utils'
import { Engine } from '@remixproject/engine'
const profile = {
name: 'scriptRunnerBridge',
displayName: 'Script Bridge',
methods: ['execute'],
events: ['log', 'info', 'warn', 'error'],
icon: 'assets/img/settings.webp',
description: 'Set up a script runner',
kind: '',
location: 'sidePanel',
version: packageJson.version,
maintainedBy: 'Remix'
}
export class ScriptRunnerUIPlugin extends ViewPlugin {
engine: Engine
current: string
constructor(engine: Engine) {
super(profile)
console.log('ScriptRunnerUIPlugin', this)
this.engine = engine
}
async onActivation () {
console.log('onActivation', this)
}
async loadScriptRunner(name: string) {
console.log('loadScriptRunner', name, this)
const profile: IframeProfile = await this.call('manager', 'getProfile', 'scriptRunner')
const newProfile: IframeProfile = {
...profile,
name: profile.name + name,
url: 'http://localhost:3000?template=' + name
}
console.log('loadScriptRunner', newProfile)
const plugin: IframePlugin = new IframePlugin(newProfile)
await this.engine.register(plugin)
await this.call('manager', 'activatePlugin', newProfile.name)
this.current = newProfile.name
this.on(newProfile.name, 'log', this.log.bind(this))
this.on(newProfile.name, 'info', this.log.bind(this))
this.on(newProfile.name, 'warn', this.log.bind(this))
this.on(newProfile.name, 'error', this.log.bind(this))
}
async execute (script: string, filePath: string) {
if(!this.current) await this.loadScriptRunner('default')
console.log('execute', script, filePath)
this.call(this.current, 'execute', script, filePath)
}
async log(data: any){
console.log('log', data)
this.emit('log', data)
}
async warn(data: any){
console.log('warn', data)
this.emit('warn', data)
}
async error(data: any){
console.log('error', data)
this.emit('error', data)
}
async info(data: any){
console.log('info', data)
this.emit('info', data)
}
render() {
return (
<div id="scriptRunnerTab">
<ScriptRunnerUI loadScriptRunner={this.loadScriptRunner.bind(this)} />
</div>
)
}
}

@ -1022,9 +1022,21 @@
"urls": [ "urls": [
"dweb:/ipfs/QmS5JdeXtYhGBvdgNTLWuBNHupyP623X4sf43fRbrgiTaA" "dweb:/ipfs/QmS5JdeXtYhGBvdgNTLWuBNHupyP623X4sf43fRbrgiTaA"
] ]
},
{
"path": "soljson-v0.8.27+commit.40a35a09.js",
"version": "0.8.27",
"build": "commit.40a35a09",
"longVersion": "0.8.27+commit.40a35a09",
"keccak256": "0x68c7a06651a847fc9a60886a6ba590a2b20d87f2d4f9570bf70fbb2b901e7713",
"sha256": "0xd91c08277f801321af4e80958015aea18b41c01d2c6a38310a23014485b0e51c",
"urls": [
"dweb:/ipfs/QmVTALD1WUQwRvEL19jgwrEFyBJMQmy9z32zvT6TAtYPY1"
]
} }
], ],
"releases": { "releases": {
"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",
"0.8.24": "soljson-v0.8.24+commit.e11b9ed9.js", "0.8.24": "soljson-v0.8.24+commit.e11b9ed9.js",
@ -1119,5 +1131,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.26" "latestRelease": "0.8.27"
} }

@ -24,6 +24,7 @@ let requiredModules = [ // services + layout views + system views
'blockchain', 'blockchain',
'web3Provider', 'web3Provider',
'scriptRunner', 'scriptRunner',
'scriptRunnerBridge',
'fetchAndCompile', 'fetchAndCompile',
'mainPanel', 'mainPanel',
'hiddenPanel', 'hiddenPanel',
@ -107,6 +108,10 @@ const isVM = (name) => {
return name.startsWith('vm') return name.startsWith('vm')
} }
const isScriptRunner = (name) => {
return name.startsWith('scriptRunner')
}
export function isNative(name) { export function isNative(name) {
// nativePlugin allows to bypass the permission request // nativePlugin allows to bypass the permission request
@ -139,7 +144,7 @@ export function isNative(name) {
'templateSelection', 'templateSelection',
'walletconnect' 'walletconnect'
] ]
return nativePlugins.includes(name) || requiredModules.includes(name) || isInjectedProvider(name) || isVM(name) return nativePlugins.includes(name) || requiredModules.includes(name) || isInjectedProvider(name) || isVM(name) || isScriptRunner(name)
} }
/** /**
@ -192,6 +197,8 @@ export class RemixAppManager extends PluginManager {
} }
} }
await this.toggleActive(name) await this.toggleActive(name)
}else{
console.log('cannot deactivate', name)
} }
} }
@ -294,6 +301,7 @@ export class RemixAppManager extends PluginManager {
return plugins.map(plugin => { return plugins.map(plugin => {
if (plugin.name === 'dgit' && Registry.getInstance().get('platform').api.isDesktop()) { plugin.url = 'https://dgit4-76cc9.web.app/' } // temporary fix if (plugin.name === 'dgit' && Registry.getInstance().get('platform').api.isDesktop()) { plugin.url = 'https://dgit4-76cc9.web.app/' } // temporary fix
if (plugin.name === testPluginName) plugin.url = testPluginUrl if (plugin.name === testPluginName) plugin.url = testPluginUrl
//console.log('plugin', plugin)
return new IframePlugin(plugin) return new IframePlugin(plugin)
}) })
} }

@ -0,0 +1 @@
export { ScriptRunnerUI } from './lib/script-runner-ui';

@ -0,0 +1,83 @@
import React, { useEffect, useState } from "react";
import { Accordion, Card, Button } from "react-bootstrap";
import axios from "axios";
import { ProjectConfiguration } from "./types";
import { FormattedMessage } from "react-intl";
import { faCheck, 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";
export interface ScriptRunnerUIProps {
// Add your props here
loadScriptRunner: (name: string) => void;
}
export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => {
const { loadScriptRunner } = props;
const [configurations, setConfigurations] = useState([]);
const [activeKey, setActiveKey] = useState('default');
const [activeConfig, setActiveConfig] = useState('default');
useEffect(() => {
// Fetch the JSON data from the localhost server using Axios
const fetchData = async () => {
try {
const response = await axios.get('http://localhost:3000/projects.json');
setConfigurations(response.data);
} catch (error) {
console.error("Error fetching the projects data:", error);
}
};
fetchData();
}, []); // 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)
};
// Filter out unpublished configurations
const publishedConfigurations = configurations.filter((config) => config.publish);
return (
<div className="px-1">
<Accordion defaultActiveKey="default">
{publishedConfigurations.map((config: ProjectConfiguration, index) => (
<div key={index}>
<div className="d-flex align-items-baseline justify-content-between">
<Accordion.Toggle as={Button} variant="link" eventKey={config.name}>
<span className="pl-2">{config.name}</span>
</Accordion.Toggle>
<div onClick={() => handleSelect(config.name)} className="pointer px-2">
{activeConfig !== config.name ?
<FontAwesomeIcon icon={faToggleOn}></FontAwesomeIcon> :
<FontAwesomeIcon className="text-success" icon={faCheck}></FontAwesomeIcon>
}
</div>
</div>
<Accordion.Collapse className="px-4" eventKey={config.name}>
<>
<p><strong>Description: </strong>{config.description}</p>
<p><strong>Dependencies:</strong></p>
<ul>
{config.dependencies.map((dep, depIndex) => (
<li key={depIndex}>
<strong>{dep.name}</strong> (v{dep.version})
</li>
))}
</ul></>
</Accordion.Collapse></div>))}
</Accordion>
</div>
);
};

@ -0,0 +1,20 @@
export interface Dependency {
version: string;
name: string;
alias?: string;
import: boolean;
require?: boolean;
windowImport?: boolean;
}
export interface Replacements {
[key: string]: string;
}
export interface ProjectConfiguration {
name: string;
publish: boolean;
description: string;
dependencies: Dependency[];
replacements: Replacements;
}

@ -209,7 +209,7 @@ export const TabsUI = (props: TabsUIProps) => {
const path = active().substr(active().indexOf('/') + 1, active().length) const path = active().substr(active().indexOf('/') + 1, active().length)
const content = await props.plugin.call('fileManager', 'readFile', path) const content = await props.plugin.call('fileManager', 'readFile', path)
if (tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') { if (tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') {
await props.plugin.call('scriptRunner', 'execute', content, path) await props.plugin.call('scriptRunnerBridge', 'execute', content, path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt]) _paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul') { } else if (tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul') {
await props.plugin.call('solidity', 'compile', path) await props.plugin.call('solidity', 'compile', path)

@ -79,28 +79,29 @@ export const filterFnAction = (name: string, filterFn, dispatch: React.Dispatch<
} }
export const registerLogScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => { export const registerLogScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => {
on('scriptRunner', commandName, (msg) => { console.log('registerLogScriptRunnerAction', commandName)
on('scriptRunnerBridge', commandName, (msg) => {
commandFn.log.apply(commandFn, msg.data) // eslint-disable-line commandFn.log.apply(commandFn, msg.data) // eslint-disable-line
dispatch({ type: commandName, payload: { commandFn, message: msg.data } }) dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
}) })
} }
export const registerInfoScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => { export const registerInfoScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => {
on('scriptRunner', commandName, (msg) => { on('scriptRunnerBridge', commandName, (msg) => {
commandFn.info.apply(commandFn, msg.data) // eslint-disable-line commandFn.info.apply(commandFn, msg.data) // eslint-disable-line
dispatch({ type: commandName, payload: { commandFn, message: msg.data } }) dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
}) })
} }
export const registerWarnScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => { export const registerWarnScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => {
on('scriptRunner', commandName, (msg) => { on('scriptRunnerBridge', commandName, (msg) => {
commandFn.warn.apply(commandFn, msg.data) // eslint-disable-line commandFn.warn.apply(commandFn, msg.data) // eslint-disable-line
dispatch({ type: commandName, payload: { commandFn, message: msg.data } }) dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
}) })
} }
export const registerErrorScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => { export const registerErrorScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch<any>) => {
on('scriptRunner', commandName, (msg) => { on('scriptRunnerBridge', commandName, (msg) => {
commandFn.error.apply(commandFn, msg.data) // eslint-disable-line commandFn.error.apply(commandFn, msg.data) // eslint-disable-line
dispatch({ type: commandName, payload: { commandFn, message: msg.data } }) dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
}) })

@ -245,7 +245,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
await call('solcoder', 'solidity_answer', script) await call('solcoder', 'solidity_answer', script)
_paq.push(['trackEvent', 'ai', 'solcoder', 'askFromTerminal']) _paq.push(['trackEvent', 'ai', 'solcoder', 'askFromTerminal'])
} else { } else {
await call('scriptRunner', 'execute', script) await call('scriptRunnerBridge', 'execute', script)
} }
done() done()
} catch (error) { } catch (error) {

@ -506,7 +506,7 @@ export const runScript = async (path: string) => {
if (error) { if (error) {
return dispatch(displayPopUp(error)) return dispatch(displayPopUp(error))
} }
plugin.call('scriptRunner', 'execute', content, path) plugin.call('scriptRunnerBridge', 'execute', content, path)
}) })
} }

@ -180,7 +180,10 @@
], ],
"@remix-api": [ "@remix-api": [
"libs/remix-api/src/index.ts" "libs/remix-api/src/index.ts"
] ],
"@remix-scriptrunner": [
"libs/remix-ui/scriptrunner/src/index.ts"
],
} }
} }
} }

Loading…
Cancel
Save