├── .gitignore ├── remix-desktop-open.png ├── config.js ├── selectFolder.js ├── release-process.md ├── .circleci └── config.yml ├── tsconfig.json ├── package.json ├── README.md ├── applicationMenu.js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | release 2 | node_modules 3 | cache 4 | -------------------------------------------------------------------------------- /remix-desktop-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/remix-desktop/HEAD/remix-desktop-open.png -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const os = require('os') 3 | const path = require('path') 4 | 5 | const cacheDir = path.join(os.homedir(), '.cache_remix_ide') 6 | const write = (data) => { 7 | const cache = read() 8 | try { 9 | fs.writeFileSync(cacheDir + '/config.json', JSON.stringify({...cache, ...data})) 10 | } catch (e) { 11 | console.error('Can\'t write config file', e) 12 | } 13 | } 14 | 15 | const read = () => { 16 | if(fs.existsSync(cacheDir + '/config.json')){ 17 | try { 18 | const data = JSON.parse(fs.readFileSync(cacheDir + '/config.json')) 19 | return data 20 | }catch(e){ 21 | console.error('Can\'t read config file', e) 22 | } 23 | } 24 | return undefined 25 | } 26 | 27 | exports.write = write 28 | exports.read = read 29 | -------------------------------------------------------------------------------- /selectFolder.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const { dialog } = require('electron') 3 | 4 | module.exports = () => { 5 | return new Promise((resolve, reject) => { 6 | dialog.showOpenDialog( 7 | { 8 | defaultPath: os.homedir(), 9 | buttonLabel: "Open", 10 | title: 'Select Working Directory (Default to the Home directory)', 11 | properties: ['openDirectory', 'createDirectory', 'promptToCreate'], 12 | message: 'Working Directory' 13 | }). 14 | then((result) => { 15 | if (result.canceled || result.filePaths.length === 0) { 16 | resolve(os.homedir()) 17 | } else { 18 | resolve(result.filePaths[0]) 19 | } 20 | }). 21 | catch((error) => reject(error)) 22 | }) 23 | } -------------------------------------------------------------------------------- /release-process.md: -------------------------------------------------------------------------------- 1 | Step for making a new release: 2 | 3 | - from https://github.com/ethereum/remix-desktop/releases, click on `Draft a new release`. 4 | - the `Tag version` should be the version number (e.g `v1.0.3-beta.6` or `v1.0.4`). 5 | - the `title` can be anything (set it the same as `Tag version` if no other obvious title can be found). 6 | - the `description` can be anything. 7 | - click on `Save draft`. 8 | - create a pull request which update the `package.json` and `package-lock.json` (if exist) to the same version number. 9 | (note that this time it does not contain the prefix `v`). 10 | - when the pull request is merged, circle-ci build runs and populate the drafted release with the binaries. 11 | - ... push PRs or commits until the binaries are ok for a release ... 12 | - when the binaries are stable enough for being released, go to the release page and publish the release. 13 | 14 | Note that when Remix IDE is released it is not necessary to make a new release of remix-desktop. 15 | Remix Desktop always verify and run the latest Remix IDE. 16 | 17 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | aliases: 3 | 4 | #- &node_installation 5 | # run: 6 | # name: "Update Node.js and npm if on MacOS" 7 | # command: .circleci/install_node.sh 8 | 9 | - &upgrade_yarn 10 | run: 11 | name: "Updates yarn" 12 | command: .circleci/upgrade_yarn.sh 13 | 14 | # Custom task removes unnecessary build files before uploading release directory 15 | - &before_store_artifacts 16 | run: 17 | name: Preparing for artifact upload 18 | command: rm -rf release/*-unpacked release/mac 19 | 20 | - &upload_artifacts 21 | store_artifacts: 22 | path: release/ 23 | 24 | #- &install 25 | # run: node --version && yarn install 26 | 27 | - &install 28 | run: node --version && yarn 29 | 30 | - &test_e2e 31 | run: yarn test:e2e -s 32 | 33 | - &run_script 34 | run: 35 | name: "Building app (deploy if on master)" 36 | no_output_timeout: 30m 37 | command: | 38 | if [[ "${CIRCLE_BRANCH}" == 'master' ]]; then 39 | yarn release --$GRID_ENV; 40 | else 41 | yarn dist --$GRID_ENV; 42 | fi; 43 | - &run_steps 44 | steps: 45 | - checkout 46 | - run: node -v 47 | # - *restore_modules_cache 48 | - *install 49 | # libp2p uses by default ursa-optional, libp2p fails using the ursa-optional built files. 50 | # removing ursa-optional allows libp2p to fallback to "keypair": https://github.com/libp2p/js-libp2p-crypto/pull/128/files#diff-3544cb4008be1bc81ba3bc70fbb55745a3343712bf391c316b21c4548b194501R23 51 | # remix-desktop issue: https://github.com/ethereum/remix-desktop/issues/40 52 | - run: rm -rf node_modules/ursa-optional 53 | # - *test_e2e 54 | - *run_script 55 | # - *save_modules_cache 56 | - *before_store_artifacts 57 | - *upload_artifacts 58 | 59 | jobs: 60 | build: 61 | docker: 62 | - image: electronuserland/builder:14 63 | environment: 64 | GRID_ENV: linux 65 | working_directory: ~/repo 66 | <<: *run_steps 67 | 68 | build-win: 69 | docker: 70 | - image: electronuserland/builder:wine 71 | environment: 72 | GRID_ENV: win 73 | working_directory: ~/repo 74 | <<: *run_steps 75 | 76 | build-mac: 77 | macos: 78 | xcode: 13.1.0 79 | environment: 80 | GRID_ENV: mac 81 | working_directory: ~/repo 82 | <<: *run_steps 83 | 84 | workflows: 85 | version: 2 86 | build: 87 | jobs: 88 | - build 89 | - build-mac 90 | - build-win 91 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // project options 4 | "lib": [ 5 | "ESNext", 6 | "dom" 7 | ], // specifies which default set of type definitions to use ("DOM", "ES6", etc) 8 | "outDir": "lib", // .js (as well as .d.ts, .js.map, etc.) files will be emitted into this directory., 9 | "removeComments": true, // Strips all comments from TypeScript files when converting into JavaScript- you rarely read compiled code so this saves space 10 | "target": "ES6", // Target environment. Most modern browsers support ES6, but you may want to set it to newer or older. (defaults to ES3) 11 | 12 | // Module resolution 13 | "baseUrl": "./", // Lets you set a base directory to resolve non-absolute module names. 14 | "esModuleInterop": true, // fixes some issues TS originally had with the ES6 spec where TypeScript treats CommonJS/AMD/UMD modules similar to ES6 module 15 | "moduleResolution": "node", // Pretty much always node for modern JS. Other option is "classic" 16 | "paths": {}, // A series of entries which re-map imports to lookup locations relative to the baseUrl 17 | 18 | // Source Map 19 | "sourceMap": true, // enables the use of source maps for debuggers and error reporting etc 20 | "sourceRoot": "/", // Specify the location where a debugger should locate TypeScript files instead of relative source locations. 21 | 22 | // Strict Checks 23 | "alwaysStrict": true, // Ensures that your files are parsed in the ECMAScript strict mode, and emit “use strict” for each source file. 24 | "allowUnreachableCode": false, // pick up dead code paths 25 | "noImplicitAny": false, // In some cases where no type annotations are present, TypeScript will fall back to a type of any for a variable when it cannot infer the type. 26 | "strictNullChecks": false, // When strictNullChecks is true, null and undefined have their own distinct types and you’ll get a type error if you try to use them where a concrete value is expected. 27 | 28 | // Linter Checks 29 | "noImplicitReturns": true, 30 | "noUncheckedIndexedAccess": true, // accessing index must always check for undefined 31 | "noUnusedLocals": false, // Report errors on unused local variables. 32 | "noUnusedParameters": false, // Report errors on unused parameters in functions 33 | "allowJs": true, 34 | "skipLibCheck": true, 35 | "experimentalDecorators": true 36 | }, 37 | "include": ["./**/*.ts", "./**/*.js"], 38 | "exclude": [ 39 | "node_modules/**/*", "lib/**/*" 40 | ], 41 | "files": ["main.js"] 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.3.7", 3 | "name": "remix-ide", 4 | "description": "Remix - Ethereum IDE", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "electron .", 8 | "dist": "electron-builder", 9 | "release": "yarn run dist" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/ethereum/remix-desktop.git" 14 | }, 15 | "author": { 16 | "name": "Remix Team", 17 | "email": "remix@ethereum.org" 18 | }, 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/ethereum/remix-desktop/issues" 22 | }, 23 | "homepage": "https://github.com/ethereum/remix-desktop#readme", 24 | "devDependencies": { 25 | "electron": "20.0.3", 26 | "electron-builder": "23.3.3", 27 | "typescript": "^4.7.4" 28 | }, 29 | "build": { 30 | "productName": "Remix IDE", 31 | "appId": "org.ethereum.remix-ide", 32 | "asar": true, 33 | "files": [ 34 | "main.js", 35 | "selectFolder.js", 36 | "setFolder.js", 37 | "applicationMenu.js", 38 | "config.js", 39 | "build/icon.png", 40 | "*.asar", 41 | "*.zip" 42 | ], 43 | "mac": { 44 | "category": "public.app-category.productivity", 45 | "target": [ 46 | { 47 | "target": "zip", 48 | "arch": [ 49 | "x64", 50 | "arm64" 51 | ] 52 | }, 53 | { 54 | "target": "dmg", 55 | "arch": [ 56 | "x64", 57 | "arm64", 58 | "universal" 59 | ] 60 | } 61 | ], 62 | "darkModeSupport": true 63 | }, 64 | "dmg": { 65 | "writeUpdateInfo": false 66 | }, 67 | "nsis": { 68 | "createDesktopShortcut": "always", 69 | "allowToChangeInstallationDirectory": true, 70 | "oneClick": false, 71 | "shortcutName": "Remix IDE", 72 | "differentialPackage": false 73 | }, 74 | "win": { 75 | "target": [ 76 | "zip", 77 | "nsis" 78 | ] 79 | }, 80 | "linux": { 81 | "target": [ 82 | "deb", 83 | "snap", 84 | "AppImage" 85 | ], 86 | "category": "WebBrowser" 87 | }, 88 | "directories": { 89 | "output": "release" 90 | } 91 | }, 92 | "dependencies": { 93 | "@remix-project/remixd": "^0.6.6", 94 | "electron-app-manager": "EthereumRemix/electron-app-manager#remix-dep", 95 | "fs-extra": "^3.0.1", 96 | "latest-version": "^5.1.0", 97 | "node-fetch": "^2.6.1", 98 | "semver": "^7.3.5", 99 | "fix-path": "3.0.0" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Join the chat at https://gitter.im/ethereum/remix](https://badges.gitter.im/ethereum/remix.svg)](https://gitter.im/ethereum/remix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | ## DEPRECATED 4 | 5 | This release of Remix Desktop is deprecated and will be replaced by a new version available at: 6 | https://github.com/remix-project-org/remix-desktop 7 | 8 | # Remix Desktop 9 | **Remix Desktop** is an Electron version of Remix IDE. It works on Linux, Windows, & Macs. 10 | 11 | Like the name says, it is a desktop app - so it you can seamlessly access files on your computer's filesystem. 12 | 13 | To find out more about Remix IDE - please go to [ethereum/remix-project](https://github.com/ethereum/remix-project) - or to see it in action, go to [remix.ethereum.org](https://remix.ethereum.org). 14 | 15 | 16 | ### Download 17 | To download Remix Desktop, see releases: [https://github.com/ethereum/remix-desktop/releases](https://github.com/ethereum/remix-desktop/releases) 18 | 19 | ## Differences between Remix Desktop & Remix IDE - the web app 20 | ### Accessing your hard drive 21 | **Remix IDE - the web app** works in a browser and without using [remixd](https://remix-ide.readthedocs.io/en/latest/remixd.html), it cannot access your computer's file system. Whereas with **Remix Desktop** accessing your filesystem is easy. 22 | 23 | Saving and accessing files saved on your computer are the big advantage of Remix Desktop. 24 | 25 | In Remix Desktop, you select a folder from the File menu (File -> Open Folder) to make it the active folder in the **File Explorers'** workspace. Go to File -> Open Folder to select. 26 | 27 | ![](remix-desktop-open.png) 28 | 29 | ### Version control & folder capacity 30 | With Remix Desktop, version control is just like it would be with any other desktop IDE. Similarly the size of a workspace folder is limited by your computer's hard drive. In Remix IDE - the web app, the size of a workspace folder is limited by the size of the browser's local storage. There are techniques for putting version control in the browser (like using remixd or the DGIT plugin), but these are work-arounds to inherent limitations of a browser. 31 | 32 | ### Deploying to a public testnet with Injected Web3 & Metamask 33 | **Remix Desktop** does not have access to the Metamask - the browser plugin - so deploying to a public chain currently involves using the Wallet Connect plugin. In contrast, **Remix IDE - the web app** has easy access to the Metamask browser plugin. 34 | 35 | ## Updates to Remix IDE & Updates to Remix Desktop 36 | Please check subscribe to our Twitter feed @EthereumRemix - so we can prompt you to download the latest version. Remix Desktop is a wrapper of Remix IDE so Remix Desktop will automatically grab the latest version of Remix IDE. You only need to download the new releases of Remix Desktop. We also post announcements in our gitter chat: https://gitter.im/ethereum/remix 37 | 38 | ## Where to go to for help 39 | Please post your questions to: https://gitter.im/ethereum/remix 40 | 41 | ## Reporting issues 42 | For posting issues - you can alert us in the gitter chat - or post the issue to this repo. 43 | -------------------------------------------------------------------------------- /applicationMenu.js: -------------------------------------------------------------------------------- 1 | const {Menu, shell, app} = require('electron') 2 | const fs = require('fs') 3 | const path = require('path') 4 | const config = require('./config') 5 | const selectFolder = require('./selectFolder') 6 | 7 | module.exports = (outdatedVersion, cacheDir, app, sharedFolderClient) => { 8 | 9 | const isMac = process.platform === 'darwin' 10 | 11 | const template = [ 12 | // { role: 'appMenu' } 13 | ...(isMac ? [{ 14 | label: app.name, 15 | submenu: [ 16 | { role: 'about' }, 17 | { type: 'separator' }, 18 | { role: 'services' }, 19 | { type: 'separator' }, 20 | { role: 'hide' }, 21 | { role: 'hideothers' }, 22 | { role: 'unhide' }, 23 | { type: 'separator' }, 24 | { role: 'quit' } 25 | ] 26 | }] : []), 27 | // { role: 'fileMenu' } 28 | { 29 | label: 'File', 30 | submenu: [ 31 | { 32 | label: 'Open Folder', 33 | click: async () => { 34 | selectFolder().then((folder) => { 35 | sharedFolderClient(folder) 36 | config.write({sharedFolder:folder}) 37 | }).catch(console.log) 38 | } 39 | }, 40 | isMac ? { role: 'close' } : { role: 'quit' }, 41 | ] 42 | }, 43 | // { role: 'editMenu' } 44 | { 45 | label: 'Edit', 46 | submenu: [ 47 | { role: 'undo' }, 48 | { role: 'redo' }, 49 | { type: 'separator' }, 50 | { role: 'cut' }, 51 | { role: 'copy' }, 52 | { role: 'paste' }, 53 | ...(isMac ? [ 54 | { role: 'pasteAndMatchStyle' }, 55 | { role: 'delete' }, 56 | { role: 'selectAll' }, 57 | { type: 'separator' }, 58 | { 59 | label: 'Speech', 60 | submenu: [ 61 | { role: 'startspeaking' }, 62 | { role: 'stopspeaking' } 63 | ] 64 | } 65 | ] : [ 66 | { role: 'delete' }, 67 | { type: 'separator' }, 68 | { role: 'selectAll' } 69 | ]) 70 | ] 71 | }, 72 | // { role: 'viewMenu' } 73 | { 74 | label: 'View', 75 | submenu: [ 76 | // { role: 'reload' }, 77 | // { role: 'forcereload' }, 78 | { role: 'resetzoom' }, 79 | { role: 'zoomin' }, 80 | { role: 'zoomout' }, 81 | { type: 'separator' }, 82 | { role: 'togglefullscreen' } 83 | ] 84 | }, 85 | // { role: 'windowMenu' } 86 | { 87 | label: 'Window', 88 | submenu: [ 89 | { role: 'minimize' }, 90 | { role: 'zoom' }, 91 | ...(isMac ? [ 92 | { type: 'separator' }, 93 | { role: 'front' }, 94 | { type: 'separator' }, 95 | { role: 'window' } 96 | ] : [ 97 | { role: 'close' } 98 | ]) 99 | ] 100 | }, 101 | { 102 | role: 'help', 103 | submenu: [ 104 | { 105 | label: 'Learn More', 106 | click: async () => { 107 | shell.openExternal('https://remix-ide.readthedocs.io') 108 | } 109 | }, 110 | { 111 | label: 'Medium Posts', 112 | click: async () => { 113 | shell.openExternal('https://medium.com/remix-ide') 114 | } 115 | }, 116 | { 117 | label: 'Community Discussions', 118 | click: async () => { 119 | shell.openExternal('https://gitter.im/ethereum/remix') 120 | } 121 | }, 122 | { 123 | label: 'Remix in StackExchange.com', 124 | click: async () => { 125 | shell.openExternal('https://ethereum.stackexchange.com/questions/tagged/remix') 126 | } 127 | }, 128 | { 129 | label: 'Check Releases', 130 | click: async () => { 131 | shell.openExternal('https://github.com/ethereum/remix-desktop/releases') 132 | } 133 | }, 134 | { 135 | label: 'Report Bugs / Issues', 136 | click: async () => { 137 | shell.openExternal('https://github.com/ethereum/remix-ide/issues') 138 | } 139 | }, 140 | { role: 'toggledevtools' }, 141 | { 142 | label: 'Clear the cache and restart Remix', 143 | click: async () => { 144 | deleteFolderRecursive(cacheDir) 145 | app.relaunch() 146 | app.exit(0) 147 | } 148 | }, 149 | ] 150 | } 151 | ] 152 | if (outdatedVersion) { 153 | template.push({ 154 | label: 'New Release Available', 155 | submenu: [ 156 | { 157 | label: 'Remix Desktop release page', 158 | click: async () => { 159 | shell.openExternal('https://github.com/ethereum/remix-desktop/releases') 160 | } 161 | }, 162 | ] 163 | }) 164 | } 165 | const menu = Menu.buildFromTemplate(template) 166 | Menu.setApplicationMenu(menu) 167 | 168 | } 169 | 170 | const deleteFolderRecursive = function (directoryPath) { 171 | try { 172 | if (fs.existsSync(directoryPath)) { 173 | fs.readdirSync(directoryPath).forEach((file, index) => { 174 | const curPath = path.join(directoryPath, file); 175 | if (fs.lstatSync(curPath).isDirectory()) { 176 | // recurse 177 | deleteFolderRecursive(curPath) 178 | } else { 179 | // delete file 180 | fs.unlinkSync(curPath) 181 | } 182 | }); 183 | fs.rmdirSync(directoryPath) 184 | console.log(directoryPath + ' deleted') 185 | } 186 | } catch (e) { 187 | console.error(e) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const remixd = require('@remix-project/remixd/src') 3 | const path = require('path') 4 | const os = require('os') 5 | const fetch = require('node-fetch') 6 | const semver = require('semver') 7 | const config = require('./config') 8 | const fixPath = require('fix-path') 9 | 10 | const { version } = require('./package.json') 11 | const applicationMenu = require('./applicationMenu') 12 | const { app, BrowserWindow, shell } = require('electron') 13 | const { AppManager, registerPackageProtocol } = require('electron-app-manager') 14 | 15 | const cacheDir = path.join(os.homedir(), '.cache_remix_ide') 16 | registerPackageProtocol(cacheDir) 17 | 18 | const remixIdeUrl = 'package://6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod' 19 | 20 | async function warnLatestVersion (current) { 21 | const res = await fetch('https://api.github.com/repos/ethereum/remix-desktop/releases/latest') 22 | let latest = (await res.json()).tag_name 23 | latest = latest.indexOf('v') === 0 ? latest.replace('v', '') : latest 24 | let ret = '' 25 | console.log(latest, current) 26 | if (semver.eq(latest, current)) { 27 | console.log('\x1b[32m%s\x1b[0m', `[INFO] you are using the latest version ${latest}`) 28 | ret = 'OK' 29 | } else if (semver.gt(latest, current)) { 30 | console.log('\x1b[33m%s\x1b[0m', `[WARN] latest version of remix-desktop is ${latest}, you are using ${current}`) 31 | console.log('\x1b[33m%s\x1b[0m', `[WARN] please download the latest version:`) 32 | console.log('\x1b[33m%s\x1b[0m', `[WARN] https://github.com/ethereum/remix-desktop/releases`) 33 | ret = 'OUTDATED' 34 | } 35 | return ret 36 | } 37 | 38 | const updater = new AppManager({ 39 | repository: 'https://github.com/ethereum/remix-desktop', 40 | auto: true, 41 | electron: true 42 | }) 43 | 44 | function createWindow () { 45 | let win = new BrowserWindow({ 46 | width: 800, 47 | height: 600, 48 | webPreferences: { 49 | nodeIntegration: false 50 | }, 51 | icon: path.join(__dirname, 'build/icon.png') 52 | }) 53 | win.webContents.on('new-window', function(e, url) { 54 | e.preventDefault(); 55 | shell.openExternal(url); 56 | }) 57 | win.loadURL('package://github.com/ethereum/remix-project') 58 | 59 | // Modify the user agent for all requests to the following urls. 60 | const filter = { 61 | urls: ['https://*.dyn.plugin.remixproject.org/ipfs/*'] 62 | } 63 | let hashes = {} 64 | win.webContents.session.webRequest.onBeforeRequest(filter, (details, callback) => { 65 | let { url } = details; 66 | let reg = /dyn.plugin.remixproject.org\/ipfs\/(.*)/ 67 | let regResult = reg.exec(url) 68 | const hash = regResult[1] 69 | hashes[hash] = url 70 | url = `http://localhost:5001/ipfs/${regResult[1]}` 71 | callback({ 72 | cancel: false, 73 | url: ( encodeURI(url ) ) 74 | }) 75 | }) 76 | 77 | win.webContents.session.webRequest.onErrorOccurred((details) => { 78 | // console.error(details) 79 | }) 80 | } 81 | 82 | let sharedFolderClient = new remixd.services.sharedFolder() 83 | let slitherClient = new remixd.services.SlitherClient() 84 | let hardhatClient = new remixd.services.HardhatClient() 85 | const services = { 86 | hardhat: () => { 87 | hardhatClient.options.customApi = {} 88 | return hardhatClient 89 | }, 90 | slither: () => { 91 | slitherClient.options.customApi = {} 92 | return slitherClient 93 | }, 94 | folder: () => { 95 | sharedFolderClient.options.customApi = {} 96 | return sharedFolderClient 97 | } 98 | } 99 | 100 | const setupApplicationMenu = async () => { 101 | let status = "" 102 | try { 103 | status = await warnLatestVersion(version) 104 | } catch (e) { 105 | console.log('unable to verify latest version') 106 | console.log(e) 107 | } 108 | applicationMenu(status === 'OUTDATED', cacheDir, app, (folder) => { 109 | sharedFolderClient.sharedFolder(folder, false) 110 | sharedFolderClient.setupNotifications(folder) 111 | slitherClient.sharedFolder(folder) 112 | hardhatClient.sharedFolder(folder) 113 | }) 114 | } 115 | 116 | 117 | // Similar object is also defined in websocket.ts 118 | const ports = { 119 | git: 65521, 120 | hardhat: 65522, 121 | slither: 65523, 122 | folder: 65520 123 | } 124 | 125 | function startService (service, callback) { 126 | try { 127 | const socket = new remixd.Websocket(ports[service], { remixIdeUrl }, () => services[service]()) 128 | socket.start(callback) 129 | } catch (e) { 130 | console.error(e) 131 | } 132 | } 133 | 134 | // return either current folder in client, or the one in cache or by default the os homedir 135 | function getFolder(client) { 136 | if(client.currentSharedFolder) return client.currentSharedFolder 137 | const cache = config.read() 138 | if(cache){ 139 | try { 140 | const folder = cache.sharedFolder 141 | if(fs.existsSync(folder)) return folder 142 | }catch(e){ 143 | } 144 | } 145 | if (process.cwd()) { 146 | return process.cwd() 147 | } else 148 | return os.homedir() 149 | } 150 | 151 | let remixdStart = () => { 152 | // electron GUI does not inherit the path from the terminal, this is a workaround 153 | fixPath() 154 | console.log('start shared folder service') 155 | try { 156 | startService('folder', (ws, client) => { 157 | client.setWebSocket(ws) 158 | client.sharedFolder(getFolder(client)) 159 | client.setupNotifications(getFolder(client)) 160 | }) 161 | 162 | startService('slither', (ws, client) => { 163 | client.setWebSocket(ws) 164 | client.sharedFolder(getFolder(client)) 165 | }) 166 | 167 | startService('hardhat', (ws, client) => { 168 | client.setWebSocket(ws) 169 | client.sharedFolder(getFolder(client)) 170 | }) 171 | 172 | } catch (error) { 173 | throw new Error(error) 174 | } 175 | } 176 | 177 | app.on('ready', () => { 178 | setupApplicationMenu() 179 | remixdStart() 180 | createWindow() 181 | }) 182 | --------------------------------------------------------------------------------