rdesktop
filip mertens 1 year ago
parent ac28db3a52
commit b106cc8cdf
  1. 13
      apps/1test/src/remix/lib/electronPlugin.ts
  2. 8
      apps/remix-ide/src/app/files/fileManager.ts
  3. 2
      apps/remix-ide/src/app/files/fileProvider.js
  4. 111
      apps/remix-ide/src/app/plugins/fsPlugin.ts
  5. 5
      apps/remixdesktop/src/engine.ts
  6. 37
      apps/remixdesktop/src/fsPlugin.ts
  7. 15
      apps/remixdesktop/src/gitPlugin.ts
  8. 36
      apps/remixdesktop/src/main.ts
  9. 24
      apps/remixdesktop/src/menus/commands.ts
  10. 56
      apps/remixdesktop/src/menus/darwin.ts
  11. 28
      apps/remixdesktop/src/menus/file.ts
  12. 26
      apps/remixdesktop/src/menus/main.ts
  13. 15
      apps/remixdesktop/src/xtermPlugin.ts
  14. 5
      libs/remix-ui/workspace/src/lib/actions/events.ts
  15. 99
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  16. 21
      package.json
  17. 139
      yarn.lock

@ -47,16 +47,11 @@ export abstract class ElectronPlugin extends Plugin {
* @param name The name of the plugin should connect to * @param name The name of the plugin should connect to
*/ */
protected async connect(name: string) { protected async connect(name: string) {
console.log('ElectronPluginConnector connect', name) const connected = await window.electronAPI.activatePlugin(name)
const r = await window.electronAPI.activatePlugin(name) if(connected && !this.loaded){
console.log('ElectronPluginConnector is connected', name, r)
/*
if(await window.electronAPI.activatePlugin(name) && !this.loaded){
console.log('ElectronPluginConnector calling handshake', name)
this.handshake() this.handshake()
} }
*/
} }
/** Close connection with the plugin */ /** Close connection with the plugin */
protected disconnect(): any | Promise<any> { protected disconnect(): any | Promise<any> {
@ -89,7 +84,6 @@ export abstract class ElectronPlugin extends Plugin {
/** Perform handshake with the client if not loaded yet */ /** Perform handshake with the client if not loaded yet */
protected async handshake() { protected async handshake() {
console.log('ElectronPluginConnector handshake', this.loaded)
if (!this.loaded) { if (!this.loaded) {
this.loaded = true this.loaded = true
let methods: string[]; let methods: string[];
@ -170,7 +164,6 @@ export abstract class ElectronPlugin extends Plugin {
// Return result from exposed method // Return result from exposed method
case 'response': { case 'response': {
const { id, payload, error } = message const { id, payload, error } = message
console.log('ElectronPluginConnector getMessage response', message, this.pendingRequest)
this.pendingRequest[id](payload, error) this.pendingRequest[id](payload, error)
delete this.pendingRequest[id] delete this.pendingRequest[id]
break break

@ -155,8 +155,12 @@ class FileManager extends Plugin {
refresh() { refresh() {
const provider = this.fileProviderOf('/') const provider = this.fileProviderOf('/')
// emit rootFolderChanged so that File Explorer reloads the file tree // emit rootFolderChanged so that File Explorer reloads the file tree
provider.event.emit('rootFolderChanged', provider.workspace || '/') if(isElectron()){
this.emit('rootFolderChanged', provider.workspace || '/') provider.event.emit('refresh')
}else{
provider.event.emit('rootFolderChanged', provider.workspace || '/')
this.emit('rootFolderChanged', provider.workspace || '/')
}
} }
/** /**

@ -286,12 +286,14 @@ class FileProvider {
if (path.indexOf('/') !== 0) path = '/' + path if (path.indexOf('/') !== 0) path = '/' + path
try { try {
const files = await window.remixFileSystem.readdir(path) const files = await window.remixFileSystem.readdir(path)
console.log(files, 'files resolveDirectory')
const ret = {} const ret = {}
if (files) { if (files) {
for (let element of files) { for (let element of files) {
path = path.replace(/^\/|\/$/g, '') // remove first and last slash path = path.replace(/^\/|\/$/g, '') // remove first and last slash
element = element.replace(/^\/|\/$/g, '') // remove first and last slash element = element.replace(/^\/|\/$/g, '') // remove first and last slash
const absPath = (path === '/' ? '' : path) + '/' + element const absPath = (path === '/' ? '' : path) + '/' + element
console.log(absPath, 'absPath')
ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: (await window.remixFileSystem.stat(absPath)).isDirectory() } ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: (await window.remixFileSystem.stat(absPath)).isDirectory() }
// ^ ret does not accept path starting with '/' // ^ ret does not accept path starting with '/'
} }

@ -1,23 +1,25 @@
import { ElectronPlugin } from '@remixproject/engine-electron'; import { ElectronPlugin } from '@remixproject/engine-electron';
import once from 'just-once';
let workingDir = '/Volumes/bunsen/code/rmproject2/remix-project/apps/remix-ide/contracts/'
const fixPath = (path: string) => {
/*
// if it starts with /, it's an absolute path remove it
if (path.startsWith('/')) {
path = path.slice(1)
}
path = workingDir + path
*/
let workingDir = null
const fixPath = (path: string) => {
return path return path
} }
function wrapCallback(opts, cb) {
if (typeof opts === "function") {
cb = opts;
}
cb = once(cb);
const resolve = (...args) => cb(null, ...args)
return [resolve, cb];
}
export class fsPlugin extends ElectronPlugin { export class fsPlugin extends ElectronPlugin {
public fs: any public fs: any
public fsSync: any
constructor() { constructor() {
super({ super({
@ -27,6 +29,20 @@ export class fsPlugin extends ElectronPlugin {
}) })
this.methods = ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists', 'setWorkingDir'] this.methods = ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists', 'setWorkingDir']
// List of commands all filesystems are expected to provide. `rm` is not
// included since it may not exist and must be handled as a special case
const commands = [
'readFile',
'writeFile',
'mkdir',
'rmdir',
'unlink',
'stat',
'lstat',
'readdir',
'readlink',
'symlink',
]
this.fs = { this.fs = {
@ -43,7 +59,7 @@ export class fsPlugin extends ElectronPlugin {
readdir: async (path: string) => { readdir: async (path: string) => {
path = fixPath(path) path = fixPath(path)
console.log('readdir', path) console.log('readdir', path)
const files = await this.call('fs', 'readdir', path) const files = await this.call('fs', 'readdir', path)
console.log('readdir', path, files) console.log('readdir', path, files)
return files return files
}, },
@ -55,9 +71,12 @@ export class fsPlugin extends ElectronPlugin {
path = fixPath(path) path = fixPath(path)
return await this.call('fs', 'mkdir', path) return await this.call('fs', 'mkdir', path)
}, },
readFile: async (path: string) => { readFile: async (path: string, options) => {
console.log('readFile', path, options)
path = fixPath(path) path = fixPath(path)
return await this.call('fs', 'readFile', path) const file = await this.call('fs', 'readFile', path)
console.log('readFile', path, file)
return file
} }
, ,
rename: async (from: string, to: string) => { rename: async (from: string, to: string) => {
@ -70,28 +89,86 @@ export class fsPlugin extends ElectronPlugin {
, ,
stat: async (path: string) => { stat: async (path: string) => {
path = fixPath(path) path = fixPath(path)
const stat = await this.call('fs', 'stat', path) const stat = await this.call('fs', 'stat', path)
stat.isDirectory = () => stat.isDirectoryValue stat.isDirectory = () => stat.isDirectoryValue
stat.isFile = () => !stat.isDirectoryValue stat.isFile = () => !stat.isDirectoryValue
//console.log('stat', path, stat) //console.log('stat', path, stat)
return stat return stat
},
lstat: async (path: string) => {
path = fixPath(path)
const stat = await this.call('fs', 'lstat', path)
stat.isDirectory = () => stat.isDirectoryValue
stat.isFile = () => !stat.isDirectoryValue
//console.log('stat', path, stat)
return stat
},
readlink: async (path: string) => {
path = fixPath(path)
return await this.call('fs', 'readlink', path)
},
symlink: async (target: string, path: string) => {
path = fixPath(path)
return await this.call('fs', 'symlink', target, path)
} }
} }
} }
async onActivation() { async onActivation() {
console.log('fsPluginClient onload', this.fs); console.log('fsPluginClient onload', this.fs);
(window as any).remixFileSystem = this.fs (window as any).remixFileSystem = this.fs;
/*(window as any).remixFileSystemCallback = {
readdir: (filepath, opts, cb) => {
console.log('readdir', filepath, opts)
const [resolve, reject] = wrapCallback(opts, cb);
(window as any).remixFileSystem.fs.readdir(filepath, opts).then(resolve).catch(reject);
},
readFile: (filepath, opts, cb) => {
console.log('readFile', filepath, opts)
const [resolve, reject] = wrapCallback(opts, cb);
(window as any).remixFileSystem.fs.readFile(filepath, opts).then(resolve).catch(reject)
},
writeFile: (filepath, content, opts, cb) => {
const [resolve, reject] = wrapCallback(opts, cb);
(window as any).remixFileSystem.fs.writeFile(filepath, content, opts).then(resolve).catch(reject)
},
mkdir: (filepath, opts, cb) => {
const [resolve, reject] = wrapCallback(opts, cb);
(window as any).remixFileSystem.fs.mkdir(filepath, opts).then(resolve).catch(reject)
},
rmdir: (filepath, opts, cb) => {
const [resolve, reject] = wrapCallback(opts, cb);
(window as any).remixFileSystem.fs.rmdir(filepath, opts).then(resolve).catch(reject)
},
unlink: (filepath, opts, cb) => {
const [resolve, reject] = wrapCallback(opts, cb);
(window as any).remixFileSystem.fs.unlink(filepath, opts).then(resolve).catch(reject)
},
stat: (filepath, opts, cb) => {
const [resolve, reject] = wrapCallback(opts, cb);
(window as any).remixFileSystem.fs.stat(filepath, opts).then(resolve).catch(reject)
}
};
*/
this.on('fs', 'workingDirChanged', (path: string) => { this.on('fs', 'workingDirChanged', async (path: string) => {
console.log('change working dir', path) console.log('change working dir', path)
workingDir = path workingDir = path
await this.call('fileManager', 'refresh')
}) })
} }

@ -22,6 +22,11 @@ ipcMain.handle('manager:activatePlugin', async (event, plugin) => {
return await appManager.call(plugin, 'createClient', event.sender.id) return await appManager.call(plugin, 'createClient', event.sender.id)
}) })
ipcMain.on('fs:openFolder', async (event) => {
console.log('fs:openFolder', event)
fsPlugin.openFolder(event)
})
ipcMain.handle('getWebContentsID', (event, message) => { ipcMain.handle('getWebContentsID', (event, message) => {
return event.sender.id return event.sender.id
}) })

@ -13,25 +13,23 @@ const profile: Profile = {
export class FSPlugin extends ElectronBasePlugin { export class FSPlugin extends ElectronBasePlugin {
clients: FSPluginClient[] = [] clients: FSPluginClient[] = []
constructor() { constructor() {
super(profile) super(profile, clientProfile, FSPluginClient)
this.methods = [...super.methods, 'closeWatch'] this.methods = [...super.methods, 'closeWatch']
} }
async createClient(webContentsId: number): Promise<void> {
this.clients.push(new FSPluginClient(webContentsId))
}
async closeClient(webContentsId: number): Promise<void> {
console.log('closeClient', webContentsId)
}
async closeWatch(): Promise<void> { async closeWatch(): Promise<void> {
for (const client of this.clients) { for (const client of this.clients) {
await client.closeWatch() await client.closeWatch()
} }
} }
openFolder(webContentsId: any): void {
const client = this.clients.find(c => c.webContentsId === webContentsId)
if (client) {
client.setWorkingDir()
}
}
} }
const clientProfile: Profile = { const clientProfile: Profile = {
@ -45,13 +43,14 @@ class FSPluginClient extends ElectronBasePluginClient {
watcher: chokidar.FSWatcher watcher: chokidar.FSWatcher
workingDir: string = '/Volumes/bunsen/code/rmproject2/remix-project/apps/remix-ide/contracts/' workingDir: string = '/Volumes/bunsen/code/rmproject2/remix-project/apps/remix-ide/contracts/'
constructor(webContentsId: number) { constructor(webContentsId: number, profile: Profile) {
super(webContentsId, clientProfile) super(webContentsId, profile)
this.onload(() => { this.onload(() => {
console.log('fsPluginClient onload') console.log('fsPluginClient onload')
}) })
} }
async readdir(path: string): Promise<string[]> { async readdir(path: string): Promise<string[]> {
// call node fs.readdir // call node fs.readdir
console.log('readdir', path) console.log('readdir', path)
@ -59,8 +58,8 @@ class FSPluginClient extends ElectronBasePluginClient {
return files return files
} }
async readFile(path: string): Promise<string> { async readFile(path: string, options: any ): Promise<string> {
return fs.readFile(this.fixPath(path), 'utf8') return fs.readFile(this.fixPath(path), 'utf8')
} }
async writeFile(path: string, content: string): Promise<void> { async writeFile(path: string, content: string): Promise<void> {
@ -84,7 +83,7 @@ class FSPluginClient extends ElectronBasePluginClient {
} }
async stat(path: string): Promise<any> { async stat(path: string): Promise<any> {
const stat = await fs.stat(path) const stat = await fs.stat(this.fixPath(path))
//console.log('stat', path, stat) //console.log('stat', path, stat)
const isDirectory = stat.isDirectory() const isDirectory = stat.isDirectory()
return { return {
@ -93,6 +92,13 @@ class FSPluginClient extends ElectronBasePluginClient {
} }
} }
async lstat(path: string): Promise<any> {
const lstat = await fs.lstat(this.fixPath(path))
return lstat
}
async exists(path: string): Promise<boolean> { async exists(path: string): Promise<boolean> {
return fs.access(this.fixPath(path)).then(() => true).catch(() => false) return fs.access(this.fixPath(path)).then(() => true).catch(() => false)
} }
@ -131,6 +137,7 @@ class FSPluginClient extends ElectronBasePluginClient {
if (path.startsWith('/')) { if (path.startsWith('/')) {
path = path.slice(1) path = path.slice(1)
} }
if(!this.workingDir.endsWith('/')) this.workingDir = this.workingDir + '/'
path = this.workingDir + path path = this.workingDir + path
return path return path
} }

@ -12,18 +12,9 @@ const profile: Profile = {
export class GitPlugin extends ElectronBasePlugin { export class GitPlugin extends ElectronBasePlugin {
client: PluginClient client: PluginClient
constructor() { constructor() {
super(profile) super(profile, clientProfile, GitPluginClient)
} }
async createClient(webContentsId: number): Promise<void> {
this.clients.push(new GitPluginClient(webContentsId))
}
async closeClient(webContentsId: number): Promise<void> {
console.log('closeClient', webContentsId)
}
} }
const clientProfile: Profile = { const clientProfile: Profile = {
@ -35,8 +26,8 @@ const clientProfile: Profile = {
class GitPluginClient extends ElectronBasePluginClient { class GitPluginClient extends ElectronBasePluginClient {
constructor(webContentsId: number) { constructor(webContentsId: number, profile: Profile) {
super(webContentsId, clientProfile) super(webContentsId, profile)
this.onload(() => { this.onload(() => {
console.log('GitPluginClient onload') console.log('GitPluginClient onload')
}) })

@ -1,4 +1,4 @@
import { app, BrowserWindow } from 'electron'; import { app, BrowserWindow, dialog, Menu } from 'electron';
import path from 'path'; import path from 'path';
@ -13,13 +13,8 @@ if (
isPackaged = true; isPackaged = true;
} }
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
app.quit();
}
export let mainWindow: BrowserWindow; export let mainWindow: BrowserWindow;
const createWindow = (): void => { export const createWindow = (): void => {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
height: 800, height: 800,
@ -62,5 +57,32 @@ app.on('activate', () => {
} }
}); });
const showAbout = () => {
void dialog.showMessageBox({
title: `About Remix`,
message: `Remix`,
detail: `Remix`,
buttons: [],
});
};
// In this file you can include the rest of your app's specific main process // In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here. // code. You can also put them in separate files and import them here.
const isMac = process.platform === 'darwin'
import FileMenu from './menus/file';
import MainMenu from './menus/main';
import darwinMenu from './menus/darwin';
import { execCommand } from './menus/commands';
const commandKeys: Record<string, string> = {
'window:new': 'CmdOrCtrl+N',
'folder:open': 'CmdOrCtrl+O',
};
const menu = [...(process.platform === 'darwin' ? [darwinMenu(commandKeys, execCommand, showAbout)] : []), FileMenu(commandKeys, execCommand)]
Menu.setApplicationMenu(Menu.buildFromTemplate(menu))

@ -0,0 +1,24 @@
import {app, Menu, BrowserWindow, ipcMain} from 'electron';
import { createWindow } from '../main';
const commands: Record<string, (focusedWindow?: BrowserWindow) => void> = {
'window:new': () => {
// If window is created on the same tick, it will consume event too
setTimeout(createWindow, 0);
},
'folder:open': (focusedWindow) => {
if (focusedWindow) {
ipcMain.emit('fs:openFolder', focusedWindow.webContents.id);
}
}
};
export const execCommand = (command: string, focusedWindow?: BrowserWindow) => {
const fn = commands[command];
if (fn) {
fn(focusedWindow);
}
};

@ -0,0 +1,56 @@
// This menu label is overrided by OSX to be the appName
// The label is set to appName here so it matches actual behavior
import {app, BrowserWindow, MenuItemConstructorOptions} from 'electron';
export default (
commandKeys: Record<string, string>,
execCommand: (command: string, focusedWindow?: BrowserWindow) => void,
showAbout: () => void
): MenuItemConstructorOptions => {
return {
label: `${app.name}`,
submenu: [
{
label: 'About Remix',
click() {
showAbout();
}
},
{
type: 'separator'
},
{
label: 'Preferences...',
accelerator: commandKeys['window:preferences'],
click() {
execCommand('window:preferences');
}
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{
role: 'hide'
},
{
role: 'hideOthers'
},
{
role: 'unhide'
},
{
type: 'separator'
},
{
role: 'quit'
}
]
};
};

@ -0,0 +1,28 @@
import {BrowserWindow, MenuItemConstructorOptions} from 'electron';
export default (
commandKeys: Record<string, string>,
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
): MenuItemConstructorOptions => {
const isMac = process.platform === 'darwin';
return {
label: 'File',
submenu: [
{
label: 'New Window',
accelerator: commandKeys['window:new'],
click(item, focusedWindow) {
execCommand('window:new', focusedWindow);
}
},
{
label: 'Open Folder',
accelerator: commandKeys['folder:open'],
click(item, focusedWindow) {
execCommand('folder:open', focusedWindow);
}
}
]
};
};

@ -0,0 +1,26 @@
import {BrowserWindow, MenuItemConstructorOptions} from 'electron';
export default (
commandKeys: Record<string, string>,
execCommand: (command: string, focusedWindow?: BrowserWindow) => void
): MenuItemConstructorOptions => {
const isMac = process.platform === 'darwin';
return {
label: 'REMIX',
submenu: [
{
label: 'Close',
accelerator: commandKeys['pane:close'],
click(item, focusedWindow) {
execCommand('pane:close', focusedWindow);
}
},
{
label: isMac ? 'Close Window' : 'Quit',
role: 'close',
accelerator: commandKeys['window:close']
}
]
};
};

@ -14,16 +14,7 @@ const profile: Profile = {
export class XtermPlugin extends ElectronBasePlugin { export class XtermPlugin extends ElectronBasePlugin {
client: PluginClient client: PluginClient
constructor() { constructor() {
super(profile) super(profile, clientProfile, XtermPluginClient)
}
async createClient(webContentsId: number): Promise<void> {
console.log('createClient', webContentsId)
this.clients.push(new XtermPluginClient(webContentsId))
}
async closeClient(webContentsId: number): Promise<void> {
console.log('closeClient', webContentsId)
} }
} }
@ -38,8 +29,8 @@ const clientProfile: Profile = {
class XtermPluginClient extends ElectronBasePluginClient { class XtermPluginClient extends ElectronBasePluginClient {
terminals: pty.IPty[] = [] terminals: pty.IPty[] = []
constructor(webContentsId: number) { constructor(webContentsId: number, profile: Profile) {
super(webContentsId, clientProfile) super(webContentsId, profile)
this.onload(() => { this.onload(() => {
console.log('XtermPluginClient onload') console.log('XtermPluginClient onload')
}) })

@ -49,6 +49,7 @@ export const listenOnPluginEvents = (filePanelPlugin) => {
}) })
plugin.on('fileManager', 'rootFolderChanged', async (path: string) => { plugin.on('fileManager', 'rootFolderChanged', async (path: string) => {
console.log('rootFolderChanged', path)
rootFolderChanged(path) rootFolderChanged(path)
}) })
@ -96,6 +97,10 @@ export const listenOnProviderEvents = (provider) => (reducerDispatch: React.Disp
await switchToWorkspace(workspaceProvider.workspace) await switchToWorkspace(workspaceProvider.workspace)
}) })
provider.event.on('refresh', () => {
fetchWorkspaceDirectory('/')
})
provider.event.on('connected', () => { provider.event.on('connected', () => {
plugin.fileManager.setMode('localhost') plugin.fileManager.setMode('localhost')
dispatch(setMode('localhost')) dispatch(setMode('localhost'))

@ -12,6 +12,7 @@ import { MenuItems, WorkSpaceState } from './types'
import { contextMenuActions } from './utils' import { contextMenuActions } from './utils'
import FileExplorerContextMenu from './components/file-explorer-context-menu' import FileExplorerContextMenu from './components/file-explorer-context-menu'
import { customAction } from '@remixproject/plugin-api' import { customAction } from '@remixproject/plugin-api'
import isElectron from 'is-electron'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
@ -684,45 +685,66 @@ export function Workspace () {
<div className='d-flex flex-column w-100 remixui_fileexplorer' data-id="remixUIWorkspaceExplorer" onClick={resetFocus}> <div className='d-flex flex-column w-100 remixui_fileexplorer' data-id="remixUIWorkspaceExplorer" onClick={resetFocus}>
<div> <div>
<header> <header>
<div className="mx-2 my-2 d-flex flex-column"> <div className="mx-2 mb-2 d-flex flex-column">
<div className="d-flex"> <div className="d-flex justify-content-between">
{currentWorkspace !== LOCALHOST ? (<span className="remixui_topmenu d-flex"> {!isElectron() ?
<Dropdown id="workspacesMenuDropdown" data-id="workspacesMenuDropdown" onToggle={() => hideIconsMenu(!showIconsMenu)} show={showIconsMenu}> <span className="d-flex align-items-end">
<Dropdown.Toggle <label className="pl-1 form-check-label" htmlFor="workspacesSelect" style={{wordBreak: 'keep-all'}}>
as={CustomIconsToggle} <FormattedMessage id='filePanel.workspace' />
onClick={() => { </label>
hideIconsMenu(!showIconsMenu) </span>: null}
}} {currentWorkspace !== LOCALHOST && !isElectron() ? (<span className="remixui_menu remixui_topmenu d-flex justify-content-between align-items-end w-75">
icon={'fas fa-bars'} <CustomTooltip
></Dropdown.Toggle> placement="top"
<Dropdown.Menu as={CustomMenu} data-id="wsdropdownMenu" className='custom-dropdown-items remixui_menuwidth' rootCloseEvent="click"> tooltipId="createWorkspaceTooltip"
<HamburgerMenu tooltipClasses="text-nowrap"
createWorkspace={createWorkspace} tooltipText={<FormattedMessage id='filePanel.create' />}
renameCurrentWorkspace={renameCurrentWorkspace} >
downloadCurrentWorkspace={downloadCurrentWorkspace} <span
deleteCurrentWorkspace={deleteCurrentWorkspace} hidden={currentWorkspace === LOCALHOST}
deleteAllWorkspaces={deleteAllWorkspaces} id='workspaceCreate'
cloneGitRepository={cloneGitRepository} data-id='workspaceCreate'
downloadWorkspaces={downloadWorkspaces} onClick={(e) => {
restoreBackup={restoreBackup} e.stopPropagation()
hideIconsMenu={hideIconsMenu} createWorkspace()
addGithubAction={addGithubAction} _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate'])
addSlitherGithubAction={addSlitherGithubAction} }}
addHelperScripts={addHelperScripts} style={{ fontSize: 'medium' }}
addTsSolTestGithubAction={addTsSolTestGithubAction} className='far fa-plus remixui_menuicon d-flex align-self-end'
showIconsMenu={showIconsMenu} >
hideWorkspaceOptions={ currentWorkspace === LOCALHOST } </span>
hideLocalhostOptions={ currentWorkspace === NO_WORKSPACE } </CustomTooltip>
/> <Dropdown id="workspacesMenuDropdown" data-id="workspacesMenuDropdown" onToggle={() => hideIconsMenu(!showIconsMenu)} show={showIconsMenu}>
</Dropdown.Menu> <Dropdown.Toggle
</Dropdown> as={CustomIconsToggle}
</span>) : null} onClick={() => {
<span className="d-flex"> hideIconsMenu(!showIconsMenu)
<label className="pl-1 form-check-label" htmlFor="workspacesSelect" style={{wordBreak: 'keep-all'}}> }}
<FormattedMessage id='filePanel.workspace' /> icon={'fas fa-bars'}
</label> ></Dropdown.Toggle>
</span> <Dropdown.Menu as={CustomMenu} data-id="wsdropdownMenu" className='custom-dropdown-items remixui_menuwidth' rootCloseEvent="click">
<HamburgerMenu
createWorkspace={createWorkspace}
renameCurrentWorkspace={renameCurrentWorkspace}
downloadCurrentWorkspace={downloadCurrentWorkspace}
deleteCurrentWorkspace={deleteCurrentWorkspace}
deleteAllWorkspaces={deleteAllWorkspaces}
cloneGitRepository={cloneGitRepository}
downloadWorkspaces={downloadWorkspaces}
restoreBackup={restoreBackup}
hideIconsMenu={hideIconsMenu}
addGithubAction={addGithubAction}
addSlitherGithubAction={addSlitherGithubAction}
addTsSolTestGithubAction={addTsSolTestGithubAction}
showIconsMenu={showIconsMenu}
hideWorkspaceOptions={ currentWorkspace === LOCALHOST }
hideLocalhostOptions={ currentWorkspace === NO_WORKSPACE }
/>
</Dropdown.Menu>
</Dropdown>
</span>) : null}
</div> </div>
{!isElectron() ? (
<Dropdown id="workspacesSelect" data-id="workspacesSelect" onToggle={toggleDropdown} show={showDropdown}> <Dropdown id="workspacesSelect" data-id="workspacesSelect" onToggle={toggleDropdown} show={showDropdown}>
<Dropdown.Toggle <Dropdown.Toggle
as={CustomToggle} as={CustomToggle}
@ -767,6 +789,7 @@ export function Workspace () {
{ ((global.fs.browser.workspaces.length <= 0) || currentWorkspace === NO_WORKSPACE) && <Dropdown.Item onClick={() => { switchWorkspace(NO_WORKSPACE) }}>{ <span className="pl-3">NO_WORKSPACE</span> }</Dropdown.Item> } { ((global.fs.browser.workspaces.length <= 0) || currentWorkspace === NO_WORKSPACE) && <Dropdown.Item onClick={() => { switchWorkspace(NO_WORKSPACE) }}>{ <span className="pl-3">NO_WORKSPACE</span> }</Dropdown.Item> }
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
) : null}
</div> </div>
</header> </header>
</div> </div>

@ -134,15 +134,15 @@
"@openzeppelin/contracts": "^4.7.3", "@openzeppelin/contracts": "^4.7.3",
"@openzeppelin/upgrades-core": "^1.22.0", "@openzeppelin/upgrades-core": "^1.22.0",
"@openzeppelin/wizard": "0.2.0", "@openzeppelin/wizard": "0.2.0",
"@remixproject/engine": "0.3.36", "@remixproject/engine": "0.3.37",
"@remixproject/engine-electron": "0.3.36", "@remixproject/engine-electron": "0.3.37",
"@remixproject/engine-web": "0.3.36", "@remixproject/engine-web": "0.3.37",
"@remixproject/plugin": "0.3.36", "@remixproject/plugin": "0.3.37",
"@remixproject/plugin-api": "0.3.36", "@remixproject/plugin-api": "0.3.37",
"@remixproject/plugin-electron": "0.3.36", "@remixproject/plugin-electron": "0.3.37",
"@remixproject/plugin-utils": "0.3.36", "@remixproject/plugin-utils": "0.3.37",
"@remixproject/plugin-webview": "0.3.36", "@remixproject/plugin-webview": "0.3.37",
"@remixproject/plugin-ws": "0.3.36", "@remixproject/plugin-ws": "0.3.37",
"@types/nightwatch": "^2.3.1", "@types/nightwatch": "^2.3.1",
"@walletconnect/ethereum-provider": "^2.6.2", "@walletconnect/ethereum-provider": "^2.6.2",
"@walletconnect/sign-client": "^2.6.0", "@walletconnect/sign-client": "^2.6.0",
@ -178,11 +178,12 @@
"http-server": "^14.1.1", "http-server": "^14.1.1",
"intro.js": "^4.1.0", "intro.js": "^4.1.0",
"isbinaryfile": "^3.0.2", "isbinaryfile": "^3.0.2",
"isomorphic-git": "^1.8.2", "isomorphic-git": "^1.24.0",
"jquery": "^3.3.1", "jquery": "^3.3.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jszip": "^3.6.0", "jszip": "^3.6.0",
"just-once": "^2.2.0",
"latest-version": "^5.1.0", "latest-version": "^5.1.0",
"merge": "^2.1.1", "merge": "^2.1.1",
"npm-install-version": "^6.0.2", "npm-install-version": "^6.0.2",

@ -5131,82 +5131,82 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590"
integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ== integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==
"@remixproject/engine-electron@0.3.36": "@remixproject/engine-electron@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/engine-electron/-/engine-electron-0.3.36.tgz#24304072b102d23ea76d88dd142c28ed991aa46b" resolved "https://registry.yarnpkg.com/@remixproject/engine-electron/-/engine-electron-0.3.37.tgz#5e14bcf08201603cfccff0761d6f8612b1f3d62f"
integrity sha512-ueiO48ViKyYgZgZmMreXyEk62cSlAzwukSyXnxI3T4G5ykKL0G461ocK3cSYfTKoZ/g0/j7oUz25I8wXtU3boA== integrity sha512-XQea0a5SPtYtOl1XH8qu9AN+Fkc/LSZ8FYuxenFUeczqy9t4gxlPibLrCQXSlrcNEdoGhDE/DW/wltQoX8JLtg==
dependencies: dependencies:
"@remixproject/engine" "0.3.36" "@remixproject/engine" "0.3.37"
"@remixproject/plugin-api" "0.3.36" "@remixproject/plugin-api" "0.3.37"
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
"@remixproject/engine-web@0.3.36": "@remixproject/engine-web@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/engine-web/-/engine-web-0.3.36.tgz#105328588fee0eee692f2e64aeb3faf2749ea56c" resolved "https://registry.yarnpkg.com/@remixproject/engine-web/-/engine-web-0.3.37.tgz#cb9e09a135f9083202207039b2f5104278d4db1b"
integrity sha512-rNYZiEFplg9A3Vfb72zVxqrUE3rVFOss8uBcAhWPt0um8vl7cZ8nRoFc5pxgnvDh+ozRo5YbWR0fqmDA4OiIUw== integrity sha512-9FcJOm5xMOta5DE/JVMu2TnGcJpoe6MagG/NC89QrxJq8mCQSDgiFeSfqJhgFXye/hjlCmaO96aLgXIeoXMhWQ==
dependencies: dependencies:
"@remixproject/engine" "0.3.36" "@remixproject/engine" "0.3.37"
"@remixproject/plugin-api" "0.3.36" "@remixproject/plugin-api" "0.3.37"
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
"@remixproject/engine@0.3.36": "@remixproject/engine@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/engine/-/engine-0.3.36.tgz#aa93210716ab959b1d39e08ea38c7357495381f3" resolved "https://registry.yarnpkg.com/@remixproject/engine/-/engine-0.3.37.tgz#995abd53c505f6f37eaa6550d81ec36f2e60d106"
integrity sha512-D5sf8WJBS5cbjqZBDWo/hN5kT6YhkZysGy0Y1QWcQj48WhOYHawl6VMExMsqVXtPZzJTWHs2N/hXuoc6MF6xfg== integrity sha512-+dO32Bdgm2GLlamCnCWIPYX0v57Ft2vWGkFwXil1xiLvPttVOjnSPkqz9Xu0DAqDIqXIr4A15E2pHklVR+shLQ==
dependencies: dependencies:
"@remixproject/plugin-api" "0.3.36" "@remixproject/plugin-api" "0.3.37"
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
"@remixproject/plugin-api@0.3.36": "@remixproject/plugin-api@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/plugin-api/-/plugin-api-0.3.36.tgz#58a77184c17ddece59fc2b8e7dd30a92ddb164a2" resolved "https://registry.yarnpkg.com/@remixproject/plugin-api/-/plugin-api-0.3.37.tgz#2b5c628f81632bb0063e4c6afbce62085d20e883"
integrity sha512-T7/9FAhm499sFbOh/iuK05GRpTZwYJtXrlMUAGE9qhfo4ep62sJg0TLss1BRnfhkDBvlS10rqdF2wWTPQ9JSPA== integrity sha512-bJ8oIpaI4qP3/Ku7jxXW3XRkTyQ2hjWX6N8yob3d/jjHtaJ/IorDx/vieGXQoJTPKAMPol0KVKCVCxx+xmzcUQ==
dependencies: dependencies:
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
"@remixproject/plugin-electron@0.3.36": "@remixproject/plugin-electron@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/plugin-electron/-/plugin-electron-0.3.36.tgz#4b0067f3b42454f95625ed774a10751a6557b3ee" resolved "https://registry.yarnpkg.com/@remixproject/plugin-electron/-/plugin-electron-0.3.37.tgz#e850b6aae55af1e7b0cd74f4583e8eeb946735ce"
integrity sha512-WM4u4zUs95adZxbC/GrMzfuCXfBde9TuoPeS2Nb9ysRQKj5eRfTIWiYrh9PxoezrEF8EYPo8gVrf9A+PGlKf3w== integrity sha512-ZmkYLk9LEewPCqXIiMPJC8ZpgRjMW3dh2LbgeSGhKG0ly8pD5VuZj3/07iP/ZvjDNuTliX08gqvCru9/oOskqA==
dependencies: dependencies:
"@remixproject/engine" "0.3.36" "@remixproject/engine" "0.3.37"
"@remixproject/plugin" "0.3.36" "@remixproject/plugin" "0.3.37"
"@remixproject/plugin-api" "0.3.36" "@remixproject/plugin-api" "0.3.37"
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
"@remixproject/plugin-utils@0.3.36": "@remixproject/plugin-utils@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/plugin-utils/-/plugin-utils-0.3.36.tgz#92478cd820e23b42a911a3e6e3f88d759f34d395" resolved "https://registry.yarnpkg.com/@remixproject/plugin-utils/-/plugin-utils-0.3.37.tgz#7068523398164976c2470f55837e93c6fa210489"
integrity sha512-gbTzp3rHVITVn45vgb/f+huo6C/zogUCTmzqOj+D9INaQR1Fr6XtOL8erKjD0pNBbyw3lxpRsDKEpRQVE7sMbg== integrity sha512-Ow4gFf15ym7Sysza4hx4+QEYQHvluu0pDEj3GlNAMWAbvbjBGxb7O81b3Tvu7n8ywESYJlbIifNr++vYY63+PQ==
dependencies: dependencies:
tslib "2.0.1" tslib "2.0.1"
"@remixproject/plugin-webview@0.3.36": "@remixproject/plugin-webview@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/plugin-webview/-/plugin-webview-0.3.36.tgz#38997745608952cc40ce5c668d0836d0d5279b12" resolved "https://registry.yarnpkg.com/@remixproject/plugin-webview/-/plugin-webview-0.3.37.tgz#b38882fc8f05ba22405c0baa4bef9662652fec1a"
integrity sha512-9C/GQLDQzzX6g8MH9L54C+K/2FxBa4tCdlxUbPH9infTFvk/jcMjpd/CSAIBK4EfZCs1mL+1Opc8bX3VrfchpA== integrity sha512-LeKS05MXnVUM4xS9IUmKBE5HJzs3L5hvuqWcRosB6ASbhzzFu4h7zeDtBbjSK46dL6/3Ym/dDqdJM9e6I+Gltw==
dependencies: dependencies:
"@remixproject/plugin" "0.3.36" "@remixproject/plugin" "0.3.37"
"@remixproject/plugin-api" "0.3.36" "@remixproject/plugin-api" "0.3.37"
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
axios "^0.21.1" axios "^0.21.1"
"@remixproject/plugin-ws@0.3.36": "@remixproject/plugin-ws@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/plugin-ws/-/plugin-ws-0.3.36.tgz#a80ccf5e7ae866bc53a79a7da4f36b67a0a33972" resolved "https://registry.yarnpkg.com/@remixproject/plugin-ws/-/plugin-ws-0.3.37.tgz#1f29d037a1743eb8b3da438f8a9dbdb3d322b9a4"
integrity sha512-+vGwHd7KAgGsPoPph8T5mGkGthJagtwsb53LopZwbwDziMEWP7IVu44TcI46qR/+oBHLm0MmH1ukCzRAkbDu+w== integrity sha512-fjGBrj3qP0UnraiG/opzMySxk0SdfABrx1PuM8f3c3tmZDdjTrVcsJ11NlNNvztOPkyY5fL4e2gXDUkpELKztg==
dependencies: dependencies:
"@remixproject/plugin" "0.3.36" "@remixproject/plugin" "0.3.37"
"@remixproject/plugin-api" "0.3.36" "@remixproject/plugin-api" "0.3.37"
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
"@remixproject/plugin@0.3.36": "@remixproject/plugin@0.3.37":
version "0.3.36" version "0.3.37"
resolved "https://registry.yarnpkg.com/@remixproject/plugin/-/plugin-0.3.36.tgz#393867208ace117c8dbfd08adb4d5feec1b17d4b" resolved "https://registry.yarnpkg.com/@remixproject/plugin/-/plugin-0.3.37.tgz#d7b41a7fd03d712717eeb44526399e6c6ce6647c"
integrity sha512-+lFgfLI3vbiB6OQIkeX2Yx1EyKdIUGU1lJ7K51HwPfbGet5Y4w8qDMXBblRM1HhVe7bDYlBOzzbcil8/03Dsnw== integrity sha512-3DgGCPE78ThfqGUJlrWwdArRol2nLZMBHTZpPxLE9K4tpspY7G1KV8HLB55mkX/uPVvVbNTRoFI4TNm87Jjiwg==
dependencies: dependencies:
"@remixproject/plugin-api" "0.3.36" "@remixproject/plugin-api" "0.3.37"
"@remixproject/plugin-utils" "0.3.36" "@remixproject/plugin-utils" "0.3.37"
events "3.2.0" events "3.2.0"
"@restart/context@^2.1.4": "@restart/context@^2.1.4":
@ -18344,6 +18344,11 @@ just-once@1.1.0:
resolved "https://registry.yarnpkg.com/just-once/-/just-once-1.1.0.tgz#fe81a185ebaeeb0947a7e705bf01cb6808db0ad8" resolved "https://registry.yarnpkg.com/just-once/-/just-once-1.1.0.tgz#fe81a185ebaeeb0947a7e705bf01cb6808db0ad8"
integrity sha512-+rZVpl+6VyTilK7vB/svlMPil4pxqIJZkbnN7DKZTOzyXfun6ZiFeq2Pk4EtCEHZ0VU4EkdFzG8ZK5F3PErcDw== integrity sha512-+rZVpl+6VyTilK7vB/svlMPil4pxqIJZkbnN7DKZTOzyXfun6ZiFeq2Pk4EtCEHZ0VU4EkdFzG8ZK5F3PErcDw==
just-once@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/just-once/-/just-once-2.2.0.tgz#e8ebc8c80838d0fdb0d3c888f1591d88ad4be4d7"
integrity sha512-Wo547FgUOUZ98jbrZ1KX8nRezdEdtgIlC2NK1u1RvR1oZ/WoU++FjprP8J8hRbaox776MHyeMZZED4DvhhHVjg==
keccak@^3.0.0: keccak@^3.0.0:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0"

Loading…
Cancel
Save