hometab & clone

rdesktop^2
filip mertens 1 year ago
parent f542fd8d1b
commit 1d36d50ab6
  1. 22
      apps/remix-ide/src/app.js
  2. 6
      apps/remix-ide/src/app/files/dgitProvider.js
  3. 46
      apps/remix-ide/src/app/files/electronProvider.ts
  4. 2
      apps/remix-ide/src/app/files/fileManager.ts
  5. 1
      apps/remix-ide/src/app/files/remixDProvider.js
  6. 2
      apps/remix-ide/src/app/panels/file-panel.js
  7. 0
      apps/remix-ide/src/app/plugins/electron/electronConfigPlugin.ts
  8. 1
      apps/remix-ide/src/app/plugins/electron/fsPlugin.ts
  9. 0
      apps/remix-ide/src/app/plugins/electron/isoGitPlugin.ts
  10. 15
      apps/remix-ide/src/app/plugins/electron/templatesPlugin.ts
  11. 31
      apps/remix-ide/src/app/plugins/remix-templates.ts
  12. 2
      apps/remix-ide/src/remixEngine.js
  13. 1
      apps/remixdesktop/package.json
  14. 15
      apps/remixdesktop/src/engine.ts
  15. 9
      apps/remixdesktop/src/main.ts
  16. 10
      apps/remixdesktop/src/menus/commands.ts
  17. 17
      apps/remixdesktop/src/menus/darwin.ts
  18. 8
      apps/remixdesktop/src/menus/file.ts
  19. 20
      apps/remixdesktop/src/menus/git.ts
  20. 58
      apps/remixdesktop/src/plugins/fsPlugin.ts
  21. 2
      apps/remixdesktop/src/plugins/gitPlugin.ts
  22. 97
      apps/remixdesktop/src/plugins/isoGitPlugin.ts
  23. 75
      apps/remixdesktop/src/plugins/templates.ts
  24. 2
      apps/remixdesktop/src/plugins/xtermPlugin.ts
  25. 2
      apps/remixdesktop/src/preload.ts
  26. 2807
      apps/remixdesktop/yarn.lock
  27. 34
      libs/remix-ui/home-tab/src/lib/components/homeTabFileElectron.tsx
  28. 7
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  29. 6
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  30. 43
      libs/remix-ui/workspace/src/lib/actions/index.ts
  31. 124
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  32. 12
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx

@ -45,9 +45,13 @@ import { FileDecorator } from './app/plugins/file-decorator'
import { CodeFormat } from './app/plugins/code-format' import { CodeFormat } from './app/plugins/code-format'
import { SolidityUmlGen } from './app/plugins/solidity-umlgen' import { SolidityUmlGen } from './app/plugins/solidity-umlgen'
import { ContractFlattener } from './app/plugins/contractFlattener' import { ContractFlattener } from './app/plugins/contractFlattener'
import { fsPlugin } from './app/plugins/fsPlugin' import { TemplatesPlugin } from './app/plugins/remix-templates'
import { isoGitPlugin } from './app/plugins/isoGitPlugin' import { fsPlugin } from './app/plugins/electron/fsPlugin'
import { electronConfig } from './app/plugins/electronConfigPlugin' import { isoGitPlugin } from './app/plugins/electron/isoGitPlugin'
import { electronConfig } from './app/plugins/electron/electronConfigPlugin'
import { electronTemplates } from './app/plugins/electron/templatesPlugin'
const isElectron = require('is-electron') const isElectron = require('is-electron')
@ -116,7 +120,7 @@ class AppComponent {
name: 'fileproviders/workspace' name: 'fileproviders/workspace'
}) })
this._components.filesProviders.electron = new ElectronProvider() this._components.filesProviders.electron = new ElectronProvider(this.appManager)
Registry.getInstance().put({ Registry.getInstance().put({
api: this._components.filesProviders.electron, api: this._components.filesProviders.electron,
name: 'fileproviders/electron' name: 'fileproviders/electron'
@ -197,7 +201,8 @@ class AppComponent {
//----- search //----- search
const search = new SearchPlugin() const search = new SearchPlugin()
//---- templates
const templates = new TemplatesPlugin()
//---------------- Solidity UML Generator ------------------------- //---------------- Solidity UML Generator -------------------------
const solidityumlgen = new SolidityUmlGen(appManager) const solidityumlgen = new SolidityUmlGen(appManager)
@ -322,6 +327,7 @@ class AppComponent {
solidityumlgen, solidityumlgen,
contractFlattener, contractFlattener,
solidityScript, solidityScript,
templates
]) ])
//---- fs plugin //---- fs plugin
@ -332,6 +338,8 @@ class AppComponent {
this.engine.register([isoGit]) this.engine.register([isoGit])
const electronConfigPlugin = new electronConfig() const electronConfigPlugin = new electronConfig()
this.engine.register([electronConfigPlugin]) this.engine.register([electronConfigPlugin])
const templatesPlugin = new electronTemplates()
this.engine.register([templatesPlugin])
} }
// LAYOUT & SYSTEM VIEWS // LAYOUT & SYSTEM VIEWS
@ -447,10 +455,10 @@ class AppComponent {
await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'codeParser', 'codeFormatter', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler']) await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'codeParser', 'codeFormatter', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler'])
await this.appManager.activatePlugin(['settings']) await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder']) await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder'])
await this.appManager.activatePlugin(['solidity-script']) await this.appManager.activatePlugin(['solidity-script', 'remix-templates'])
if(isElectron()){ if(isElectron()){
await this.appManager.activatePlugin(['fs', 'isogit', 'electronconfig']) await this.appManager.activatePlugin(['fs', 'isogit', 'electronconfig', 'electronTemplates'])
} }
this.appManager.on( this.appManager.on(

@ -366,7 +366,7 @@ class DGitProvider extends Plugin {
async clone(input, workspaceName, workspaceExists = false) { async clone(input, workspaceName, workspaceExists = false) {
if (isElectron()) { if (isElectron()) {
const folder = await this.call('isogit', 'openFolder') const folder = await this.call('fs', 'selectFolder')
if (!folder) return false if (!folder) return false
console.log('folder', folder) console.log('folder', folder)
const cmd = { const cmd = {
@ -378,8 +378,8 @@ class DGitProvider extends Plugin {
input input
} }
const result = await this.call('isogit', 'clone', cmd) const result = await this.call('isogit', 'clone', cmd)
await this.call('fs', 'openWindow', folder) this.call('fs', 'openWindow', folder)
return result
} else { } else {
const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.') const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.')
if (!permission) return false if (!permission) return false

@ -7,18 +7,45 @@ declare global {
} }
export class ElectronProvider extends FileProvider { export class ElectronProvider extends FileProvider {
constructor () { constructor(appManager) {
super('') super('')
this._appManager = appManager
}
async init() {
this._appManager.on('fs', 'change', (event, path) => {
console.log('change', event, path)
switch (event) {
case 'add':
this.event.emit('fileAdded', path)
break
case 'unlink':
this.event.emit('fileRemoved', path)
break
case 'change':
this.event.emit('fileChanged', path)
break
case 'rename':
this.event.emit('fileRenamed', path)
break
case 'addDir':
this.event.emit('folderAdded', path)
break
case 'unlinkDir':
this.event.emit('fileRemoved', path)
}
})
} }
// isDirectory is already included // isDirectory is already included
// this is a more efficient version of the default implementation // this is a more efficient version of the default implementation
async resolveDirectory (path, cb) { async resolveDirectory(path, cb) {
path = this.removePrefix(path) path = this.removePrefix(path)
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 ELECTRON') console.log(files.length, 'files resolveDirectory ELECTRON')
const ret = {} const ret = {}
if (files) { if (files) {
for (let element of files) { for (let element of files) {
@ -37,4 +64,17 @@ export class ElectronProvider extends FileProvider {
} }
} }
/**
* Removes the folder recursively
* @param {*} path is the folder to be removed
*/
async remove(path) {
console.log('remove', path)
try {
await window.remixFileSystem.rmdir(path)
} catch (error) {
console.log(error)
}
}
} }

@ -24,7 +24,7 @@ const profile = {
permission: true, permission: true,
version: packageJson.version, version: packageJson.version,
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir',
'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'selectFolder', 'setFile', 'switchFile', 'refresh',
'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles', 'isGitRepo'], 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles', 'isGitRepo'],
kind: 'file-system' kind: 'file-system'
} }

@ -26,6 +26,7 @@ module.exports = class RemixDProvider extends FileProvider {
}) })
this._appManager.on('remixd', 'fileAdded', (path) => { this._appManager.on('remixd', 'fileAdded', (path) => {
console.log('fileAdded remixd', path)
this.event.emit('fileAdded', path) this.event.emit('fileAdded', path)
}) })

@ -30,7 +30,7 @@ const { SlitherHandle } = require('../files/slither-handle.js')
const profile = { const profile = {
name: 'filePanel', name: 'filePanel',
displayName: 'File explorer', displayName: 'File explorer',
methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getAvailableWorkspaceName', 'getWorkspaces', 'createWorkspace', 'setWorkspace', 'registerContextMenuItem', 'renameWorkspace', 'deleteWorkspace'], methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getAvailableWorkspaceName', 'getWorkspaces', 'createWorkspace', 'setWorkspace', 'registerContextMenuItem', 'renameWorkspace', 'deleteWorkspace', 'loadTemplate', 'clone'],
events: ['setWorkspace', 'workspaceRenamed', 'workspaceDeleted', 'workspaceCreated'], events: ['setWorkspace', 'workspaceRenamed', 'workspaceDeleted', 'workspaceCreated'],
icon: 'assets/img/fileManager.webp', icon: 'assets/img/fileManager.webp',
description: 'Remix IDE file explorer', description: 'Remix IDE file explorer',

@ -43,7 +43,6 @@ export class fsPlugin extends ElectronPlugin {
rmdir: async (path: string) => { rmdir: async (path: string) => {
path = fixPath(path) path = fixPath(path)
return await this.call('fs', 'rmdir', path) return await this.call('fs', 'rmdir', path)
}, },
readdir: async (path: string) => { readdir: async (path: string) => {
path = fixPath(path) path = fixPath(path)

@ -0,0 +1,15 @@
import { ElectronPlugin } from '@remixproject/engine-electron';
export class electronTemplates extends ElectronPlugin {
constructor() {
super({
displayName: 'electronTemplates',
name: 'electronTemplates',
description: 'templates',
})
}
onActivation(): void {
}
}

@ -0,0 +1,31 @@
import { Plugin } from '@remixproject/engine'
import * as templateWithContent from '@remix-project/remix-ws-templates'
const profile = {
name: 'remix-templates',
displayName: 'remix-templates',
description: 'Remix Templates plugin',
methods: ['getTemplate', 'loadTemplateInNewWindow'],
}
export class TemplatesPlugin extends Plugin {
constructor() {
super(profile)
}
async getTemplate (template: string, opts?: any) {
const templateList = Object.keys(templateWithContent)
if (!templateList.includes(template)) return
// @ts-ignore
const files = await templateWithContent[template](opts)
console.log('files for template ', files)
return files
}
// electron only method
async loadTemplateInNewWindow (template: string, opts?: any) {
const files = await this.getTemplate(template, opts)
this.call('electronTemplates', 'loadTemplateInNewWindow', files)
}
}

@ -20,6 +20,8 @@ export class RemixEngine extends Engine {
if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 } if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 }
if (name === 'walletconnect') return { queueTimeout: 60000 * 4 } if (name === 'walletconnect') return { queueTimeout: 60000 * 4 }
if (name === 'udapp') return { queueTimeout: 60000 * 4 } if (name === 'udapp') return { queueTimeout: 60000 * 4 }
if (name === 'fs') return { queueTimeout: 60000 * 4 }
if (name === 'isogit') return { queueTimeout: 60000 * 4 }
return { queueTimeout: 10000 } return { queueTimeout: 10000 }
} }

@ -22,6 +22,7 @@
"typescript": "^5.1.3" "typescript": "^5.1.3"
}, },
"dependencies": { "dependencies": {
"@remix-project/remix-ws-templates": "^1.0.19",
"electron-is-packaged": "^1.0.2", "electron-is-packaged": "^1.0.2",
"glob": "^10.2.7", "glob": "^10.2.7",
"node-pty": "^0.10.1" "node-pty": "^0.10.1"

@ -7,6 +7,7 @@ import { XtermPlugin } from './plugins/xtermPlugin';
import git from 'isomorphic-git' import git from 'isomorphic-git'
import { IsoGitPlugin } from './plugins/isoGitPlugin'; import { IsoGitPlugin } from './plugins/isoGitPlugin';
import { ConfigPlugin } from './plugins/configPlugin'; import { ConfigPlugin } from './plugins/configPlugin';
import { TemplatesPlugin } from './plugins/templates';
const engine = new Engine() const engine = new Engine()
const appManager = new PluginManager() const appManager = new PluginManager()
@ -15,12 +16,14 @@ const gitPlugin = new GitPlugin()
const xtermPlugin = new XtermPlugin() const xtermPlugin = new XtermPlugin()
const isoGitPlugin = new IsoGitPlugin() const isoGitPlugin = new IsoGitPlugin()
const configPlugin = new ConfigPlugin() const configPlugin = new ConfigPlugin()
const templatesPlugin = new TemplatesPlugin()
engine.register(appManager) engine.register(appManager)
engine.register(fsPlugin) engine.register(fsPlugin)
engine.register(gitPlugin) engine.register(gitPlugin)
engine.register(xtermPlugin) engine.register(xtermPlugin)
engine.register(isoGitPlugin) engine.register(isoGitPlugin)
engine.register(configPlugin) engine.register(configPlugin)
engine.register(templatesPlugin)
appManager.activatePlugin('electronconfig') appManager.activatePlugin('electronconfig')
appManager.activatePlugin('fs') appManager.activatePlugin('fs')
@ -36,6 +39,18 @@ ipcMain.on('fs:openFolder', async (event) => {
fsPlugin.openFolder(event) fsPlugin.openFolder(event)
}) })
ipcMain.on('template:open', async (event) => {
console.log('template:open', event)
templatesPlugin.openTemplate(event)
})
ipcMain.on('git:startclone', async (event) => {
console.log('git:startclone', event)
isoGitPlugin.startClone(event)
})
ipcMain.handle('getWebContentsID', (event, message) => { ipcMain.handle('getWebContentsID', (event, message) => {
return event.sender.id return event.sender.id
}) })

@ -46,10 +46,7 @@ export const createWindow = async (dir?: string): Promise<void> => {
}) })
windowSet.add(mainWindow) windowSet.add(mainWindow)
//mainWindow.webContents.openDevTools();
// Open the DevTools.
mainWindow.webContents.openDevTools();
}; };
// This method will be called when Electron has finished // This method will be called when Electron has finished
@ -97,6 +94,7 @@ import MainMenu from './menus/main';
import darwinMenu from './menus/darwin'; import darwinMenu from './menus/darwin';
import WindowMenu from './menus/window'; import WindowMenu from './menus/window';
import EditMenu from './menus/edit'; import EditMenu from './menus/edit';
import GitMenu from './menus/git';
import { execCommand } from './menus/commands'; import { execCommand } from './menus/commands';
const commandKeys: Record<string, string> = { const commandKeys: Record<string, string> = {
@ -107,7 +105,8 @@ const commandKeys: Record<string, string> = {
const menu = [...(process.platform === 'darwin' ? [darwinMenu(commandKeys, execCommand, showAbout)] : []), const menu = [...(process.platform === 'darwin' ? [darwinMenu(commandKeys, execCommand, showAbout)] : []),
FileMenu(commandKeys, execCommand), FileMenu(commandKeys, execCommand),
EditMenu(commandKeys, execCommand), EditMenu(commandKeys, execCommand),
WindowMenu(commandKeys, execCommand, []) WindowMenu(commandKeys, execCommand, []),
GitMenu(commandKeys, execCommand)
] ]
Menu.setApplicationMenu(Menu.buildFromTemplate(menu)) Menu.setApplicationMenu(Menu.buildFromTemplate(menu))

@ -11,6 +11,16 @@ const commands: Record<string, (focusedWindow?: BrowserWindow) => void> = {
if (focusedWindow) { if (focusedWindow) {
ipcMain.emit('fs:openFolder', focusedWindow.webContents.id); ipcMain.emit('fs:openFolder', focusedWindow.webContents.id);
} }
},
'template:open': (focusedWindow) => {
if (focusedWindow) {
ipcMain.emit('template:open', focusedWindow.webContents.id);
}
},
'git:startclone': (focusedWindow) => {
if (focusedWindow) {
ipcMain.emit('git:startclone', focusedWindow.webContents.id);
}
} }
}; };

@ -19,23 +19,6 @@ export default (
{ {
type: 'separator' type: 'separator'
}, },
{
label: 'Preferences...',
accelerator: commandKeys['window:preferences'],
click() {
execCommand('window:preferences');
}
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{ {
role: 'hide' role: 'hide'
}, },

@ -22,7 +22,13 @@ export default (
click(item, focusedWindow) { click(item, focusedWindow) {
execCommand('folder:open', focusedWindow); execCommand('folder:open', focusedWindow);
} }
} },
{
label: 'Load Template in New Window',
click(item, focusedWindow) {
execCommand('template:open', focusedWindow);
}
},
] ]
}; };
}; };

@ -0,0 +1,20 @@
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: 'Git',
submenu: [
{
label: 'Clone Repository in New Window',
click(item, focusedWindow) {
execCommand('git:startclone', focusedWindow);
}
}
]
};
};

@ -72,7 +72,7 @@ const clientProfile: Profile = {
name: 'fs', name: 'fs',
displayName: 'fs', displayName: 'fs',
description: 'fs', description: 'fs',
methods: ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'lstat', 'exists', 'currentPath', 'watch', 'closeWatch', 'setWorkingDir', 'openFolder', 'getRecentFolders', 'glob', 'openWindow'] methods: ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'lstat', 'exists', 'currentPath', 'watch', 'closeWatch', 'setWorkingDir', 'openFolder', 'getRecentFolders', 'glob', 'openWindow', 'selectFolder']
} }
class FSPluginClient extends ElectronBasePluginClient { class FSPluginClient extends ElectronBasePluginClient {
@ -83,11 +83,12 @@ class FSPluginClient extends ElectronBasePluginClient {
super(webContentsId, profile) super(webContentsId, profile)
this.onload(() => { this.onload(() => {
//console.log('fsPluginClient onload') //console.log('fsPluginClient onload')
this.window.webContents.openDevTools()
this.window.on('close', async () => { this.window.on('close', async () => {
console.log('close', this.webContentsId) console.log('close', this.webContentsId)
await this.removeFromOpenedFolders(this.workingDir) await this.removeFromOpenedFolders(this.workingDir)
}) })
}) })
} }
@ -96,9 +97,11 @@ class FSPluginClient extends ElectronBasePluginClient {
// call node fs.readdir // call node fs.readdir
console.log('readdir', path) console.log('readdir', path)
if (!path) return [] if (!path) return []
const files = await fs.readdir(this.fixPath(path),{ const startTime = Date.now()
const files = await fs.readdir(this.fixPath(path), {
withFileTypes: true withFileTypes: true
}) })
const result: any[] = [] const result: any[] = []
for (const file of files) { for (const file of files) {
const isDirectory = file.isDirectory() const isDirectory = file.isDirectory()
@ -107,11 +110,12 @@ class FSPluginClient extends ElectronBasePluginClient {
isDirectory isDirectory
}) })
} }
console.log('readdir', path, Date.now() - startTime)
return result return result
} }
async glob(path: string, pattern: string, options?: GlobOptions): Promise<string[] | Path[]> { async glob(path: string, pattern: string, options?: GlobOptions): Promise<string[] | Path[]> {
path = this.fixPath(path) path = this.fixPath(path)
console.log('glob', path, pattern, options) console.log('glob', path, pattern, options)
const files = await glob(path + pattern, { const files = await glob(path + pattern, {
@ -122,10 +126,10 @@ class FSPluginClient extends ElectronBasePluginClient {
for (const file of files) { for (const file of files) {
let pathWithoutWorkingDir = (file as Path).path.replace(this.workingDir, '') let pathWithoutWorkingDir = (file as Path).path.replace(this.workingDir, '')
if(!pathWithoutWorkingDir.endsWith('/')){ if (!pathWithoutWorkingDir.endsWith('/')) {
pathWithoutWorkingDir = pathWithoutWorkingDir + '/' pathWithoutWorkingDir = pathWithoutWorkingDir + '/'
} }
if(pathWithoutWorkingDir.startsWith('/')){ if (pathWithoutWorkingDir.startsWith('/')) {
pathWithoutWorkingDir = pathWithoutWorkingDir.slice(1) pathWithoutWorkingDir = pathWithoutWorkingDir.slice(1)
} }
result.push({ result.push({
@ -133,7 +137,7 @@ class FSPluginClient extends ElectronBasePluginClient {
isDirectory: (file as Path).isDirectory(), isDirectory: (file as Path).isDirectory(),
}) })
} }
console.log('glob', result) //console.log('glob', result)
return result return result
} }
@ -159,7 +163,10 @@ class FSPluginClient extends ElectronBasePluginClient {
} }
async rmdir(path: string): Promise<void> { async rmdir(path: string): Promise<void> {
return fs.rmdir(this.fixPath(path)) console.log('rmdir', this.fixPath(path))
return fs.rm(this.fixPath(path), {
recursive: true
})
} }
async unlink(path: string): Promise<void> { async unlink(path: string): Promise<void> {
@ -209,11 +216,25 @@ class FSPluginClient extends ElectronBasePluginClient {
return process.cwd() return process.cwd()
} }
async watch(path: string): Promise<void> { async watch(): Promise<void> {
if (this.watcher) this.watcher.close() if (this.watcher) this.watcher.close()
console.log('watch', this.workingDir)
this.watcher = this.watcher =
chokidar.watch(this.fixPath(path)).on('change', (path, stats) => { chokidar.watch(this.workingDir, {
this.emit('change', path, stats) ignorePermissionErrors: true, ignoreInitial: true,
ignored: [
'**/node_modules/**',
'**/.git/**',
]
}).on('all', (eventName, path, stats) => {
console.log('change', eventName, path, stats)
// remove workingDir from path
path = path.replace(this.workingDir, '')
try {
this.emit('change', eventName, path)
} catch (e) {
console.log('error emitting change', e)
}
}) })
} }
@ -253,6 +274,19 @@ class FSPluginClient extends ElectronBasePluginClient {
} }
async selectFolder(path?: string): Promise<string> {
let dirs: string[] | undefined
if (!path) {
dirs = dialog.showOpenDialogSync(this.window, {
properties: ['openDirectory', 'createDirectory', "showHiddenFiles"]
})
}
path = dirs && dirs.length && dirs[0] ? dirs[0] : path
if (!path) return ''
return path
}
async openFolder(path?: string): Promise<void> { async openFolder(path?: string): Promise<void> {
let dirs: string[] | undefined let dirs: string[] | undefined
@ -268,6 +302,7 @@ class FSPluginClient extends ElectronBasePluginClient {
await this.updateOpenedFolders(path) await this.updateOpenedFolders(path)
this.window.setTitle(this.workingDir) this.window.setTitle(this.workingDir)
console.log('setWorkingDir', path) console.log('setWorkingDir', path)
this.watch()
this.emit('workingDirChanged', path) this.emit('workingDirChanged', path)
} }
@ -277,6 +312,7 @@ class FSPluginClient extends ElectronBasePluginClient {
await this.updateRecentFolders(path) await this.updateRecentFolders(path)
await this.updateOpenedFolders(path) await this.updateOpenedFolders(path)
this.window.setTitle(this.workingDir) this.window.setTitle(this.workingDir)
this.watch()
this.emit('workingDirChanged', path) this.emit('workingDirChanged', path)
} }

@ -9,7 +9,7 @@ const profile: Profile = {
} }
export class GitPlugin extends ElectronBasePlugin { export class GitPlugin extends ElectronBasePlugin {
client: PluginClient clients: GitPluginClient[] = []
constructor() { constructor() {
super(profile, clientProfile, GitPluginClient) super(profile, clientProfile, GitPluginClient)
} }

@ -13,13 +13,20 @@ const profile: Profile = {
} }
export class IsoGitPlugin extends ElectronBasePlugin { export class IsoGitPlugin extends ElectronBasePlugin {
client: PluginClient clients: IsoGitPluginClient[] = []
constructor() { constructor() {
super(profile, clientProfile, IsoGitPluginClient) super(profile, clientProfile, IsoGitPluginClient)
} }
startClone(webContentsId: any): void {
const client = this.clients.find(c => c.webContentsId === webContentsId)
if (client) {
client.startClone()
}
}
} }
const parseInput = (input: any)=> { const parseInput = (input: any) => {
return { return {
corsProxy: 'https://corsproxy.remixproject.org/', corsProxy: 'https://corsproxy.remixproject.org/',
http, http,
@ -51,30 +58,30 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
console.log('workingDirChanged', path) console.log('workingDirChanged', path)
this.workingDir = path this.workingDir = path
await this.status({ await this.status({
}) })
}) })
}) })
} }
async getGitConfig () { async getGitConfig() {
return { return {
fs, fs,
dir: this.workingDir, dir: this.workingDir,
} }
} }
async status (cmd: any) { async status(cmd: any) {
console.log('status') console.log('status')
const status = await git.statusMatrix({ const status = await git.statusMatrix({
...await this.getGitConfig(), ...await this.getGitConfig(),
...cmd ...cmd
}) })
console.log('STATUS', status, await this.getGitConfig()) //console.log('STATUS', status, await this.getGitConfig())
return status return status
} }
async log (cmd: any) { async log(cmd: any) {
console.log('log') console.log('log')
const log = await git.log({ const log = await git.log({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -84,7 +91,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return log return log
} }
async add (cmd: any) { async add(cmd: any) {
console.log('add') console.log('add')
const add = await git.add({ const add = await git.add({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -104,7 +111,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return rm return rm
} }
async commit (cmd: any) { async commit(cmd: any) {
console.log('commit') console.log('commit')
const commit = await git.commit({ const commit = await git.commit({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -114,14 +121,14 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return commit return commit
} }
async init (input: any) { async init(input: any) {
await git.init({ await git.init({
...await this.getGitConfig(), ...await this.getGitConfig(),
defaultBranch: (input && input.branch) || 'main' defaultBranch: (input && input.branch) || 'main'
}) })
} }
async branch (cmd: any) { async branch(cmd: any) {
console.log('branch') console.log('branch')
const branch = await git.branch({ const branch = await git.branch({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -131,7 +138,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return branch return branch
} }
async lsfiles (cmd: any) { async lsfiles(cmd: any) {
console.log('lsfiles') console.log('lsfiles')
const lsfiles = await git.listFiles({ const lsfiles = await git.listFiles({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -141,7 +148,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return lsfiles return lsfiles
} }
async resolveref (cmd: any) { async resolveref(cmd: any) {
console.log('resolveref') console.log('resolveref')
const resolveref = await git.resolveRef({ const resolveref = await git.resolveRef({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -152,7 +159,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
} }
async readblob (cmd: any) { async readblob(cmd: any) {
console.log('readblob') console.log('readblob')
const readblob = await git.readBlob({ const readblob = await git.readBlob({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -162,7 +169,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return readblob return readblob
} }
async checkout (cmd: any) { async checkout(cmd: any) {
console.log('checkout') console.log('checkout')
const checkout = await git.checkout({ const checkout = await git.checkout({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -172,7 +179,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return checkout return checkout
} }
async push (cmd: any) { async push(cmd: any) {
console.log('push') console.log('push')
const push = await git.push({ const push = await git.push({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -183,7 +190,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return push return push
} }
async pull (cmd: any) { async pull(cmd: any) {
console.log('pull', cmd) console.log('pull', cmd)
const pull = await git.pull({ const pull = await git.pull({
...await this.getGitConfig(), ...await this.getGitConfig(),
@ -207,26 +214,19 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
async clone(cmd: any) { async clone(cmd: any) {
console.log('clone') console.log('clone')
const clone = await git.clone({ try {
...await this.getGitConfig(), const clone = await git.clone({
...cmd, ...await this.getGitConfig(),
...parseInput(cmd.input), ...cmd,
dir: cmd.dir || this.workingDir ...parseInput(cmd.input),
}) dir: cmd.dir || this.workingDir
console.log('CLONE', clone)
return clone
}
async openFolder(path?: string): Promise<string> {
let dirs: string[] | undefined
if (!path) {
dirs = dialog.showOpenDialogSync(this.window, {
properties: ['openDirectory', 'createDirectory', "showHiddenFiles"]
}) })
console.log('CLONE', clone)
return clone
} catch (e) {
console.log('CLONE ERROR', e)
throw e
} }
path = dirs && dirs.length && dirs[0] ? dirs[0] : path
if (!path) return ''
return path
} }
async addremote(cmd: any) { async addremote(cmd: any) {
@ -248,12 +248,12 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
console.log('DELREMOTE', delremote) console.log('DELREMOTE', delremote)
return delremote return delremote
} }
remotes = async () => { remotes = async () => {
let remotes = [] let remotes = []
remotes = await git.listRemotes({...await this.getGitConfig() }) remotes = await git.listRemotes({ ...await this.getGitConfig() })
console.log('remotes', remotes) console.log('remotes', remotes)
return remotes return remotes
} }
@ -272,7 +272,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
async branches() { async branches() {
try { try {
let cmd: any = {...await this.getGitConfig()} let cmd: any = { ...await this.getGitConfig() }
const remotes = await this.remotes() const remotes = await this.remotes()
let branches = [] let branches = []
branches = (await git.listBranches(cmd)).map((branch) => { return { remote: undefined, name: branch } }) branches = (await git.listBranches(cmd)).map((branch) => { return { remote: undefined, name: branch } })
@ -281,10 +281,10 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
...cmd, ...cmd,
remote: remote.remote remote: remote.remote
} }
const remotebranches = (await git.listBranches(cmd)).map((branch) => { return { remote: remote.remote, name: branch } }) const remotebranches = (await git.listBranches(cmd)).map((branch) => { return { remote: remote.remote, name: branch } })
branches = [...branches, ...remotebranches] branches = [...branches, ...remotebranches]
} }
console.log('branches', branches) console.log('branches', branches)
return branches return branches
@ -293,11 +293,10 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
} }
} }
async startClone() {
this.call('filePanel' as any, 'clone')
}
} }

@ -0,0 +1,75 @@
import { PluginClient } from "@remixproject/plugin";
import { Profile } from "@remixproject/plugin-utils";
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import * as templateWithContent from '@remix-project/remix-ws-templates'
import fs from 'fs/promises'
import { createWindow } from "../main";
import path from 'path'
const profile: Profile = {
name: 'electronTemplates',
displayName: 'electronTemplates',
description: 'Templates plugin',
}
export class TemplatesPlugin extends ElectronBasePlugin {
clients: TemplatesPluginClient[] = []
constructor() {
super(profile, clientProfile, TemplatesPluginClient)
}
openTemplate(webContentsId: any): void {
const client = this.clients.find(c => c.webContentsId === webContentsId)
if (client) {
client.openTemplate()
}
}
}
const clientProfile: Profile = {
name: 'electronTemplates',
displayName: 'electronTemplates',
description: 'Templates plugin',
methods: ['loadTemplateInNewWindow', 'openTemplate'],
}
export type WorkspaceTemplate = 'gist-template' | 'code-template' | 'remixDefault' | 'blank' | 'ozerc20' | 'zeroxErc20' | 'ozerc721'
class TemplatesPluginClient extends ElectronBasePluginClient {
constructor(webContentsId: number, profile: Profile) {
super(webContentsId, profile)
this.onload(() => {
console.log('TemplatesPluginClient onload')
})
}
async loadTemplateInNewWindow (files: any) {
let folder = await this.call('fs' as any, 'selectFolder')
if (!folder || folder === '') return
// @ts-ignore
console.log('files for template ', files)
for (const file in files) {
try {
if(!folder.endsWith('/')) folder += '/'
console.log('writing file', folder + file)
await fs.mkdir(path.dirname(folder + file), { recursive: true})
await fs.writeFile(folder + file, files[file], {
encoding: 'utf8'
})
} catch (error) {
console.error(error)
}
}
createWindow(folder)
}
async openTemplate(){
this.call('filePanel' as any, 'loadTemplate')
}
}

@ -12,7 +12,7 @@ const profile: Profile = {
} }
export class XtermPlugin extends ElectronBasePlugin { export class XtermPlugin extends ElectronBasePlugin {
client: PluginClient clients: XtermPluginClient[] = []
constructor() { constructor() {
super(profile, clientProfile, XtermPluginClient) super(profile, clientProfile, XtermPluginClient)
} }

@ -6,7 +6,7 @@ console.log('preload.ts')
/* preload script needs statically defined API for each plugin */ /* preload script needs statically defined API for each plugin */
const exposedPLugins = ['fs', 'git', 'xterm', 'isogit', 'electronconfig'] const exposedPLugins = ['fs', 'git', 'xterm', 'isogit', 'electronconfig', 'electronTemplates']
console.log('preload.ts', process) console.log('preload.ts', process)
let webContentsId: number | undefined let webContentsId: number | undefined

File diff suppressed because it is too large Load Diff

@ -0,0 +1,34 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useRef, useReducer } from 'react'
import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
const _paq = window._paq = window._paq || [] // eslint-disable-line
import { CustomTooltip } from '@remix-ui/helper';
interface HomeTabFileProps {
plugin: any
}
export const HomeTabFileElectron = ({ plugin }: HomeTabFileProps) => {
const loadTemplate = async () => {
plugin.call('filePanel', 'loadTemplate')
}
const clone = async () => {
plugin.call('filePanel', 'clone')
}
return (
<div className="justify-content-start mt-1 p-2 d-flex flex-column" id="hTFileSection">
<label style={{ fontSize: "1.2rem" }}><FormattedMessage id='home.files' /></label>
<label style={{ fontSize: "0.8rem" }} className="pt-2"><FormattedMessage id='home.loadFrom' /></label>
<div className="d-flex">
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={async () => await loadTemplate()}>Project Template</button>
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={async () => await clone()}>Clone a Git Repository</button>
</div>
</div>
)
}

@ -7,6 +7,7 @@ import Carousel from 'react-multi-carousel'
import WorkspaceTemplate from './workspaceTemplate' import WorkspaceTemplate from './workspaceTemplate'
import 'react-multi-carousel/lib/styles.css' import 'react-multi-carousel/lib/styles.css'
import CustomNavButtons from './customNavButtons' import CustomNavButtons from './customNavButtons'
import isElectron from 'is-electron'
declare global { declare global {
interface Window { interface Window {
_paq: any _paq: any
@ -58,6 +59,12 @@ function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) {
} }
const createWorkspace = async (templateName) => { const createWorkspace = async (templateName) => {
if(isElectron()){
await plugin.call('remix-templates', 'loadTemplateInNewWindow', templateName)
return
}
await plugin.appManager.activatePlugin('filePanel') await plugin.appManager.activatePlugin('filePanel')
const timeStamp = Date.now() const timeStamp = Date.now()
let templateDisplayName = TEMPLATE_NAMES[templateName] let templateDisplayName = TEMPLATE_NAMES[templateName]

@ -9,6 +9,8 @@ import HomeTabScamAlert from './components/homeTabScamAlert'
import HomeTabGetStarted from './components/homeTabGetStarted' import HomeTabGetStarted from './components/homeTabGetStarted'
import HomeTabFeatured from './components/homeTabFeatured' import HomeTabFeatured from './components/homeTabFeatured'
import HomeTabFeaturedPlugins from './components/homeTabFeaturedPlugins' import HomeTabFeaturedPlugins from './components/homeTabFeaturedPlugins'
import isElectron from 'is-electron'
import { HomeTabFileElectron } from './components/homeTabFileElectron'
declare global { declare global {
interface Window { interface Window {
@ -50,7 +52,9 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<div className='d-flex flex-row w-100'> <div className='d-flex flex-row w-100'>
<div className="px-2 pl-3 justify-content-start d-flex border-right flex-column" id="remixUIHTLeft" style={{ width: 'inherit' }}> <div className="px-2 pl-3 justify-content-start d-flex border-right flex-column" id="remixUIHTLeft" style={{ width: 'inherit' }}>
<HomeTabTitle /> <HomeTabTitle />
<HomeTabFile plugin={plugin} /> {!isElectron()?
<HomeTabFile plugin={plugin} />:
<HomeTabFileElectron plugin={plugin}></HomeTabFileElectron>}
<HomeTabLearn plugin={plugin} /> <HomeTabLearn plugin={plugin} />
</div> </div>
<div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{width: "65%"}} id="remixUIHTRight"> <div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{width: "65%"}} id="remixUIHTRight">

@ -8,7 +8,7 @@ import { createWorkspaceTemplate, getWorkspaces, loadWorkspacePreset, setPlugin,
import { QueryParams } from '@remix-project/remix-lib' import { QueryParams } from '@remix-project/remix-lib'
import { fetchContractFromEtherscan } from '@remix-project/core-plugin' // eslint-disable-line import { fetchContractFromEtherscan } from '@remix-project/core-plugin' // eslint-disable-line
import JSZip from 'jszip' import JSZip from 'jszip'
import isElectron from 'is-electron' import isElectron from 'is-electron'
export * from './events' export * from './events'
export * from './workspace' export * from './workspace'
@ -55,7 +55,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
const electrOnProvider = filePanelPlugin.fileProviders.electron const electrOnProvider = filePanelPlugin.fileProviders.electron
const params = queryParams.get() as UrlParametersType const params = queryParams.get() as UrlParametersType
let workspaces = [] let workspaces = []
if (!isElectron()){ if (!isElectron()) {
workspaces = await getWorkspaces() || [] workspaces = await getWorkspaces() || []
dispatch(setWorkspaces(workspaces)) dispatch(setWorkspaces(workspaces))
} }
@ -80,11 +80,11 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
let etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token') let etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token')
if (!etherscanKey) etherscanKey = '2HKUX5ZVASZIKWJM8MIQVCRUVZ6JAWT531' if (!etherscanKey) etherscanKey = '2HKUX5ZVASZIKWJM8MIQVCRUVZ6JAWT531'
const networks = [ const networks = [
{id: 1, name: 'mainnet'}, { id: 1, name: 'mainnet' },
{id: 3, name: 'ropsten'}, { id: 3, name: 'ropsten' },
{id: 4, name: 'rinkeby'}, { id: 4, name: 'rinkeby' },
{id: 42, name: 'kovan'}, { id: 42, name: 'kovan' },
{id: 5, name: 'goerli'} { id: 5, name: 'goerli' }
] ]
let found = false let found = false
const workspaceName = 'etherscan-code-sample' const workspaceName = 'etherscan-code-sample'
@ -113,7 +113,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
await workspaceProvider.set(filePath, data.compilationTargets[filePath]['content']) await workspaceProvider.set(filePath, data.compilationTargets[filePath]['content'])
} }
plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(filePath)) plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(filePath))
plugin.call('notification', 'toast', `Added ${count} verified contract${count === 1 ? '': 's'} from ${foundOnNetworks.join(',')} network${foundOnNetworks.length === 1 ? '': 's'} of Etherscan for contract address ${contractAddress} !!`) plugin.call('notification', 'toast', `Added ${count} verified contract${count === 1 ? '' : 's'} from ${foundOnNetworks.join(',')} network${foundOnNetworks.length === 1 ? '' : 's'} of Etherscan for contract address ${contractAddress} !!`)
} catch (error) { } catch (error) {
await basicWorkspaceInit(workspaces, workspaceProvider) await basicWorkspaceInit(workspaces, workspaceProvider)
} }
@ -121,21 +121,22 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
} else if (isElectron()) { } else if (isElectron()) {
console.log('isElectron initWorkspace') console.log('isElectron initWorkspace')
plugin.call('notification', 'toast', `connecting to electron...`) plugin.call('notification', 'toast', `connecting to electron...`)
if(params.opendir){ if (params.opendir) {
params.opendir = decodeURIComponent(params.opendir) params.opendir = decodeURIComponent(params.opendir)
plugin.call('notification', 'toast', `opening ${params.opendir}...`) plugin.call('notification', 'toast', `opening ${params.opendir}...`)
plugin.call('fs', 'setWorkingDir', params.opendir) await plugin.call('fs', 'setWorkingDir', params.opendir)
} }
plugin.setWorkspace({ name: 'electron', isLocalhost: false }) plugin.setWorkspace({ name: 'electron', isLocalhost: false })
dispatch(setCurrentWorkspace({ name: 'electron', isGitRepo: false })) dispatch(setCurrentWorkspace({ name: 'electron', isGitRepo: false }))
electrOnProvider.init()
listenOnProviderEvents(electrOnProvider)(dispatch) listenOnProviderEvents(electrOnProvider)(dispatch)
listenOnPluginEvents(plugin) listenOnPluginEvents(plugin)
dispatch(setMode('browser')) dispatch(setMode('browser'))
dispatch(fsInitializationCompleted()) dispatch(fsInitializationCompleted())
plugin.emit('workspaceInitializationCompleted') plugin.emit('workspaceInitializationCompleted')
return return
} else if (localStorage.getItem("currentWorkspace")) { } else if (localStorage.getItem("currentWorkspace")) {
const index = workspaces.findIndex(element => element.name == localStorage.getItem("currentWorkspace")) const index = workspaces.findIndex(element => element.name == localStorage.getItem("currentWorkspace"))
if (index !== -1) { if (index !== -1) {
@ -143,10 +144,10 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
workspaceProvider.setWorkspace(name) workspaceProvider.setWorkspace(name)
plugin.setWorkspace({ name: name, isLocalhost: false }) plugin.setWorkspace({ name: name, isLocalhost: false })
dispatch(setCurrentWorkspace({ name: name, isGitRepo: false })) dispatch(setCurrentWorkspace({ name: name, isGitRepo: false }))
}else{ } else {
_paq.push(['trackEvent', 'Storage', 'error', `Workspace in localstorage not found: ${localStorage.getItem("currentWorkspace")}`]) _paq.push(['trackEvent', 'Storage', 'error', `Workspace in localstorage not found: ${localStorage.getItem("currentWorkspace")}`])
await basicWorkspaceInit(workspaces, workspaceProvider) await basicWorkspaceInit(workspaces, workspaceProvider)
} }
} else { } else {
await basicWorkspaceInit(workspaces, workspaceProvider) await basicWorkspaceInit(workspaces, workspaceProvider)
} }
@ -155,9 +156,9 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
listenOnProviderEvents(workspaceProvider)(dispatch) listenOnProviderEvents(workspaceProvider)(dispatch)
listenOnProviderEvents(localhostProvider)(dispatch) listenOnProviderEvents(localhostProvider)(dispatch)
listenOnProviderEvents(electrOnProvider)(dispatch) listenOnProviderEvents(electrOnProvider)(dispatch)
if(isElectron()){ if (isElectron()) {
dispatch(setMode('browser')) dispatch(setMode('browser'))
}else{ } else {
dispatch(setMode('browser')) dispatch(setMode('browser'))
} }
@ -207,11 +208,11 @@ export const publishToGist = async (path?: string, type?: string) => {
const accessToken = config.get('settings/gist-access-token') const accessToken = config.get('settings/gist-access-token')
if (!accessToken) { if (!accessToken) {
dispatch(displayNotification('Authorize Token', 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.', 'Close', null, () => {})) dispatch(displayNotification('Authorize Token', 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.', 'Close', null, () => { }))
} else { } else {
const params = queryParams.get() as SolidityConfiguration const params = queryParams.get() as SolidityConfiguration
const description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' + const description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' +
params.version + '&optimize=' + params.optimize + '&runs=' + params.runs + '&gist=' params.version + '&optimize=' + params.optimize + '&runs=' + params.runs + '&gist='
const gists = new Gists({ token: accessToken }) const gists = new Gists({ token: accessToken })
if (id) { if (id) {
@ -259,7 +260,7 @@ export const publishToGist = async (path?: string, type?: string) => {
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error)
dispatch(displayNotification('Publish to gist Failed', 'Failed to create gist: ' + error.message, 'Close', null, async () => {})) dispatch(displayNotification('Publish to gist Failed', 'Failed to create gist: ' + error.message, 'Close', null, async () => { }))
} }
} }
@ -292,7 +293,7 @@ export const createNewFolder = async (path: string, rootDir: string) => {
const exists = await fileManager.exists(dirName) const exists = await fileManager.exists(dirName)
if (exists) { if (exists) {
return dispatch(displayNotification('Failed to create folder', `A folder ${extractNameFromKey(path)} already exists at this location. Please choose a different name.`, 'Close', null, () => {})) return dispatch(displayNotification('Failed to create folder', `A folder ${extractNameFromKey(path)} already exists at this location. Please choose a different name.`, 'Close', null, () => { }))
} }
await fileManager.mkdir(dirName) await fileManager.mkdir(dirName)
path = path.indexOf(rootDir + '/') === 0 ? path.replace(rootDir + '/', '') : path path = path.indexOf(rootDir + '/') === 0 ? path.replace(rootDir + '/', '') : path
@ -318,7 +319,7 @@ export const renamePath = async (oldPath: string, newPath: string) => {
const exists = await fileManager.exists(newPath) const exists = await fileManager.exists(newPath)
if (exists) { if (exists) {
dispatch(displayNotification('Rename File Failed', `A file or folder ${extractNameFromKey(newPath)} already exists at this location. Please choose a different name.`, 'Close', null, () => {})) dispatch(displayNotification('Rename File Failed', `A file or folder ${extractNameFromKey(newPath)} already exists at this location. Please choose a different name.`, 'Close', null, () => { }))
} else { } else {
await fileManager.rename(oldPath, newPath) await fileManager.rename(oldPath, newPath)
} }
@ -476,7 +477,7 @@ const handleGistResponse = (error, data) => {
if (data.html_url) { if (data.html_url) {
dispatch(displayNotification('Gist is ready', `The gist is at ${data.html_url}. Would you like to open it in a new window?`, 'OK', 'Cancel', () => { dispatch(displayNotification('Gist is ready', `The gist is at ${data.html_url}. Would you like to open it in a new window?`, 'OK', 'Cancel', () => {
window.open(data.html_url, '_blank') window.open(data.html_url, '_blank')
}, () => {})) }, () => { }))
} else { } else {
const error = JSON.stringify(data.errors, null, '\t') || '' const error = JSON.stringify(data.errors, null, '\t') || ''
const message = data.message === 'Not Found' ? data.message + '. Please make sure the API token has right to create a gist.' : data.message const message = data.message === 'Not Found' ? data.message + '. Please make sure the API token has right to create a gist.' : data.message

@ -13,6 +13,7 @@ import { ROOT_PATH, slitherYml, solTestYml, tsSolTestYml } from '../utils/consta
import { IndexedDBStorage } from '../../../../../../apps/remix-ide/src/app/files/filesystems/indexedDB' import { IndexedDBStorage } from '../../../../../../apps/remix-ide/src/app/files/filesystems/indexedDB'
import { getUncommittedFiles } from '../utils/gitStatusFilter' import { getUncommittedFiles } from '../utils/gitStatusFilter'
import { AppModal, ModalTypes } from '@remix-ui/app' import { AppModal, ModalTypes } from '@remix-ui/app'
import isElectron from 'is-electron'
declare global { declare global {
interface Window { remixFileSystemCallback: IndexedDBStorage; } interface Window { remixFileSystemCallback: IndexedDBStorage; }
@ -83,6 +84,15 @@ const removeSlash = (s: string) => {
} }
export const createWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate, opts = null, isEmpty = false, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void, isGitRepo: boolean = false, createCommit: boolean = true) => { export const createWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate, opts = null, isEmpty = false, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void, isGitRepo: boolean = false, createCommit: boolean = true) => {
if (isElectron()) {
if (workspaceTemplateName) {
await plugin.call('remix-templates', 'loadTemplateInNewWindow', workspaceTemplateName, opts)
}
return
}
await plugin.fileManager.closeAllFiles() await plugin.fileManager.closeAllFiles()
const promise = createWorkspaceTemplate(workspaceName, workspaceTemplateName) const promise = createWorkspaceTemplate(workspaceName, workspaceTemplateName)
dispatch(createWorkspaceRequest(promise)) dispatch(createWorkspaceRequest(promise))
@ -165,6 +175,7 @@ export type UrlParametersType = {
export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault', opts?) => { export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault', opts?) => {
const workspaceProvider = plugin.fileProviders.workspace const workspaceProvider = plugin.fileProviders.workspace
const electronProvider = plugin.fileProviders.electron
const params = queryParams.get() as UrlParametersType const params = queryParams.get() as UrlParametersType
switch (template) { switch (template) {
@ -243,6 +254,7 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
_paq.push(['trackEvent', 'workspace', 'template', template]) _paq.push(['trackEvent', 'workspace', 'template', template])
// @ts-ignore // @ts-ignore
const files = await templateWithContent[template](opts) const files = await templateWithContent[template](opts)
console.log('files for template ', files)
for (const file in files) { for (const file in files) {
try { try {
await workspaceProvider.set(file, files[file]) await workspaceProvider.set(file, files[file])
@ -276,7 +288,7 @@ export const fetchWorkspaceDirectory = async (path: string) => {
if (error) { if (error) {
console.error(error) console.error(error)
return reject(error) return reject(error)
} }
console.log('fetchWorkspaceDirectory', fileTree) console.log('fetchWorkspaceDirectory', fileTree)
resolve(fileTree) resolve(fileTree)
}) })
@ -346,12 +358,12 @@ export const switchToWorkspace = async (name: string) => {
// if there is no other workspace, create remix default workspace // if there is no other workspace, create remix default workspace
plugin.call('notification', 'toast', `No workspace found! Creating default workspace ....`) plugin.call('notification', 'toast', `No workspace found! Creating default workspace ....`)
await createWorkspace('default_workspace', 'remixDefault') await createWorkspace('default_workspace', 'remixDefault')
} else if(name === ELECTRON) { } else if (name === ELECTRON) {
await plugin.fileProviders.workspace.setWorkspace(name) await plugin.fileProviders.workspace.setWorkspace(name)
await plugin.setWorkspace({ name, isLocalhost: false }) await plugin.setWorkspace({ name, isLocalhost: false })
dispatch(setMode('browser')) dispatch(setMode('browser'))
dispatch(setCurrentWorkspace({ name, isGitRepo:false })) dispatch(setCurrentWorkspace({ name, isGitRepo: false }))
} else { } else {
const isActive = await plugin.call('manager', 'isActive', 'remixd') const isActive = await plugin.call('manager', 'isActive', 'remixd')
@ -417,8 +429,8 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error,
okFn: () => { okFn: () => {
loadFile(name, file, workspaceProvider, cb) loadFile(name, file, workspaceProvider, cb)
}, },
cancelFn: () => {}, cancelFn: () => { },
hideFn: () => {} hideFn: () => { }
} }
plugin.call('notification', 'modal', modalContent) plugin.call('notification', 'modal', modalContent)
} }
@ -426,7 +438,7 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error,
} }
export const uploadFolder = async (target, targetFolder: string, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => { export const uploadFolder = async (target, targetFolder: string, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => {
for(const file of [...target.files]) { for (const file of [...target.files]) {
const workspaceProvider = plugin.fileProviders.workspace const workspaceProvider = plugin.fileProviders.workspace
const name = targetFolder === '/' ? file.webkitRelativePath : `${targetFolder}/${file.webkitRelativePath}` const name = targetFolder === '/' ? file.webkitRelativePath : `${targetFolder}/${file.webkitRelativePath}`
if (!await workspaceProvider.exists(name)) { if (!await workspaceProvider.exists(name)) {
@ -442,8 +454,8 @@ export const uploadFolder = async (target, targetFolder: string, cb?: (err: Erro
okFn: () => { okFn: () => {
loadFile(name, file, workspaceProvider, cb) loadFile(name, file, workspaceProvider, cb)
}, },
cancelFn: () => {}, cancelFn: () => { },
hideFn: () => {} hideFn: () => { }
} }
plugin.call('notification', 'modal', modalContent) plugin.call('notification', 'modal', modalContent)
} }
@ -495,46 +507,58 @@ export const cloneRepository = async (url: string) => {
const token = config.get('settings/gist-access-token') const token = config.get('settings/gist-access-token')
const repoConfig = { url, token } const repoConfig = { url, token }
try { if (isElectron()) {
const repoName = await getRepositoryTitle(url) try {
await plugin.call('dGitProvider', 'clone', repoConfig)
await createWorkspace(repoName, 'blank', null, true, null, true, false) } catch (e) {
const promise = plugin.call('dGitProvider', 'clone', repoConfig, repoName, true) console.log(e)
plugin.call('notification', 'alert', {
dispatch(cloneRepositoryRequest())
promise.then(async () => {
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
await fetchWorkspaceDirectory(ROOT_PATH)
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const branches = await getGitRepoBranches(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceBranches(branches))
const currentBranch = await getGitRepoCurrentBranch(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceCurrentBranch(currentBranch))
dispatch(cloneRepositorySuccess())
}).catch(() => {
const cloneModal = {
id: 'cloneGitRepository', id: 'cloneGitRepository',
title: 'Clone Git Repository', message: e
message: 'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin', })
modalType: 'modal', }
okLabel: 'OK', } else {
okFn: async () => { try {
await deleteWorkspace(repoName) const repoName = await getRepositoryTitle(url)
dispatch(cloneRepositoryFailed())
}, await createWorkspace(repoName, 'blank', null, true, null, true, false)
hideFn: async () => { const promise = plugin.call('dGitProvider', 'clone', repoConfig, repoName, true)
await deleteWorkspace(repoName)
dispatch(cloneRepositoryFailed()) dispatch(cloneRepositoryRequest())
promise.then(async () => {
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
await fetchWorkspaceDirectory(ROOT_PATH)
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const branches = await getGitRepoBranches(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceBranches(branches))
const currentBranch = await getGitRepoCurrentBranch(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceCurrentBranch(currentBranch))
dispatch(cloneRepositorySuccess())
}).catch(() => {
const cloneModal = {
id: 'cloneGitRepository',
title: 'Clone Git Repository',
message: 'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin',
modalType: 'modal',
okLabel: 'OK',
okFn: async () => {
await deleteWorkspace(repoName)
dispatch(cloneRepositoryFailed())
},
hideFn: async () => {
await deleteWorkspace(repoName)
dispatch(cloneRepositoryFailed())
}
} }
} plugin.call('notification', 'modal', cloneModal)
plugin.call('notification', 'modal', cloneModal) })
}) } catch (e) {
} catch (e) { dispatch(displayPopUp('An error occured: ' + e))
dispatch(displayPopUp('An error occured: ' + e)) }
} }
} }
@ -671,21 +695,21 @@ export const createNewBranch = async (branch: string) => {
export const createSolidityGithubAction = async () => { export const createSolidityGithubAction = async () => {
const path = '.github/workflows/run-solidity-unittesting.yml' const path = '.github/workflows/run-solidity-unittesting.yml'
await plugin.call('fileManager', 'writeFile', path , solTestYml) await plugin.call('fileManager', 'writeFile', path, solTestYml)
plugin.call('fileManager', 'open', path) plugin.call('fileManager', 'open', path)
} }
export const createTsSolGithubAction = async () => { export const createTsSolGithubAction = async () => {
const path = '.github/workflows/run-js-test.yml' const path = '.github/workflows/run-js-test.yml'
await plugin.call('fileManager', 'writeFile', path , tsSolTestYml) await plugin.call('fileManager', 'writeFile', path, tsSolTestYml)
plugin.call('fileManager', 'open', path) plugin.call('fileManager', 'open', path)
} }
export const createSlitherGithubAction = async () => { export const createSlitherGithubAction = async () => {
const path = '.github/workflows/run-slither-action.yml' const path = '.github/workflows/run-slither-action.yml'
await plugin.call('fileManager', 'writeFile', path , slitherYml) await plugin.call('fileManager', 'writeFile', path, slitherYml)
plugin.call('fileManager', 'open', path) plugin.call('fileManager', 'open', path)
} }

@ -102,6 +102,16 @@ export function Workspace() {
} }
setCurrentWorkspace(workspaceName) setCurrentWorkspace(workspaceName)
resetFocus() resetFocus()
// expose some UI to the plugin, perhaps not the best way to do it
if (global.plugin) {
global.plugin.loadTemplate = async () => {
createWorkspace()
}
global.plugin.clone = async () => {
cloneGitRepository()
}
}
}, []) }, [])
useEffect(() => { useEffect(() => {
@ -686,7 +696,7 @@ export function Workspace() {
<FormattedMessage id='filePanel.workspace' /> <FormattedMessage id='filePanel.workspace' />
</label> </label>
</span> : null} </span> : null}
{currentWorkspace !== LOCALHOST && !isElectron() ? (<span className="remixui_menu remixui_topmenu d-flex justify-content-between align-items-end w-75"> {currentWorkspace !== LOCALHOST ? (<span className="remixui_menu remixui_topmenu d-flex justify-content-between align-items-end w-75">
<CustomTooltip <CustomTooltip
placement="top" placement="top"
tooltipId="createWorkspaceTooltip" tooltipId="createWorkspaceTooltip"

Loading…
Cancel
Save