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. 6
      apps/remixdesktop/src/menus/file.ts
  19. 20
      apps/remixdesktop/src/menus/git.ts
  20. 54
      apps/remixdesktop/src/plugins/fsPlugin.ts
  21. 2
      apps/remixdesktop/src/plugins/gitPlugin.ts
  22. 65
      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. 35
      libs/remix-ui/workspace/src/lib/actions/index.ts
  31. 44
      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 { SolidityUmlGen } from './app/plugins/solidity-umlgen'
import { ContractFlattener } from './app/plugins/contractFlattener'
import { fsPlugin } from './app/plugins/fsPlugin'
import { isoGitPlugin } from './app/plugins/isoGitPlugin'
import { electronConfig } from './app/plugins/electronConfigPlugin'
import { TemplatesPlugin } from './app/plugins/remix-templates'
import { fsPlugin } from './app/plugins/electron/fsPlugin'
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')
@ -116,7 +120,7 @@ class AppComponent {
name: 'fileproviders/workspace'
})
this._components.filesProviders.electron = new ElectronProvider()
this._components.filesProviders.electron = new ElectronProvider(this.appManager)
Registry.getInstance().put({
api: this._components.filesProviders.electron,
name: 'fileproviders/electron'
@ -197,7 +201,8 @@ class AppComponent {
//----- search
const search = new SearchPlugin()
//---- templates
const templates = new TemplatesPlugin()
//---------------- Solidity UML Generator -------------------------
const solidityumlgen = new SolidityUmlGen(appManager)
@ -322,6 +327,7 @@ class AppComponent {
solidityumlgen,
contractFlattener,
solidityScript,
templates
])
//---- fs plugin
@ -332,6 +338,8 @@ class AppComponent {
this.engine.register([isoGit])
const electronConfigPlugin = new electronConfig()
this.engine.register([electronConfigPlugin])
const templatesPlugin = new electronTemplates()
this.engine.register([templatesPlugin])
}
// 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(['settings'])
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()){
await this.appManager.activatePlugin(['fs', 'isogit', 'electronconfig'])
await this.appManager.activatePlugin(['fs', 'isogit', 'electronconfig', 'electronTemplates'])
}
this.appManager.on(

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

@ -7,18 +7,45 @@ declare global {
}
export class ElectronProvider extends FileProvider {
constructor () {
constructor(appManager) {
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
// this is a more efficient version of the default implementation
async resolveDirectory (path, cb) {
async resolveDirectory(path, cb) {
path = this.removePrefix(path)
if (path.indexOf('/') !== 0) path = '/' + path
try {
const files = await window.remixFileSystem.readdir(path)
console.log(files, 'files resolveDirectory ELECTRON')
console.log(files.length, 'files resolveDirectory ELECTRON')
const ret = {}
if (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,
version: packageJson.version,
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'],
kind: 'file-system'
}

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

@ -30,7 +30,7 @@ const { SlitherHandle } = require('../files/slither-handle.js')
const profile = {
name: 'filePanel',
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'],
icon: 'assets/img/fileManager.webp',
description: 'Remix IDE file explorer',

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

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

@ -7,6 +7,7 @@ import { XtermPlugin } from './plugins/xtermPlugin';
import git from 'isomorphic-git'
import { IsoGitPlugin } from './plugins/isoGitPlugin';
import { ConfigPlugin } from './plugins/configPlugin';
import { TemplatesPlugin } from './plugins/templates';
const engine = new Engine()
const appManager = new PluginManager()
@ -15,12 +16,14 @@ const gitPlugin = new GitPlugin()
const xtermPlugin = new XtermPlugin()
const isoGitPlugin = new IsoGitPlugin()
const configPlugin = new ConfigPlugin()
const templatesPlugin = new TemplatesPlugin()
engine.register(appManager)
engine.register(fsPlugin)
engine.register(gitPlugin)
engine.register(xtermPlugin)
engine.register(isoGitPlugin)
engine.register(configPlugin)
engine.register(templatesPlugin)
appManager.activatePlugin('electronconfig')
appManager.activatePlugin('fs')
@ -36,6 +39,18 @@ ipcMain.on('fs:openFolder', async (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) => {
return event.sender.id
})

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

@ -11,6 +11,16 @@ const commands: Record<string, (focusedWindow?: BrowserWindow) => void> = {
if (focusedWindow) {
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'
},
{
label: 'Preferences...',
accelerator: commandKeys['window:preferences'],
click() {
execCommand('window:preferences');
}
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{
role: 'hide'
},

@ -22,7 +22,13 @@ export default (
click(item, 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',
displayName: '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 {
@ -83,6 +83,7 @@ class FSPluginClient extends ElectronBasePluginClient {
super(webContentsId, profile)
this.onload(() => {
//console.log('fsPluginClient onload')
this.window.webContents.openDevTools()
this.window.on('close', async () => {
console.log('close', this.webContentsId)
await this.removeFromOpenedFolders(this.workingDir)
@ -96,9 +97,11 @@ class FSPluginClient extends ElectronBasePluginClient {
// call node fs.readdir
console.log('readdir', path)
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
})
const result: any[] = []
for (const file of files) {
const isDirectory = file.isDirectory()
@ -107,6 +110,7 @@ class FSPluginClient extends ElectronBasePluginClient {
isDirectory
})
}
console.log('readdir', path, Date.now() - startTime)
return result
}
@ -122,10 +126,10 @@ class FSPluginClient extends ElectronBasePluginClient {
for (const file of files) {
let pathWithoutWorkingDir = (file as Path).path.replace(this.workingDir, '')
if(!pathWithoutWorkingDir.endsWith('/')){
if (!pathWithoutWorkingDir.endsWith('/')) {
pathWithoutWorkingDir = pathWithoutWorkingDir + '/'
}
if(pathWithoutWorkingDir.startsWith('/')){
if (pathWithoutWorkingDir.startsWith('/')) {
pathWithoutWorkingDir = pathWithoutWorkingDir.slice(1)
}
result.push({
@ -133,7 +137,7 @@ class FSPluginClient extends ElectronBasePluginClient {
isDirectory: (file as Path).isDirectory(),
})
}
console.log('glob', result)
//console.log('glob', result)
return result
}
@ -159,7 +163,10 @@ class FSPluginClient extends ElectronBasePluginClient {
}
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> {
@ -209,11 +216,25 @@ class FSPluginClient extends ElectronBasePluginClient {
return process.cwd()
}
async watch(path: string): Promise<void> {
async watch(): Promise<void> {
if (this.watcher) this.watcher.close()
console.log('watch', this.workingDir)
this.watcher =
chokidar.watch(this.fixPath(path)).on('change', (path, stats) => {
this.emit('change', path, stats)
chokidar.watch(this.workingDir, {
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> {
let dirs: string[] | undefined
@ -268,6 +302,7 @@ class FSPluginClient extends ElectronBasePluginClient {
await this.updateOpenedFolders(path)
this.window.setTitle(this.workingDir)
console.log('setWorkingDir', path)
this.watch()
this.emit('workingDirChanged', path)
}
@ -277,6 +312,7 @@ class FSPluginClient extends ElectronBasePluginClient {
await this.updateRecentFolders(path)
await this.updateOpenedFolders(path)
this.window.setTitle(this.workingDir)
this.watch()
this.emit('workingDirChanged', path)
}

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

@ -13,13 +13,20 @@ const profile: Profile = {
}
export class IsoGitPlugin extends ElectronBasePlugin {
client: PluginClient
clients: IsoGitPluginClient[] = []
constructor() {
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 {
corsProxy: 'https://corsproxy.remixproject.org/',
http,
@ -57,24 +64,24 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
})
}
async getGitConfig () {
async getGitConfig() {
return {
fs,
dir: this.workingDir,
}
}
async status (cmd: any) {
async status(cmd: any) {
console.log('status')
const status = await git.statusMatrix({
...await this.getGitConfig(),
...cmd
})
console.log('STATUS', status, await this.getGitConfig())
//console.log('STATUS', status, await this.getGitConfig())
return status
}
async log (cmd: any) {
async log(cmd: any) {
console.log('log')
const log = await git.log({
...await this.getGitConfig(),
@ -84,7 +91,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return log
}
async add (cmd: any) {
async add(cmd: any) {
console.log('add')
const add = await git.add({
...await this.getGitConfig(),
@ -104,7 +111,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return rm
}
async commit (cmd: any) {
async commit(cmd: any) {
console.log('commit')
const commit = await git.commit({
...await this.getGitConfig(),
@ -114,14 +121,14 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return commit
}
async init (input: any) {
async init(input: any) {
await git.init({
...await this.getGitConfig(),
defaultBranch: (input && input.branch) || 'main'
})
}
async branch (cmd: any) {
async branch(cmd: any) {
console.log('branch')
const branch = await git.branch({
...await this.getGitConfig(),
@ -131,7 +138,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return branch
}
async lsfiles (cmd: any) {
async lsfiles(cmd: any) {
console.log('lsfiles')
const lsfiles = await git.listFiles({
...await this.getGitConfig(),
@ -141,7 +148,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return lsfiles
}
async resolveref (cmd: any) {
async resolveref(cmd: any) {
console.log('resolveref')
const resolveref = await git.resolveRef({
...await this.getGitConfig(),
@ -152,7 +159,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
}
async readblob (cmd: any) {
async readblob(cmd: any) {
console.log('readblob')
const readblob = await git.readBlob({
...await this.getGitConfig(),
@ -162,7 +169,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return readblob
}
async checkout (cmd: any) {
async checkout(cmd: any) {
console.log('checkout')
const checkout = await git.checkout({
...await this.getGitConfig(),
@ -172,7 +179,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return checkout
}
async push (cmd: any) {
async push(cmd: any) {
console.log('push')
const push = await git.push({
...await this.getGitConfig(),
@ -183,7 +190,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
return push
}
async pull (cmd: any) {
async pull(cmd: any) {
console.log('pull', cmd)
const pull = await git.pull({
...await this.getGitConfig(),
@ -207,6 +214,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
async clone(cmd: any) {
console.log('clone')
try {
const clone = await git.clone({
...await this.getGitConfig(),
...cmd,
@ -215,18 +223,10 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
})
console.log('CLONE', clone)
return clone
} catch (e) {
console.log('CLONE ERROR', e)
throw e
}
async openFolder(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 addremote(cmd: any) {
@ -253,7 +253,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
remotes = async () => {
let remotes = []
remotes = await git.listRemotes({...await this.getGitConfig() })
remotes = await git.listRemotes({ ...await this.getGitConfig() })
console.log('remotes', remotes)
return remotes
}
@ -272,7 +272,7 @@ class IsoGitPluginClient extends ElectronBasePluginClient {
async branches() {
try {
let cmd: any = {...await this.getGitConfig()}
let cmd: any = { ...await this.getGitConfig() }
const remotes = await this.remotes()
let branches = []
branches = (await git.listBranches(cmd)).map((branch) => { return { remote: undefined, name: branch } })
@ -294,10 +294,9 @@ 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 {
client: PluginClient
clients: XtermPluginClient[] = []
constructor() {
super(profile, clientProfile, XtermPluginClient)
}

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

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

@ -55,7 +55,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
const electrOnProvider = filePanelPlugin.fileProviders.electron
const params = queryParams.get() as UrlParametersType
let workspaces = []
if (!isElectron()){
if (!isElectron()) {
workspaces = await getWorkspaces() || []
dispatch(setWorkspaces(workspaces))
}
@ -80,11 +80,11 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
let etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token')
if (!etherscanKey) etherscanKey = '2HKUX5ZVASZIKWJM8MIQVCRUVZ6JAWT531'
const networks = [
{id: 1, name: 'mainnet'},
{id: 3, name: 'ropsten'},
{id: 4, name: 'rinkeby'},
{id: 42, name: 'kovan'},
{id: 5, name: 'goerli'}
{ id: 1, name: 'mainnet' },
{ id: 3, name: 'ropsten' },
{ id: 4, name: 'rinkeby' },
{ id: 42, name: 'kovan' },
{ id: 5, name: 'goerli' }
]
let found = false
const workspaceName = 'etherscan-code-sample'
@ -113,7 +113,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
await workspaceProvider.set(filePath, data.compilationTargets[filePath]['content'])
}
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) {
await basicWorkspaceInit(workspaces, workspaceProvider)
}
@ -121,14 +121,15 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
} else if (isElectron()) {
console.log('isElectron initWorkspace')
plugin.call('notification', 'toast', `connecting to electron...`)
if(params.opendir){
if (params.opendir) {
params.opendir = decodeURIComponent(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 })
dispatch(setCurrentWorkspace({ name: 'electron', isGitRepo: false }))
electrOnProvider.init()
listenOnProviderEvents(electrOnProvider)(dispatch)
listenOnPluginEvents(plugin)
dispatch(setMode('browser'))
@ -143,7 +144,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
workspaceProvider.setWorkspace(name)
plugin.setWorkspace({ name: name, isLocalhost: false })
dispatch(setCurrentWorkspace({ name: name, isGitRepo: false }))
}else{
} else {
_paq.push(['trackEvent', 'Storage', 'error', `Workspace in localstorage not found: ${localStorage.getItem("currentWorkspace")}`])
await basicWorkspaceInit(workspaces, workspaceProvider)
}
@ -155,9 +156,9 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
listenOnProviderEvents(workspaceProvider)(dispatch)
listenOnProviderEvents(localhostProvider)(dispatch)
listenOnProviderEvents(electrOnProvider)(dispatch)
if(isElectron()){
if (isElectron()) {
dispatch(setMode('browser'))
}else{
} else {
dispatch(setMode('browser'))
}
@ -207,7 +208,7 @@ export const publishToGist = async (path?: string, type?: string) => {
const accessToken = config.get('settings/gist-access-token')
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 {
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=' +
@ -259,7 +260,7 @@ export const publishToGist = async (path?: string, type?: string) => {
}
} catch (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)
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)
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)
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 {
await fileManager.rename(oldPath, newPath)
}
@ -476,7 +477,7 @@ const handleGistResponse = (error, data) => {
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', () => {
window.open(data.html_url, '_blank')
}, () => {}))
}, () => { }))
} else {
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

@ -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 { getUncommittedFiles } from '../utils/gitStatusFilter'
import { AppModal, ModalTypes } from '@remix-ui/app'
import isElectron from 'is-electron'
declare global {
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) => {
if (isElectron()) {
if (workspaceTemplateName) {
await plugin.call('remix-templates', 'loadTemplateInNewWindow', workspaceTemplateName, opts)
}
return
}
await plugin.fileManager.closeAllFiles()
const promise = createWorkspaceTemplate(workspaceName, workspaceTemplateName)
dispatch(createWorkspaceRequest(promise))
@ -165,6 +175,7 @@ export type UrlParametersType = {
export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault', opts?) => {
const workspaceProvider = plugin.fileProviders.workspace
const electronProvider = plugin.fileProviders.electron
const params = queryParams.get() as UrlParametersType
switch (template) {
@ -243,6 +254,7 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
_paq.push(['trackEvent', 'workspace', 'template', template])
// @ts-ignore
const files = await templateWithContent[template](opts)
console.log('files for template ', files)
for (const file in files) {
try {
await workspaceProvider.set(file, files[file])
@ -346,11 +358,11 @@ export const switchToWorkspace = async (name: string) => {
// if there is no other workspace, create remix default workspace
plugin.call('notification', 'toast', `No workspace found! Creating default workspace ....`)
await createWorkspace('default_workspace', 'remixDefault')
} else if(name === ELECTRON) {
} else if (name === ELECTRON) {
await plugin.fileProviders.workspace.setWorkspace(name)
await plugin.setWorkspace({ name, isLocalhost: false })
dispatch(setMode('browser'))
dispatch(setCurrentWorkspace({ name, isGitRepo:false }))
dispatch(setCurrentWorkspace({ name, isGitRepo: false }))
} else {
const isActive = await plugin.call('manager', 'isActive', 'remixd')
@ -417,8 +429,8 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error,
okFn: () => {
loadFile(name, file, workspaceProvider, cb)
},
cancelFn: () => {},
hideFn: () => {}
cancelFn: () => { },
hideFn: () => { }
}
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) => {
for(const file of [...target.files]) {
for (const file of [...target.files]) {
const workspaceProvider = plugin.fileProviders.workspace
const name = targetFolder === '/' ? file.webkitRelativePath : `${targetFolder}/${file.webkitRelativePath}`
if (!await workspaceProvider.exists(name)) {
@ -442,8 +454,8 @@ export const uploadFolder = async (target, targetFolder: string, cb?: (err: Erro
okFn: () => {
loadFile(name, file, workspaceProvider, cb)
},
cancelFn: () => {},
hideFn: () => {}
cancelFn: () => { },
hideFn: () => { }
}
plugin.call('notification', 'modal', modalContent)
}
@ -495,6 +507,17 @@ export const cloneRepository = async (url: string) => {
const token = config.get('settings/gist-access-token')
const repoConfig = { url, token }
if (isElectron()) {
try {
await plugin.call('dGitProvider', 'clone', repoConfig)
} catch (e) {
console.log(e)
plugin.call('notification', 'alert', {
id: 'cloneGitRepository',
message: e
})
}
} else {
try {
const repoName = await getRepositoryTitle(url)
@ -536,6 +559,7 @@ export const cloneRepository = async (url: string) => {
} catch (e) {
dispatch(displayPopUp('An error occured: ' + e))
}
}
}
export const checkGit = async () => {
@ -671,21 +695,21 @@ export const createNewBranch = async (branch: string) => {
export const createSolidityGithubAction = async () => {
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)
}
export const createTsSolGithubAction = async () => {
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)
}
export const createSlitherGithubAction = async () => {
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)
}

@ -102,6 +102,16 @@ export function Workspace() {
}
setCurrentWorkspace(workspaceName)
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(() => {
@ -686,7 +696,7 @@ export function Workspace() {
<FormattedMessage id='filePanel.workspace' />
</label>
</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
placement="top"
tooltipId="createWorkspaceTooltip"

Loading…
Cancel
Save