Merge branch 'master' into workspace_completion

pull/5714/head
Aniket 1 week ago committed by GitHub
commit 24c93fd691
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      apps/circuit-compiler/src/app/actions/index.ts
  2. 2
      apps/remix-ide-e2e/src/tests/remixd.test.ts
  3. 33
      apps/remix-ide-e2e/src/tests/script-runner.test.ts
  4. 9
      apps/remix-ide/src/app.ts
  5. 2
      apps/remix-ide/src/app/components/plugin-manager-component.js
  6. 4
      apps/remix-ide/src/app/panels/tab-proxy.js
  7. 359
      apps/remix-ide/src/app/plugins/script-runner-bridge.tsx
  8. 34
      apps/remix-ide/src/app/tabs/locales/en/git.json
  9. 377
      apps/remix-ide/src/app/tabs/script-runner-ui.tsx
  10. 2
      apps/remixdesktop/test/tests/app/foundry.test.ts
  11. 35
      apps/remixdesktop/test/tests/app/github_3.test.ts
  12. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  13. 6
      libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx
  14. 6
      libs/remix-ui/panel/src/lib/plugins/panel-header.tsx
  15. 6
      libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx
  16. 6
      libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx
  17. 2
      libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx
  18. 7
      libs/remix-ui/scriptrunner/src/types/index.ts
  19. 3
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx

@ -117,7 +117,7 @@ export const generateProof = async (plugin: CircomPluginClient, appState: AppSta
const r1csBuffer = await plugin.call('fileManager', 'readFile', r1csPath, { encoding: null })
// @ts-ignore
const r1cs = new Uint8Array(r1csBuffer)
const wtnsPath = isElectron() ? extractParentFromKey(appState.filePath) + "/.bin/" + fileName.replace('.circom', '_js') + "/" + fileName.replace('.circom', '.wtn') : r1csPath.replace('.r1cs', '.wtn')
const wtnsPath = extractParentFromKey(appState.filePath) + "/.bin/" + fileName.replace('.circom', '_js') + "/" + fileName.replace('.circom', '.wtn')
// @ts-ignore
const wtnsBuffer = await plugin.call('fileManager', 'readFile', wtnsPath, { encoding: null })
// @ts-ignore

@ -527,7 +527,7 @@ async function installFoundry(): Promise<void> {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes("foundryup: use - chisel 0.3.0")
data.toString().includes("foundryup: use - chisel 1.0.0-stable")
) {
console.log('resolving')
resolve()

@ -2,14 +2,22 @@
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
module.exports = {
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'Should activate plugins': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.waitForElementVisible('*[plugin="pluginManager"]')
.click('*[plugin="pluginManager"]')
.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentPluginManager"]')
.scrollAndClick('*[data-id="pluginManagerComponentActivateButtonUIScriptRunner"]')
},
'Should load default script runner': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('scriptRunnerBridge')
.waitForElementVisible('[data-id="sr-loaded-default"]')
.waitForElementVisible('[data-id="dependency-ethers-^5"]')
.waitForElementVisible('[data-id="sr-toggle-ethers6"]')
@ -57,21 +65,36 @@ module.exports = {
.waitForElementPresent('*[data-id="create-semaphore"]')
.scrollAndClick('*[data-id="create-semaphore"]')
.modalFooterOKClick('TemplatesSelection')
.clickLaunchIcon('scriptRunnerBridge')
.waitForElementVisible({
locateStrategy: 'xpath',
selector: "//li[@data-id='UIScriptRunner' and @role='tab']"
})
.click({
locateStrategy: 'xpath',
selector: "//li[@data-id='UIScriptRunner' and @role='tab']"
})
.waitForElementVisible('[data-id="sr-loaded-default"]')
.waitForElementVisible('[data-id="dependency-ethers-^5"]')
.waitForElementVisible('[data-id="sr-toggle-ethers6"]')
},
'switch to default workspace that should be on ethers6': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.switchWorkspace('default_workspace')
.clickLaunchIcon('scriptRunnerBridge')
.waitForElementVisible({
locateStrategy: 'xpath',
selector: "//li[@data-id='UIScriptRunner' and @role='tab']"
})
.click({
locateStrategy: 'xpath',
selector: "//li[@data-id='UIScriptRunner' and @role='tab']"
})
.waitForElementVisible('[data-id="sr-loaded-ethers6"]')
.waitForElementPresent('[data-id="dependency-ethers-^6"]')
}
}
module.exports = tests
const deployWithEthersJs = `
import { ethers } from 'ethers'

@ -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')

@ -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()
})

@ -164,7 +164,9 @@ export default class TabProxy extends Plugin {
})
this.on('manager', 'pluginActivated', ({ name, location, displayName, icon, description }) => {
if (location === 'mainPanel') {
console.log('pluginActivated', name, location, displayName, icon, description)
this.addTab(
name,
displayName,
@ -201,6 +203,7 @@ export default class TabProxy extends Plugin {
}
focus (name) {
console.log('focus', name)
this.emit('switchApp', name)
this.tabsApi.activateTab(name)
}
@ -324,6 +327,7 @@ export default class TabProxy extends Plugin {
removeTab (name, currentFileTab) {
delete this._handlers[name]
let previous = currentFileTab
if(!this.loadedTabs.find(tab => tab.name === name)) return // prevent removing tab that doesn't exist
this.loadedTabs = this.loadedTabs.filter((tab, index) => {
if (!previous && tab.name === name) {
if(index - 1 >= 0 && this.loadedTabs[index - 1])

@ -0,0 +1,359 @@
import { IframePlugin, IframeProfile, ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { customScriptRunnerConfig, IScriptRunnerState, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner'
import { Profile } from '@remixproject/plugin-utils'
import { Engine, Plugin } from '@remixproject/engine'
import axios from 'axios'
import { AppModal } from '@remix-ui/app'
import { isArray } from 'lodash'
import { PluginViewWrapper } from '@remix-ui/helper'
import { CustomRemixApi } from '@remix-api'
import { ScriptRunnerUIPlugin } from '../tabs/script-runner-ui'
const profile = {
name: 'scriptRunnerBridge',
displayName: 'Script configuration',
methods: ['execute'],
events: ['log', 'info', 'warn', 'error'],
icon: 'assets/img/solid-gear-circle-play.svg',
description: 'Configure the dependencies for running scripts.',
kind: '',
version: packageJson.version,
maintainedBy: 'Remix',
}
const configFileName = '.remix/script.config.json'
let baseUrl = 'https://remix-project-org.github.io/script-runner-generator'
const customBuildUrl = 'http://localhost:4000/build' // this will be used when the server is ready
export class ScriptRunnerBridgePlugin extends Plugin {
engine: Engine
dispatch: React.Dispatch<any> = () => {}
workspaceScriptRunnerDefaults: Record<string, string>
customConfig: ScriptRunnerConfig
configurations: ProjectConfiguration[]
activeConfig: ProjectConfiguration
enableCustomScriptRunner: boolean
plugin: Plugin<any, CustomRemixApi>
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<boolean> {
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}&timestamp=${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}&timestamp=${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<void> {
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)
}
}
}
}

@ -1,21 +1,21 @@
{
"git.push": "push",
"git.pull": "pull",
"git.commit": "commit",
"git.sync": "sync",
"git.syncchanges": "sync changes",
"git.publish": "publish",
"git.ignore": "ignore",
"git.createBranch": "create branch",
"git.deleteBranch": "delete branch",
"git.mergeBranch": "merge branch",
"git.rebaseBranch": "rebase branch",
"git.checkout": "checkout",
"git.fetch": "fetch",
"git.refresh": "refresh",
"git.unstageall": "unstage all",
"git.stageall": "stage all",
"git.noremote": "this repo has no remotes",
"git.push": "Push",
"git.pull": "Pull",
"git.commit": "Commit",
"git.sync": "Sync",
"git.syncchanges": "Sync changes",
"git.publish": "Publish",
"git.ignore": "Ignore",
"git.createBranch": "Create branch",
"git.deleteBranch": "Delete branch",
"git.mergeBranch": "Merge branch",
"git.rebaseBranch": "Rebase branch",
"git.checkout": "Checkout",
"git.fetch": "Fetch",
"git.refresh": "Refresh",
"git.unstageall": "Unstage all",
"git.stageall": "Stage all",
"git.noremote": "This repo has no remotes",
"git.init": "Initialize repository",
"git.setup": "Setup git"
}

@ -1,83 +1,36 @@
import { IframePlugin, IframeProfile, ViewPlugin } from '@remixproject/engine-web'
import { ViewPlugin, ViewProfile } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { customScriptRunnerConfig, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner' // eslint-disable-line
import { Profile } from '@remixproject/plugin-utils'
import { Engine, Plugin } from '@remixproject/engine'
import axios from 'axios'
import { AppModal } from '@remix-ui/app'
import { isArray } from 'lodash'
import { customScriptRunnerConfig, IScriptRunnerState, ProjectConfiguration, ScriptRunnerConfig, ScriptRunnerUI } from '@remix-scriptrunner'
import { PluginViewWrapper } from '@remix-ui/helper'
import { CustomRemixApi } from '@remix-api'
import { ScriptRunnerBridgePlugin } from '../plugins/script-runner-bridge'
const profile = {
name: 'scriptRunnerBridge',
const profile: ViewProfile = {
name: 'UIScriptRunner',
displayName: 'Script configuration',
methods: ['execute'],
events: ['log', 'info', 'warn', 'error'],
methods: [],
events: [],
icon: 'assets/img/solid-gear-circle-play.svg',
description: 'Configure the dependencies for running scripts.',
kind: '',
location: 'sidePanel',
location: 'mainPanel',
version: packageJson.version,
maintainedBy: 'Remix'
}
const configFileName = '.remix/script.config.json'
let baseUrl = 'https://remix-project-org.github.io/script-runner-generator'
const customBuildUrl = 'http://localhost:4000/build' // this will be used when the server is ready
interface IScriptRunnerState {
customConfig: customScriptRunnerConfig
configurations: ProjectConfiguration[]
activeConfig: ProjectConfiguration
enableCustomScriptRunner: boolean
maintainedBy: 'Remix',
}
export class ScriptRunnerUIPlugin extends ViewPlugin {
engine: Engine
dispatch: React.Dispatch<any> = () => { }
workspaceScriptRunnerDefaults: Record<string, string>
customConfig: ScriptRunnerConfig
configurations: ProjectConfiguration[]
activeConfig: ProjectConfiguration
enableCustomScriptRunner: boolean
plugin: Plugin<any, CustomRemixApi>
scriptRunnerProfileName: string
constructor(engine: Engine) {
dispatch: React.Dispatch<IScriptRunnerState> = () => {}
state: IScriptRunnerState
bridge: ScriptRunnerBridgePlugin
constructor(plugin: ScriptRunnerBridgePlugin) {
super(profile)
this.engine = engine
this.workspaceScriptRunnerDefaults = {}
this.plugin = this
this.enableCustomScriptRunner = false // implement this later
this.bridge = plugin
}
async onActivation() {
this.on('filePanel', 'setWorkspace', async (workspace: string) => {
this.activeConfig = null
this.customConfig =
{
defaultConfig: 'default',
customConfig: {
baseConfiguration: 'default',
dependencies: []
}
}
await this.loadCustomConfig()
await this.loadConfigurations()
this.on('scriptRunnerBridge', 'render', () => {
this.renderComponent()
})
this.plugin.on('fileManager', 'fileSaved', async (file: string) => {
if (file === configFileName && this.enableCustomScriptRunner) {
await this.loadCustomConfig()
this.renderComponent()
}
})
await this.loadCustomConfig()
await this.loadConfigurations()
this.renderComponent()
}
@ -89,309 +42,37 @@ export class ScriptRunnerUIPlugin extends ViewPlugin {
)
}
setDispatch(dispatch: React.Dispatch<any>) {
setDispatch(dispatch: React.Dispatch<IScriptRunnerState>) {
this.dispatch = dispatch
this.renderComponent()
}
renderComponent() {
this.dispatch({
customConfig: this.customConfig,
configurations: this.configurations,
activeConfig: this.activeConfig,
enableCustomScriptRunner: this.enableCustomScriptRunner
customConfig: this.bridge.customConfig.customConfig,
configurations: this.bridge.configurations,
activeConfig: this.bridge.activeConfig,
enableCustomScriptRunner: this.bridge.enableCustomScriptRunner,
})
}
updateComponent(state: IScriptRunnerState) {
return (
<ScriptRunnerUI
customConfig={state.customConfig}
configurations={state.configurations}
activeConfig={state.activeConfig}
enableCustomScriptRunner={state.enableCustomScriptRunner}
activateCustomScriptRunner={this.activateCustomScriptRunner.bind(this)}
saveCustomConfig={this.saveCustomConfig.bind(this)}
openCustomConfig={this.openCustomConfig.bind(this)}
loadScriptRunner={this.selectScriptRunner.bind(this)}
/>
)
}
async selectScriptRunner(config: ProjectConfiguration) {
if (await this.loadScriptRunner(config))
await this.saveCustomConfig(this.customConfig)
}
async loadScriptRunner(config: ProjectConfiguration): Promise<boolean> {
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}&timestamp=${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}&timestamp=${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
}
})
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<void> {
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 activateCustomScriptRunner(config: customScriptRunnerConfig) {
this.bridge.activateCustomScriptRunner(config)
}
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))
this.bridge.saveCustomConfig(content)
}
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)
async openCustomConfig() {
this.bridge.openCustomConfig()
}
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)
async selectScriptRunner(config: ProjectConfiguration) {
this.bridge.selectScriptRunner(config)
}
updateComponent(state: IScriptRunnerState) {
return <ScriptRunnerUI customConfig={state.customConfig} configurations={state.configurations} activeConfig={state.activeConfig} enableCustomScriptRunner={state.enableCustomScriptRunner} activateCustomScriptRunner={this.activateCustomScriptRunner.bind(this)} saveCustomConfig={this.saveCustomConfig.bind(this)} openCustomConfig={this.openCustomConfig.bind(this)} loadScriptRunner={this.selectScriptRunner.bind(this)} />
}
}
}

@ -89,7 +89,7 @@ async function installFoundry(): Promise<void> {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes("foundryup: use - chisel 0.3.0")
data.toString().includes("foundryup: use - chisel 1.0.0-stable")
) {
console.log('resolving')
resolve()

@ -131,7 +131,7 @@ const tests = {
.elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) {
console.log('Number of commit-summary elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length > commitCount)
})
}).pause(10000)
},
'load more branches from remote #group3': function (browser: NightwatchBrowser) {
@ -155,15 +155,34 @@ const tests = {
if (useIsoGit) {
browser.waitForElementVisible('*[data-id="remote-sync-origin"]')
const branchSelector = '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]';
browser
.waitForElementVisible('*[data-id="remote-sync-origin"]')
.click('*[data-id="remote-sync-origin"]')
.waitForElementVisible('*[data-id="loader-indicator"]')
.waitForElementNotPresent('*[data-id="loader-indicator"]')
.pause(2000)
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) {
console.log('Number of branches elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length > branchCount)
})
browser.perform(function (done) {
function checkElements() {
browser.execute(
function (xpath) {
return document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength;
},
[branchSelector],
function (result) {
if ((result.value as number) > 1) {
console.log('Number of loaded branches elements:', result.value);
done();
} else {
browser.pause(1000); // Wait and check again
checkElements();
}
}
);
}
checkElements();
});
} else {
browser.waitForElementVisible('*[data-id="show-more-branches-on-remote"]')
.click('*[data-id="show-more-branches-on-remote"]')

@ -46,7 +46,7 @@ const iconButtons: HometabIconSection[] = [
{
textToolip: <FormattedMessage id="home.joinUsOnDiscord" />,
matomoTrackingEntry: ['trackEvent', 'hometab', 'socialmedia', 'discord'],
urlLink: 'https://discord.com/invite/nfv6ZYjAeP',
urlLink: 'https://discord.gg/zUNteAzJs3',
iconClass: 'fa-discord',
placement: 'top'
}

@ -32,14 +32,14 @@ function PluginButton({ imgPath, envID, envText, callback, l2, description, main
{l2 && <label className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_l2Label">L2</label>}
{ maintainedBy?.toLowerCase() === 'remix' ? (
<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-by-remix" tooltipText={<FormattedMessage id="home.maintainedByRemix" />}>
<i className="bg-light text-success mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel fas fa-check"></i>
<i className="bg-light text-success mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel fa-solid fa-shield-halved"></i>
</CustomTooltip>) :
maintainedBy ?
(<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-external" tooltipText={"Maintained by " + maintainedBy}>
<i aria-hidden="true" className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
: (<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-external" tooltipText={<FormattedMessage id="panel.maintainedExternally" />}>
<i aria-hidden="true" className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
}
</div>

@ -50,14 +50,14 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
<div className="d-flex flex-row">
{ plugin?.profile?.maintainedBy?.toLowerCase() === 'remix' ? (
<CustomTooltip placement="auto" tooltipId="maintainedByTooltipRemix" tooltipText={<FormattedMessage id="home.maintainedByRemix" />}>
<i className="text-success mt-1 px-1 fas fa-check"></i>
<i className="text-success mt-1 px-1 fa-solid fa-shield-halved"></i>
</CustomTooltip>) :
plugin?.profile?.maintainedBy ?
(<CustomTooltip placement="auto" tooltipId={"maintainedByTooltip" + plugin?.profile?.maintainedBy} tooltipText={"Maintained by " + plugin?.profile?.maintainedBy}>
<i aria-hidden="true" className="mt-1 px-1 text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="mt-1 px-1 text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
: (<CustomTooltip placement="auto" tooltipId="maintainedByTooltipRemixUnknown" tooltipText={<FormattedMessage id="panel.maintainedExternally" />}>
<i aria-hidden="true" className="mt-1 px-1 text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="mt-1 px-1 text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
}
</div>

@ -26,7 +26,7 @@ function ActivePluginCard({ profile, buttonText, deactivatePlugin }: PluginCardP
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedByRemix" />}
>
<i aria-hidden="true" className="px-1 text-success fas fa-check"></i>
<i aria-hidden="true" className="px-1 text-success fa-solid fa-shield-halved"></i>
</CustomTooltip>)
: profile?.maintainedBy ? (
<CustomTooltip
@ -35,7 +35,7 @@ function ActivePluginCard({ profile, buttonText, deactivatePlugin }: PluginCardP
tooltipClasses="text-nowrap"
tooltipText={"Maintained by " + profile?.maintainedBy}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="px-1 text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
: (<CustomTooltip
placement="right"
@ -43,7 +43,7 @@ function ActivePluginCard({ profile, buttonText, deactivatePlugin }: PluginCardP
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedExternally" />}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="px-1 text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
}
{profile.documentation && (

@ -43,7 +43,7 @@ function InactivePluginCard({ profile, buttonText, activatePlugin }: PluginCardP
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedByRemix" />}
>
<i aria-hidden="true" className="px-1 text-success fas fa-check"></i>
<i aria-hidden="true" className="px-1 text-success fa-solid fa-shield-halved"></i>
</CustomTooltip>)
: profile?.maintainedBy ? (
<CustomTooltip
@ -52,7 +52,7 @@ function InactivePluginCard({ profile, buttonText, activatePlugin }: PluginCardP
tooltipClasses="text-nowrap"
tooltipText={"Maintained by " + profile?.maintainedBy}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="px-1 text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
: (<CustomTooltip
placement="right"
@ -60,7 +60,7 @@ function InactivePluginCard({ profile, buttonText, activatePlugin }: PluginCardP
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedExternally" />}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
<i aria-hidden="true" className="px-1 text-secondary fa-solid fa-shield-halved"></i>
</CustomTooltip>)
}
{profile.documentation && (

@ -33,7 +33,7 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => {
}
return (
<div className="px-1">
<div className="px-1 pt-2">
<Accordion activeKey={activeKey} defaultActiveKey="default">
{configurations.filter((config) => config.publish).map((config: ProjectConfiguration, index) => (
<div key={index}>

@ -35,3 +35,10 @@ export interface ScriptRunnerConfig {
customConfig: customScriptRunnerConfig
}
export interface IScriptRunnerState {
customConfig: customScriptRunnerConfig
configurations: ProjectConfiguration[]
activeConfig: ProjectConfiguration
enableCustomScriptRunner: boolean
}

@ -243,7 +243,8 @@ export const TabsUI = (props: TabsUIProps) => {
data-id="script-config"
className="btn text-dark border-left ml-2 pr-0 py-0 d-flex"
onClick={async () => {
props.plugin.call('menuicons', 'select', 'scriptRunnerBridge')
await props.plugin.call('manager', 'activatePlugin', 'UIScriptRunner')
await props.plugin.call('tabs', 'focus', 'UIScriptRunner')
}}
>
<i className="fa-kit fa-solid-gear-circle-play"></i>

Loading…
Cancel
Save