diff --git a/apps/1test/src/electron/engine.ts b/apps/1test/src/electron/engine.ts index 96cf0773d9..60d129a352 100644 --- a/apps/1test/src/electron/engine.ts +++ b/apps/1test/src/electron/engine.ts @@ -23,7 +23,7 @@ appManager.activatePlugin('xterm') ipcMain.handle('manager:activatePlugin', async (event, plugin) => { console.log('manager:activatePlugin', plugin, event.sender.id) - appManager.call(plugin, 'createClient', event.sender.id) + return await appManager.call(plugin, 'createClient', event.sender.id) }) ipcMain.handle('getWebContentsID', (event, message) => { diff --git a/apps/1test/src/electron/fsPlugin.ts b/apps/1test/src/electron/fsPlugin.ts index 73b8d4eb72..7167776ead 100644 --- a/apps/1test/src/electron/fsPlugin.ts +++ b/apps/1test/src/electron/fsPlugin.ts @@ -14,19 +14,10 @@ const profile: Profile = { export class FSPlugin extends ElectronBasePlugin { clients: FSPluginClient[] = [] constructor() { - super(profile) + super(profile, clientProfile, FSPluginClient) this.methods = [...super.methods, 'closeWatch'] } - async createClient(webContentsId: number): Promise { - this.clients.push(new FSPluginClient(webContentsId)) - } - - async closeClient(webContentsId: number): Promise { - console.log('closeClient', webContentsId) - } - - async closeWatch(): Promise { for (const client of this.clients) { await client.closeWatch() @@ -46,8 +37,8 @@ class FSPluginClient extends ElectronBasePluginClient { watcher: chokidar.FSWatcher workingDir: string = '/Volumes/bunsen/code/rmproject2/remix-project/apps/remix-ide/contracts/' - constructor(webContentsId: number) { - super(webContentsId, clientProfile) + constructor(webContentsId: number, profile: Profile) { + super(webContentsId, profile) this.onload(() => { console.log('fsPluginClient onload') }) @@ -83,7 +74,7 @@ class FSPluginClient extends ElectronBasePluginClient { } async stat(path: string): Promise { - const stat = await fs.stat(path) + const stat = await fs.stat(path) //console.log('stat', path, stat) const isDirectory = stat.isDirectory() return { diff --git a/apps/1test/src/electron/gitPlugin.ts b/apps/1test/src/electron/gitPlugin.ts index 65f635b0e8..3b40a19c28 100644 --- a/apps/1test/src/electron/gitPlugin.ts +++ b/apps/1test/src/electron/gitPlugin.ts @@ -12,18 +12,9 @@ const profile: Profile = { export class GitPlugin extends ElectronBasePlugin { client: PluginClient constructor() { - super(profile) + super(profile, clientProfile, GitPluginClient) } - async createClient(webContentsId: number): Promise { - this.clients.push(new GitPluginClient(webContentsId)) - } - - async closeClient(webContentsId: number): Promise { - console.log('closeClient', webContentsId) - } - - } const clientProfile: Profile = { @@ -35,8 +26,8 @@ const clientProfile: Profile = { class GitPluginClient extends ElectronBasePluginClient { - constructor(webContentsId: number) { - super(webContentsId, clientProfile) + constructor(webContentsId: number, profile: Profile) { + super(webContentsId, profile) this.onload(() => { console.log('GitPluginClient onload') }) diff --git a/apps/1test/src/electron/lib/electronBasePlugin.ts b/apps/1test/src/electron/lib/electronBasePlugin.ts index d0a31103d9..991e3526dc 100644 --- a/apps/1test/src/electron/lib/electronBasePlugin.ts +++ b/apps/1test/src/electron/lib/electronBasePlugin.ts @@ -5,22 +5,34 @@ import { BrowserWindow } from "electron"; import { createElectronClient } from "./electronPluginClient"; export interface ElectronBasePluginInterface { - createClient(windowId: number): Promise; - closeClient(windowId: number): Promise; + createClient(windowId: number): Promise; + closeClient(windowId: number): Promise; } export abstract class ElectronBasePlugin extends Plugin implements ElectronBasePluginInterface { clients: ElectronBasePluginClient[] = []; - constructor(profile: Profile) { + clientClass: any + clientProfile: Profile + constructor(profile: Profile, clientProfile: Profile, clientClass: any) { super(profile); this.methods = ['createClient', 'closeClient']; + this.clientClass = clientClass; + this.clientProfile = clientProfile; } - async createClient(windowId: number): Promise { - console.log('createClient method not implemented'); + async createClient(webContentsId: number): Promise { + if (this.clients.find(client => client.webContentsId === webContentsId)) return true + const client = new this.clientClass(webContentsId, this.clientProfile); + this.clients.push(client); + return new Promise((resolve, reject) => { + client.onload(() => { + resolve(true) + }) + }) } - async closeClient(windowId: number): Promise { - console.log('closeClient method not implemented'); + async closeClient(windowId: number): Promise { + this.clients = this.clients.filter(client => client.webContentsId !== windowId) + return true; } } @@ -29,7 +41,6 @@ export class ElectronBasePluginClient extends PluginClient { webContentsId: number; constructor(webcontentsid: number, profile: Profile, methods: string[] = []) { super(); - console.log('ElectronBasePluginClient', profile); this.methods = profile.methods; this.webContentsId = webcontentsid; BrowserWindow.getAllWindows().forEach((window) => { diff --git a/apps/1test/src/electron/xtermPlugin.ts b/apps/1test/src/electron/xtermPlugin.ts index 53db68dad9..9a64e8df05 100644 --- a/apps/1test/src/electron/xtermPlugin.ts +++ b/apps/1test/src/electron/xtermPlugin.ts @@ -17,16 +17,7 @@ const profile: Profile = { export class XtermPlugin extends ElectronBasePlugin { client: PluginClient constructor() { - super(profile) - } - - async createClient(webContentsId: number): Promise { - console.log('createClient', webContentsId) - this.clients.push(new XtermPluginClient(webContentsId)) - } - - async closeClient(webContentsId: number): Promise { - console.log('closeClient', webContentsId) + super(profile, clientProfile, XtermPluginClient) } } @@ -41,8 +32,8 @@ const clientProfile: Profile = { class XtermPluginClient extends ElectronBasePluginClient { terminals: pty.IPty[] = [] - constructor(webContentsId: number) { - super(webContentsId, clientProfile) + constructor(webContentsId: number, profile: Profile) { + super(webContentsId, profile) this.onload(() => { console.log('XtermPluginClient onload') }) diff --git a/apps/1test/src/index.ts b/apps/1test/src/index.ts index 584e062f92..2376c5c666 100644 --- a/apps/1test/src/index.ts +++ b/apps/1test/src/index.ts @@ -33,7 +33,7 @@ export const createWindow = (): void => { mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); mainWindow.maximize(); // Open the DevTools. - //mainWindow.webContents.openDevTools(); + mainWindow.webContents.openDevTools(); BrowserWindow.getAllWindows().forEach(window => { console.log('window IDS created', window.webContents.id) diff --git a/apps/1test/src/remix/lib/electronPlugin.ts b/apps/1test/src/remix/lib/electronPlugin.ts index f797c8867f..9412c98c83 100644 --- a/apps/1test/src/remix/lib/electronPlugin.ts +++ b/apps/1test/src/remix/lib/electronPlugin.ts @@ -47,9 +47,16 @@ export abstract class ElectronPlugin extends Plugin { * @param name The name of the plugin should connect to */ protected async connect(name: string) { + console.log('ElectronPluginConnector connect', name) + const r = await window.electronAPI.activatePlugin(name) + console.log('ElectronPluginConnector is connected', name, r) + + /* if(await window.electronAPI.activatePlugin(name) && !this.loaded){ + console.log('ElectronPluginConnector calling handshake', name) this.handshake() } + */ } /** Close connection with the plugin */ protected disconnect(): any | Promise { @@ -82,6 +89,7 @@ export abstract class ElectronPlugin extends Plugin { /** Perform handshake with the client if not loaded yet */ protected async handshake() { + console.log('ElectronPluginConnector handshake', this.loaded) if (!this.loaded) { this.loaded = true let methods: string[]; @@ -109,6 +117,7 @@ export abstract class ElectronPlugin extends Plugin { protected async getMessage(message: Message) { // Check for handshake request from the client if (message.action === 'request' && message.key === 'handshake') { + console.log('ElectronPluginConnector getMessage handshake', message) return this.handshake() } @@ -161,6 +170,7 @@ export abstract class ElectronPlugin extends Plugin { // Return result from exposed method case 'response': { const { id, payload, error } = message + console.log('ElectronPluginConnector getMessage response', message, this.pendingRequest) this.pendingRequest[id](payload, error) delete this.pendingRequest[id] break diff --git a/apps/1test/src/renderer.ts b/apps/1test/src/renderer.ts index d37801a377..f94afdb551 100644 --- a/apps/1test/src/renderer.ts +++ b/apps/1test/src/renderer.ts @@ -11,6 +11,8 @@ class MyAppManager extends PluginManager { onActivation(): void { this.on('fs', 'loaded', async () => { console.log('fs loaded') + const files = await this.call('fs', 'readdir', './') + console.log('files', files) }) /* this.on('fs', 'loaded', async () => { diff --git a/apps/remix-ide/contracts/.deps/remix-tests/remix_accounts.sol b/apps/remix-ide/contracts/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000..b4ee04a169 --- /dev/null +++ b/apps/remix-ide/contracts/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + return address(0); + } +} diff --git a/apps/remix-ide/contracts/.deps/remix-tests/remix_tests.sol b/apps/remix-ide/contracts/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000..b8b9960362 --- /dev/null +++ b/apps/remix-ide/contracts/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/apps/remix-ide/src/app/plugins/fsPlugin.ts b/apps/remix-ide/src/app/plugins/fsPlugin.ts index fb1b1549e9..d3f1b2e7b7 100644 --- a/apps/remix-ide/src/app/plugins/fsPlugin.ts +++ b/apps/remix-ide/src/app/plugins/fsPlugin.ts @@ -1,26 +1,33 @@ import { ElectronPlugin } from '@remixproject/engine-electron'; +let workingDir = '/Volumes/bunsen/code/rmproject2/remix-project/apps/remix-ide/contracts/' + const fixPath = (path: string) => { - const workingDir = '/Volumes/bunsen/code/rmproject2/remix-project/apps/remix-ide/contracts/' + /* // if it starts with /, it's an absolute path remove it if (path.startsWith('/')) { path = path.slice(1) } path = workingDir + path + */ + return path } export class fsPlugin extends ElectronPlugin { public fs: any + constructor() { super({ displayName: 'fs', name: 'fs', description: 'fs', }) - this.methods = ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists'] + this.methods = ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists', 'setWorkingDir'] + + this.fs = { exists: async (path: string) => { @@ -35,7 +42,9 @@ export class fsPlugin extends ElectronPlugin { }, readdir: async (path: string) => { path = fixPath(path) + console.log('readdir', path) const files = await this.call('fs', 'readdir', path) + console.log('readdir', path, files) return files }, unlink: async (path: string) => { @@ -71,13 +80,19 @@ export class fsPlugin extends ElectronPlugin { } - } + } + async onActivation() { console.log('fsPluginClient onload', this.fs); (window as any).remixFileSystem = this.fs + + this.on('fs', 'workingDirChanged', (path: string) => { + console.log('change working dir', path) + workingDir = path + }) } } \ No newline at end of file diff --git a/apps/remixdesktop/src/engine.ts b/apps/remixdesktop/src/engine.ts index 9a27037d47..d611ce6af5 100644 --- a/apps/remixdesktop/src/engine.ts +++ b/apps/remixdesktop/src/engine.ts @@ -15,12 +15,15 @@ engine.register(fsPlugin) engine.register(gitPlugin) engine.register(xtermPlugin) -ipcMain.handle('manager:activatePlugin', async (event, arg) => { - console.log('manager:activatePlugin', arg) - if(await appManager.isActive(arg)){ - return true - } - return await appManager.activatePlugin(arg) +appManager.activatePlugin('fs') + +ipcMain.handle('manager:activatePlugin', async (event, plugin) => { + console.log('manager:activatePlugin', plugin, event.sender.id) + return await appManager.call(plugin, 'createClient', event.sender.id) +}) + +ipcMain.handle('getWebContentsID', (event, message) => { + return event.sender.id }) app.on('before-quit', async () => { diff --git a/apps/remixdesktop/src/fsPlugin.ts b/apps/remixdesktop/src/fsPlugin.ts index 363d506711..2911826495 100644 --- a/apps/remixdesktop/src/fsPlugin.ts +++ b/apps/remixdesktop/src/fsPlugin.ts @@ -1,80 +1,90 @@ -import { PluginClient } from "@remixproject/plugin"; -import { createElectronClient } from "@remixproject/plugin-electron" -import { Plugin } from '@remixproject/engine'; +import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" import fs from 'fs/promises' -import { Stats } from "fs"; import { Profile } from "@remixproject/plugin-utils"; import chokidar from 'chokidar' -import { mainWindow } from "./main"; +import { dialog } from "electron"; const profile: Profile = { displayName: 'fs', name: 'fs', - description: 'fs', + description: 'fs' } -export class FSPlugin extends Plugin { - client: FSPluginClient +export class FSPlugin extends ElectronBasePlugin { + clients: FSPluginClient[] = [] constructor() { super(profile) - this.methods = ['closeWatch'] + this.methods = [...super.methods, 'closeWatch'] + } + + async createClient(webContentsId: number): Promise { + this.clients.push(new FSPluginClient(webContentsId)) } - onActivation(): void { - this.client = new FSPluginClient() + async closeClient(webContentsId: number): Promise { + console.log('closeClient', webContentsId) } + async closeWatch(): Promise { - console.log('closeWatch') - if (this.client) - await this.client.closeWatch() + for (const client of this.clients) { + await client.closeWatch() + } } } -class FSPluginClient extends PluginClient { - watcher: chokidar.FSWatcher | undefined - constructor() { - super() - this.methods = ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists', 'watch', 'closeWatch', 'currentPath'] - createElectronClient(this, profile, mainWindow) - console.log(mainWindow) +const clientProfile: Profile = { + name: 'fs', + displayName: 'fs', + description: 'fs', + methods: ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists', 'currentPath', 'watch', 'closeWatch', 'setWorkingDir'] +} + +class FSPluginClient extends ElectronBasePluginClient { + watcher: chokidar.FSWatcher + workingDir: string = '/Volumes/bunsen/code/rmproject2/remix-project/apps/remix-ide/contracts/' + + constructor(webContentsId: number) { + super(webContentsId, clientProfile) this.onload(() => { console.log('fsPluginClient onload') }) } async readdir(path: string): Promise { - - return fs.readdir(path) + // call node fs.readdir + console.log('readdir', path) + const files = fs.readdir(this.fixPath(path)) + return files } async readFile(path: string): Promise { - return fs.readFile(path, 'utf8') + return fs.readFile(this.fixPath(path), 'utf8') } async writeFile(path: string, content: string): Promise { - return fs.writeFile(path, content, 'utf8') + return fs.writeFile(this.fixPath(path), content, 'utf8') } async mkdir(path: string): Promise { - return fs.mkdir(path) + return fs.mkdir(this.fixPath(path)) } async rmdir(path: string): Promise { - return fs.rmdir(path) + return fs.rmdir(this.fixPath(path)) } async unlink(path: string): Promise { - return fs.unlink(path) + return fs.unlink(this.fixPath(path)) } async rename(oldPath: string, newPath: string): Promise { - return fs.rename(oldPath, newPath) + return fs.rename(this.fixPath(oldPath), this.fixPath(newPath)) } async stat(path: string): Promise { - const stat = await fs.stat(path) + const stat = await fs.stat(path) //console.log('stat', path, stat) const isDirectory = stat.isDirectory() return { @@ -84,7 +94,7 @@ class FSPluginClient extends PluginClient { } async exists(path: string): Promise { - return fs.access(path).then(() => true).catch(() => false) + return fs.access(this.fixPath(path)).then(() => true).catch(() => false) } async currentPath(): Promise { @@ -92,19 +102,38 @@ class FSPluginClient extends PluginClient { } async watch(path: string): Promise { - console.log('watch', path) if (this.watcher) this.watcher.close() this.watcher = - chokidar.watch(path).on('change', (path, stats) => { + chokidar.watch(this.fixPath(path)).on('change', (path, stats) => { console.log('change', path, stats) this.emit('change', path, stats) }) } async closeWatch(): Promise { - console.log('closeWatch') + console.log('closing Watcher', this.webContentsId) if (this.watcher) this.watcher.close() } + async setWorkingDir(): Promise { + const dirs = dialog.showOpenDialogSync(this.window, { + properties: ['openDirectory'] + }) + if (dirs && dirs.length > 0) { + this.workingDir = dirs[0] + this.emit('workingDirChanged', dirs[0]) + } + + } + + fixPath(path: string): string { + + if (path.startsWith('/')) { + path = path.slice(1) + } + path = this.workingDir + path + return path + } + } \ No newline at end of file diff --git a/apps/remixdesktop/src/gitPlugin.ts b/apps/remixdesktop/src/gitPlugin.ts index ac161f456a..3ecd82d646 100644 --- a/apps/remixdesktop/src/gitPlugin.ts +++ b/apps/remixdesktop/src/gitPlugin.ts @@ -1,9 +1,7 @@ -import { Plugin } from "@remixproject/engine"; import { PluginClient } from "@remixproject/plugin"; import { Profile } from "@remixproject/plugin-utils"; import { spawn } from "child_process"; -import { createElectronClient } from "@remixproject/plugin-electron" -import { mainWindow } from "./main"; +import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" const profile: Profile = { name: 'git', @@ -11,23 +9,34 @@ const profile: Profile = { description: 'Git plugin', } -export class GitPlugin extends Plugin { +export class GitPlugin extends ElectronBasePlugin { client: PluginClient constructor() { super(profile) } - onActivation(): void { - this.client = new GitPluginClient() + async createClient(webContentsId: number): Promise { + this.clients.push(new GitPluginClient(webContentsId)) } + async closeClient(webContentsId: number): Promise { + console.log('closeClient', webContentsId) + } + + } -class GitPluginClient extends PluginClient { - constructor() { - super() - this.methods = ['log', 'status', 'add', 'commit', 'push', 'pull', 'clone', 'checkout', 'branch', 'merge', 'reset', 'revert', 'diff', 'stash', 'apply', 'cherryPick', 'rebase', 'tag', 'fetch', 'remote', 'config', 'show', 'init', 'help', 'version'] - createElectronClient(this, profile, mainWindow) +const clientProfile: Profile = { + name: 'git', + displayName: 'Git', + description: 'Git plugin', + methods: ['log', 'status', 'add', 'commit', 'push', 'pull', 'clone', 'checkout', 'branch', 'merge', 'reset', 'revert', 'diff', 'stash', 'apply', 'cherryPick', 'rebase', 'tag', 'fetch', 'remote', 'config', 'show', 'init', 'help', 'version'] +} + +class GitPluginClient extends ElectronBasePluginClient { + + constructor(webContentsId: number) { + super(webContentsId, clientProfile) this.onload(() => { console.log('GitPluginClient onload') }) diff --git a/apps/remixdesktop/src/preload.ts b/apps/remixdesktop/src/preload.ts index 2ad41ba373..b9918327aa 100644 --- a/apps/remixdesktop/src/preload.ts +++ b/apps/remixdesktop/src/preload.ts @@ -1,6 +1,3 @@ -// electron preload script -// write contextbridge to window - import { Message } from '@remixproject/plugin-utils' import { contextBridge, ipcRenderer } from 'electron' @@ -11,16 +8,36 @@ console.log('preload.ts') const exposedPLugins = ['fs', 'git', 'xterm'] +console.log('preload.ts', process) +let webContentsId: number | undefined +/* +// get the window id from the process arguments +const windowIdFromArgs = process.argv.find(arg => arg.startsWith('--window-id=')) +if (windowIdFromArgs) { + [, windowId] = windowIdFromArgs.split('=') + console.log('windowId', windowId, ) +} +*/ +ipcRenderer.invoke('getWebContentsID').then((id: number) => { + webContentsId = id + console.log('getWebContentsID', webContentsId) +}) + contextBridge.exposeInMainWorld('electronAPI', { activatePlugin: (name: string) => { return ipcRenderer.invoke('manager:activatePlugin', name) }, - + + getWindowId: () => ipcRenderer.invoke('getWindowID'), + plugins: exposedPLugins.map(name => { return { name, on: (cb:any) => ipcRenderer.on(`${name}:send`, cb), - send: (message: Partial) => ipcRenderer.send(`${name}:on`, message) + send: (message: Partial) => { + console.log('send', message, `${name}:on:${webContentsId}`) + ipcRenderer.send(`${name}:on:${webContentsId}`, message) + } } }) }) \ No newline at end of file diff --git a/apps/remixdesktop/src/xtermPlugin.ts b/apps/remixdesktop/src/xtermPlugin.ts index 6d6d86482b..59e28cc6ee 100644 --- a/apps/remixdesktop/src/xtermPlugin.ts +++ b/apps/remixdesktop/src/xtermPlugin.ts @@ -1,12 +1,9 @@ -import { Plugin } from "@remixproject/engine"; import { PluginClient } from "@remixproject/plugin"; import { Profile } from "@remixproject/plugin-utils"; -import { spawn } from "child_process"; -import { createElectronClient } from "@remixproject/plugin-electron" +import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" import os from 'os'; import * as pty from "node-pty" -import { mainWindow } from "./main"; const profile: Profile = { name: 'xterm', @@ -14,60 +11,70 @@ const profile: Profile = { description: 'xterm plugin', } -export class XtermPlugin extends Plugin { +export class XtermPlugin extends ElectronBasePlugin { client: PluginClient constructor() { super(profile) } - onActivation(): void { - this.client = new XtermPluginClient() + async createClient(webContentsId: number): Promise { + console.log('createClient', webContentsId) + this.clients.push(new XtermPluginClient(webContentsId)) } + async closeClient(webContentsId: number): Promise { + console.log('closeClient', webContentsId) + } + +} + +const clientProfile: Profile = { + name: 'xterm', + displayName: 'xterm', + description: 'xterm plugin', + methods: ['createTerminal', 'close', 'keystroke'] } -class XtermPluginClient extends PluginClient { +class XtermPluginClient extends ElectronBasePluginClient { + terminals: pty.IPty[] = [] - constructor() { - super() - this.methods = ['keystroke', 'createTerminal', 'close'] - createElectronClient(this, profile, mainWindow) + constructor(webContentsId: number) { + super(webContentsId, clientProfile) this.onload(() => { console.log('XtermPluginClient onload') }) } async keystroke(key: string, pid: number): Promise { - console.log('keystroke', key) this.terminals[pid].write(key) } - async createTerminal(path?: string): Promise{ + async createTerminal(path?: string): Promise { const shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash'; const ptyProcess = pty.spawn(shell, [], { - name: 'xterm-color', - cols: 80, - rows: 30, - cwd: path || process.cwd() , - env: process.env as { [key: string]: string }, + name: 'xterm-color', + cols: 80, + rows: 30, + cwd: path || process.cwd(), + //env: process.env }); ptyProcess.onData((data: string) => { this.sendData(data, ptyProcess.pid); }) this.terminals[ptyProcess.pid] = ptyProcess - console.log('create terminal', ptyProcess.pid) + return ptyProcess.pid } - async close(pid: number): Promise{ + async close(pid: number): Promise { this.terminals[pid].kill() delete this.terminals[pid] this.emit('close', pid) } - async sendData(data: string, pid: number){ + async sendData(data: string, pid: number) { this.emit('data', data, pid) } diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx index f9b7b1e1f0..1eca550e05 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx @@ -59,7 +59,7 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) { } const startSolidity = async () => { - await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) + //await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) plugin.verticalIcons.select('solidity') _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidity']) } diff --git a/libs/remix-ui/search/src/lib/context/context.tsx b/libs/remix-ui/search/src/lib/context/context.tsx index c4cc7ca551..f754ebde2f 100644 --- a/libs/remix-ui/search/src/lib/context/context.tsx +++ b/libs/remix-ui/search/src/lib/context/context.tsx @@ -311,6 +311,7 @@ export const SearchProvider = ({ } useEffect(() => { + return plugin.on('filePanel', 'setWorkspace', async workspace => { value.setSearchResults(null) value.clearUndo() diff --git a/libs/remix-ui/workspace/src/lib/actions/index.ts b/libs/remix-ui/workspace/src/lib/actions/index.ts index 1c609b153f..b381193b6a 100644 --- a/libs/remix-ui/workspace/src/lib/actions/index.ts +++ b/libs/remix-ui/workspace/src/lib/actions/index.ts @@ -44,6 +44,7 @@ const basicWorkspaceInit = async (workspaces: { name: string; isGitRepo: boolean } export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch) => { + console.log('initWorkspace', filePanelPlugin) if (filePanelPlugin) { plugin = filePanelPlugin dispatch = reducerDispatch @@ -52,8 +53,11 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React. const localhostProvider = filePanelPlugin.fileProviders.localhost const electrOnProvider = filePanelPlugin.fileProviders.browser const params = queryParams.get() as UrlParametersType - const workspaces = await getWorkspaces() || [] - dispatch(setWorkspaces(workspaces)) + let workspaces = [] + if (!isElectron()){ + workspaces = await getWorkspaces() || [] + dispatch(setWorkspaces(workspaces)) + } if (params.gist) { await createWorkspaceTemplate('gist-sample', 'gist-template') plugin.setWorkspace({ name: 'gist-sample', isLocalhost: false }) @@ -114,9 +118,17 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React. } } else await basicWorkspaceInit(workspaces, workspaceProvider) } else if (isElectron()) { + console.log('isElectron initWorkspace') plugin.call('notification', 'toast', `connecting to electron...`) + plugin.setWorkspace({ name: 'electron', isLocalhost: false }) + dispatch(setCurrentWorkspace({ name: 'electron', isGitRepo: false })) + listenOnProviderEvents(electrOnProvider)(dispatch) + dispatch(setMode('browser')) + dispatch(fsInitializationCompleted()) + plugin.emit('workspaceInitializationCompleted') + return } else if (localStorage.getItem("currentWorkspace")) { const index = workspaces.findIndex(element => element.name == localStorage.getItem("currentWorkspace")) diff --git a/package.json b/package.json index 005bd1b791..f3edc5bd99 100644 --- a/package.json +++ b/package.json @@ -134,15 +134,15 @@ "@openzeppelin/contracts": "^4.7.3", "@openzeppelin/upgrades-core": "^1.22.0", "@openzeppelin/wizard": "0.2.0", - "@remixproject/engine": "0.3.35", - "@remixproject/engine-electron": " 0.3.35", - "@remixproject/engine-web": "0.3.35", - "@remixproject/plugin": "0.3.35", - "@remixproject/plugin-api": "0.3.35", - "@remixproject/plugin-electron": "0.3.35", - "@remixproject/plugin-utils": "0.3.35", - "@remixproject/plugin-webview": "0.3.35", - "@remixproject/plugin-ws": "0.3.35", + "@remixproject/engine": "0.3.36", + "@remixproject/engine-electron": "0.3.36", + "@remixproject/engine-web": "0.3.36", + "@remixproject/plugin": "0.3.36", + "@remixproject/plugin-api": "0.3.36", + "@remixproject/plugin-electron": "0.3.36", + "@remixproject/plugin-utils": "0.3.36", + "@remixproject/plugin-webview": "0.3.36", + "@remixproject/plugin-ws": "0.3.36", "@types/nightwatch": "^2.3.1", "@walletconnect/ethereum-provider": "^2.6.2", "@walletconnect/sign-client": "^2.6.0", diff --git a/yarn.lock b/yarn.lock index 70188a89b0..bf87ffdc05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5131,82 +5131,82 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590" integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ== -"@remixproject/engine-electron@ 0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/engine-electron/-/engine-electron-0.3.35.tgz#6c5bf507e7719f9767b837c48f07f29f4bbc5ff5" - integrity sha512-JPKzQ/Bn7QZDQzGceyZI23pZ4jhECf26Ajy/+my38zZWygoUms0J743yD4Z8ClrnXTUa7cXejOMJkwKcMDJ6kQ== - dependencies: - "@remixproject/engine" "0.3.35" - "@remixproject/plugin-api" "0.3.35" - "@remixproject/plugin-utils" "0.3.35" - -"@remixproject/engine-web@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/engine-web/-/engine-web-0.3.35.tgz#34a017fa8675164a762dc8bcec9a2c7f8410f950" - integrity sha512-d0fBA/FjFdMY9FCp2sEs/sL7kSo29IsFY02mM4HA4byjGdXmOtFquNI/bg32IX3aDWgsnTZhgbI1LQLU0vnxwg== - dependencies: - "@remixproject/engine" "0.3.35" - "@remixproject/plugin-api" "0.3.35" - "@remixproject/plugin-utils" "0.3.35" - -"@remixproject/engine@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/engine/-/engine-0.3.35.tgz#4bbb21a7ce6ecdbd021fd231ba3daefbbedb2d6d" - integrity sha512-lK1h7njE/dGLnBayE5X/zcnkjOeAu+UvDkE9PBRCdIte21M6UqmzClslCL1FjHP7j5hrr769OziQqvGajk4nFQ== - dependencies: - "@remixproject/plugin-api" "0.3.35" - "@remixproject/plugin-utils" "0.3.35" - -"@remixproject/plugin-api@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/plugin-api/-/plugin-api-0.3.35.tgz#1f47eda88bb49c14b29614549f9f42387afba054" - integrity sha512-lleQQ4B3QMtnjWflJHmLZYQ19jR/vGDT6ULskbj90qjss/xPp4NGqv18DnH5m8ycI2sTxcCdx9GEljFNieY3Bg== - dependencies: - "@remixproject/plugin-utils" "0.3.35" - -"@remixproject/plugin-electron@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/plugin-electron/-/plugin-electron-0.3.35.tgz#64646f8deafb6ec816d6bf156a610d8d1c8b8276" - integrity sha512-fdRm4StVdkuZj0nvRk9KxqC1jcwZ4ytjKbdPRpZMXl2rrImvxMt4VDTkz9MHTYufDF6CDprMfHN3zLlFKuelBA== - dependencies: - "@remixproject/engine" "0.3.35" - "@remixproject/plugin" "0.3.35" - "@remixproject/plugin-api" "0.3.35" - "@remixproject/plugin-utils" "0.3.35" - -"@remixproject/plugin-utils@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/plugin-utils/-/plugin-utils-0.3.35.tgz#7d3b561c1bddea1c1c0bd74970b91052c1a42e5b" - integrity sha512-+QCbOp45+bn5ou6CLmTqDQ2AGsCiQIEXQlg3ULZNWKeo+8yvMXOHWJeLYRPV8TsSJQ6G1t3FeIVg4ZUGEPTFsw== +"@remixproject/engine-electron@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/engine-electron/-/engine-electron-0.3.36.tgz#24304072b102d23ea76d88dd142c28ed991aa46b" + integrity sha512-ueiO48ViKyYgZgZmMreXyEk62cSlAzwukSyXnxI3T4G5ykKL0G461ocK3cSYfTKoZ/g0/j7oUz25I8wXtU3boA== + dependencies: + "@remixproject/engine" "0.3.36" + "@remixproject/plugin-api" "0.3.36" + "@remixproject/plugin-utils" "0.3.36" + +"@remixproject/engine-web@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/engine-web/-/engine-web-0.3.36.tgz#105328588fee0eee692f2e64aeb3faf2749ea56c" + integrity sha512-rNYZiEFplg9A3Vfb72zVxqrUE3rVFOss8uBcAhWPt0um8vl7cZ8nRoFc5pxgnvDh+ozRo5YbWR0fqmDA4OiIUw== + dependencies: + "@remixproject/engine" "0.3.36" + "@remixproject/plugin-api" "0.3.36" + "@remixproject/plugin-utils" "0.3.36" + +"@remixproject/engine@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/engine/-/engine-0.3.36.tgz#aa93210716ab959b1d39e08ea38c7357495381f3" + integrity sha512-D5sf8WJBS5cbjqZBDWo/hN5kT6YhkZysGy0Y1QWcQj48WhOYHawl6VMExMsqVXtPZzJTWHs2N/hXuoc6MF6xfg== + dependencies: + "@remixproject/plugin-api" "0.3.36" + "@remixproject/plugin-utils" "0.3.36" + +"@remixproject/plugin-api@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/plugin-api/-/plugin-api-0.3.36.tgz#58a77184c17ddece59fc2b8e7dd30a92ddb164a2" + integrity sha512-T7/9FAhm499sFbOh/iuK05GRpTZwYJtXrlMUAGE9qhfo4ep62sJg0TLss1BRnfhkDBvlS10rqdF2wWTPQ9JSPA== + dependencies: + "@remixproject/plugin-utils" "0.3.36" + +"@remixproject/plugin-electron@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/plugin-electron/-/plugin-electron-0.3.36.tgz#4b0067f3b42454f95625ed774a10751a6557b3ee" + integrity sha512-WM4u4zUs95adZxbC/GrMzfuCXfBde9TuoPeS2Nb9ysRQKj5eRfTIWiYrh9PxoezrEF8EYPo8gVrf9A+PGlKf3w== + dependencies: + "@remixproject/engine" "0.3.36" + "@remixproject/plugin" "0.3.36" + "@remixproject/plugin-api" "0.3.36" + "@remixproject/plugin-utils" "0.3.36" + +"@remixproject/plugin-utils@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/plugin-utils/-/plugin-utils-0.3.36.tgz#92478cd820e23b42a911a3e6e3f88d759f34d395" + integrity sha512-gbTzp3rHVITVn45vgb/f+huo6C/zogUCTmzqOj+D9INaQR1Fr6XtOL8erKjD0pNBbyw3lxpRsDKEpRQVE7sMbg== dependencies: tslib "2.0.1" -"@remixproject/plugin-webview@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/plugin-webview/-/plugin-webview-0.3.35.tgz#d602f954d93dd3cbef8c41e43aa6c0e6da79b293" - integrity sha512-Ch2KjufmMjq+3Ynj7NkPP4lvlxxHmE4TY2k3bII+nn2dEa0ts0/aNCp5fCzMEVMivNzVpZIQBPEMzJhSv+ULcA== +"@remixproject/plugin-webview@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/plugin-webview/-/plugin-webview-0.3.36.tgz#38997745608952cc40ce5c668d0836d0d5279b12" + integrity sha512-9C/GQLDQzzX6g8MH9L54C+K/2FxBa4tCdlxUbPH9infTFvk/jcMjpd/CSAIBK4EfZCs1mL+1Opc8bX3VrfchpA== dependencies: - "@remixproject/plugin" "0.3.35" - "@remixproject/plugin-api" "0.3.35" - "@remixproject/plugin-utils" "0.3.35" + "@remixproject/plugin" "0.3.36" + "@remixproject/plugin-api" "0.3.36" + "@remixproject/plugin-utils" "0.3.36" axios "^0.21.1" -"@remixproject/plugin-ws@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/plugin-ws/-/plugin-ws-0.3.35.tgz#16a3f6673bb37288db531dcd063af59fefb01229" - integrity sha512-4sZnd2Fme3T+Ohr0+I4hJlHtZVF0oG2srGds80Cp0MiYmz9pJ3l2lhSe4Dl7Np14nfYrqxiwRGf6s4q2PQx6Ug== +"@remixproject/plugin-ws@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/plugin-ws/-/plugin-ws-0.3.36.tgz#a80ccf5e7ae866bc53a79a7da4f36b67a0a33972" + integrity sha512-+vGwHd7KAgGsPoPph8T5mGkGthJagtwsb53LopZwbwDziMEWP7IVu44TcI46qR/+oBHLm0MmH1ukCzRAkbDu+w== dependencies: - "@remixproject/plugin" "0.3.35" - "@remixproject/plugin-api" "0.3.35" - "@remixproject/plugin-utils" "0.3.35" + "@remixproject/plugin" "0.3.36" + "@remixproject/plugin-api" "0.3.36" + "@remixproject/plugin-utils" "0.3.36" -"@remixproject/plugin@0.3.35": - version "0.3.35" - resolved "https://registry.yarnpkg.com/@remixproject/plugin/-/plugin-0.3.35.tgz#6e4a8f2d400f70ae5351a9b560d3855f461389b4" - integrity sha512-GGLyAv8sV0X0QhcF7ZqSfS0Fb+xhaFsWzhRZPeVs54d4a6ddOjCrR7A/vyIPGNS2vU4YSVbi8tk1Q1RuVBLAgQ== +"@remixproject/plugin@0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@remixproject/plugin/-/plugin-0.3.36.tgz#393867208ace117c8dbfd08adb4d5feec1b17d4b" + integrity sha512-+lFgfLI3vbiB6OQIkeX2Yx1EyKdIUGU1lJ7K51HwPfbGet5Y4w8qDMXBblRM1HhVe7bDYlBOzzbcil8/03Dsnw== dependencies: - "@remixproject/plugin-api" "0.3.35" - "@remixproject/plugin-utils" "0.3.35" + "@remixproject/plugin-api" "0.3.36" + "@remixproject/plugin-utils" "0.3.36" events "3.2.0" "@restart/context@^2.1.4":