diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9f35452..f7c6d53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,7 @@ on: env: JOB_TRANSFER_ARTIFACT: build-artifacts + NODE_VERSION: 18.17 jobs: build: @@ -22,7 +23,7 @@ jobs: fail-fast: false matrix: config: - - os: windows-2019 + - os: [self-hosted, windows-sign-pc] - os: ubuntu-latest - os: macos-13 - os: macos-14 @@ -31,15 +32,17 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Install Node.js 16.x - uses: actions/setup-node@v3 + - name: Install Node.js ${{ env.NODE_VERSION }} + if: runner.name != 'WINDOWS-SIGN-PC' + uses: actions/setup-node@v4 with: - node-version: '16' + node-version: ${{ env.NODE_VERSION }} registry-url: 'https://registry.npmjs.org' - name: Install Python 3.x + if: runner.name != 'WINDOWS-SIGN-PC' uses: actions/setup-python@v4 with: python-version: '3.11.x' @@ -51,11 +54,18 @@ jobs: AC_USERNAME: ${{ secrets.AC_USERNAME }} AC_PASSWORD: ${{ secrets.AC_PASSWORD }} AC_TEAM_ID: ${{ secrets.AC_TEAM_ID }} + INSTALLER_CERT_WINDOWS_CER: "/tmp/cert.cer" + # We are hardcoding the path for signtool because is not present on the windows PATH env var by default. + # Keep in mind that this path could change when upgrading to a new runner version + SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.19041.0/x86/signtool.exe" + WIN_CERT_PASSWORD: ${{ secrets.INSTALLER_CERT_WINDOWS_PASSWORD }} + WIN_CERT_CONTAINER_NAME: ${{ secrets.INSTALLER_CERT_WINDOWS_CONTAINER }} # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # IS_NIGHTLY: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') }} IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }} IS_FORK: ${{ github.event.pull_request.head.repo.fork == true }} + run: | # See: https://www.electron.build/code-signing if [ $IS_FORK = true ]; then diff --git a/backend/ipc.js b/backend/ipc.js index d4ddc74..8bace22 100644 --- a/backend/ipc.js +++ b/backend/ipc.js @@ -6,7 +6,7 @@ const { getAllFiles } = require('./helpers.js') -module.exports = function registerIPCHandlers(win, ipcMain, app) { +module.exports = function registerIPCHandlers(win, ipcMain, app, dialog) { ipcMain.handle('open-folder', async (event) => { console.log('ipcMain', 'open-folder') const folder = await openFolderDialog(win) @@ -43,7 +43,8 @@ module.exports = function registerIPCHandlers(win, ipcMain, app) { ipcMain.handle('save-file', (event, filePath, content) => { console.log('ipcMain', 'save-file', filePath, content) - fs.writeFileSync(filePath, content, 'utf8') + const data = Buffer.from(content); + fs.writeFileSync(filePath, data) return true }) @@ -122,6 +123,12 @@ module.exports = function registerIPCHandlers(win, ipcMain, app) { return app.getAppPath() }) + ipcMain.handle('open-dialog', (event, opt) => { + console.log('ipcMain', 'open-dialog', opt) + const response = dialog.showMessageBoxSync(win, opt) + return response != opt.cancelId + }) + win.on('close', (event) => { console.log('BrowserWindow', 'close') event.preventDefault() diff --git a/build_resources/windowsCustomSign.js b/build_resources/windowsCustomSign.js new file mode 100644 index 0000000..6ef107b --- /dev/null +++ b/build_resources/windowsCustomSign.js @@ -0,0 +1,30 @@ +const childProcess = require('child_process'); + +exports.default = async function (configuration) { + if (!process.env.GITHUB_ACTIONS) { + return; + } + + const SIGNTOOL_PATH = process.env.SIGNTOOL_PATH; + const INSTALLER_CERT_WINDOWS_CER = process.env.INSTALLER_CERT_WINDOWS_CER; + const CERT_PASSWORD = process.env.WIN_CERT_PASSWORD; + const CONTAINER_NAME = process.env.WIN_CERT_CONTAINER_NAME; + const filePath = configuration.path; + + if ( + SIGNTOOL_PATH && + INSTALLER_CERT_WINDOWS_CER && + CERT_PASSWORD && + CONTAINER_NAME + ) { + childProcess.execSync( + `"${SIGNTOOL_PATH}" sign -d "Arduino Lab for MicroPython" -f "${INSTALLER_CERT_WINDOWS_CER}" -csp "eToken Base Cryptographic Provider" -k "[{{${CERT_PASSWORD}}}]=${CONTAINER_NAME}" -fd sha256 -tr http://timestamp.digicert.com -td SHA256 -v "${filePath}"`, + { stdio: 'inherit' } + ); + } else { + console.warn( + `Custom windows signing was no performed one of the following variables was not provided: SIGNTOOL_PATH (${SIGNTOOL_PATH}), INSTALLER_CERT_WINDOWS_CERT (${INSTALLER_CERT_WINDOWS_CER}), CERT_PASSWORD (${CERT_PASSWORD}), CONTAINER_NAME (${CONTAINER_NAME})` + ); + process.exit(1); + } +}; diff --git a/index.js b/index.js index b38f989..57eba4c 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -const { app, BrowserWindow, ipcMain } = require('electron') +const { app, BrowserWindow, ipcMain, dialog } = require('electron') const path = require('path') const fs = require('fs') @@ -49,7 +49,7 @@ function createWindow () { win.show() }) - registerIPCHandlers(win, ipcMain, app) + registerIPCHandlers(win, ipcMain, app, dialog) registerMenu(win) app.on('activate', () => { diff --git a/package-lock.json b/package-lock.json index 883d73e..d6726c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "MIT", "dependencies": { "about-window": "^1.15.2", - "micropython.js": "github:arduino/micropython.js#v1.4.4" + "micropython.js": "github:arduino/micropython.js#v1.5.0" }, "devDependencies": { "electron": "^19.0.10", @@ -210,13 +210,10 @@ } }, "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -519,9 +516,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.18.87", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.87.tgz", - "integrity": "sha512-+IzfhNirR/MDbXz6Om5eHV54D9mQlEMGag6AgEzlju0xH3M8baCXYwqQ6RKgGMpn9wSTx6Ltya/0y4Z8eSfdLw==", + "version": "16.18.119", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.119.tgz", + "integrity": "sha512-ia7V9a2FnhUFfetng4/sRPBMTwHZUkPFY736rb1cg9AgG7MZdR97q7/nLR9om+sq5f1la9C857E0l/nrI0RiFQ==", "dev": true }, "node_modules/@types/plist": { @@ -552,9 +549,9 @@ "optional": true }, "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -748,13 +745,10 @@ } }, "node_modules/app-builder-lib/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -781,6 +775,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "delegates": "^1.0.0", @@ -853,9 +848,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, "node_modules/async-exit-hook": { @@ -952,6 +947,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "optional": true }, @@ -1150,6 +1146,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1501,9 +1498,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -1515,11 +1512,11 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1612,9 +1609,9 @@ "dev": true }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, "engines": { "node": ">=8" @@ -1766,9 +1763,9 @@ "dev": true }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "dependencies": { "jake": "^10.8.5" @@ -2262,13 +2259,10 @@ } }, "node_modules/electron-rebuild/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2366,9 +2360,9 @@ "optional": true }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -2485,9 +2479,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "dependencies": { "asynckit": "^0.4.0", @@ -2544,6 +2538,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -2604,6 +2599,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -2639,14 +2635,11 @@ } }, "node_modules/global-agent/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "optional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2671,13 +2664,14 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "optional": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -2788,9 +2782,9 @@ "dev": true }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "optional": true, "dependencies": { @@ -2944,6 +2938,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -3049,9 +3044,9 @@ "dev": true }, "node_modules/jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "dependencies": { "async": "^3.2.3", @@ -3270,8 +3265,8 @@ } }, "node_modules/micropython.js": { - "version": "1.4.4", - "resolved": "git+ssh://git@github.com/arduino/micropython.js.git#1200053178d9be82ec47cbde26d7f1f7fece6f85", + "version": "1.5.0", + "resolved": "git+ssh://git@github.com/arduino/micropython.js.git#7657256c7f7c37d9f3b3112deb98d64665c68c65", "dependencies": { "serialport": "^10.4.0" }, @@ -3454,23 +3449,23 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/node-abi": { - "version": "3.56.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz", - "integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==", + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", "dev": true, "dependencies": { "semver": "^7.3.5" @@ -3480,13 +3475,10 @@ } }, "node_modules/node-abi/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -3511,13 +3503,10 @@ } }, "node_modules/node-api-version/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -3551,9 +3540,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.3.tgz", + "integrity": "sha512-EMS95CMJzdoSKoIiXo8pxKoL8DYxwIZXYlLmgPb8KUv794abpnLK6ynsCAWNliOjREKruYKdzbh76HHYUHX7nw==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -3561,13 +3550,10 @@ } }, "node_modules/node-gyp/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -3617,6 +3603,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "are-we-there-yet": "^3.0.0", @@ -3808,9 +3795,9 @@ "optional": true }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "dependencies": { "end-of-stream": "^1.1.0", @@ -3919,6 +3906,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -3970,9 +3958,9 @@ } }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true }, "node_modules/semver": { @@ -4114,9 +4102,9 @@ } }, "node_modules/socks": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", - "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, "dependencies": { "ip-address": "^9.0.5", @@ -4247,9 +4235,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -4449,9 +4437,9 @@ } }, "node_modules/utf8-byte-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", "dev": true }, "node_modules/util-deprecate": { diff --git a/package.json b/package.json index 2d3f773..0c4d3a5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "arduino-lab-micropython-ide", "productName": "Arduino Lab for MicroPython", - "version": "0.10.0", + "version": "0.11.0", "description": "Arduino Lab for MicroPython is a project sponsored by Arduino, based on original work by Murilo Polese.\nThis is an experimental pre-release software, please direct any questions exclusively to Github issues.", "main": "index.js", "scripts": { @@ -28,6 +28,7 @@ }, "win": { "target": "zip", + "sign": "./build_resources/windowsCustomSign.js", "icon": "build_resources/icon.png" }, "linux": { @@ -39,7 +40,7 @@ "license": "MIT", "dependencies": { "about-window": "^1.15.2", - "micropython.js": "github:arduino/micropython.js#v1.4.4" + "micropython.js": "github:arduino/micropython.js#v1.5.0" }, "engines": { "node": "18" diff --git a/preload.js b/preload.js index 3388904..ddcb8aa 100644 --- a/preload.js +++ b/preload.js @@ -50,7 +50,7 @@ const Serial = { return board.fs_ils(folder) }, loadFile: async (file) => { - const output = await board.fs_cat(file) + const output = await board.fs_cat_binary(file) return output || '' }, removeFile: async (file) => { @@ -60,7 +60,7 @@ const Serial = { return board.fs_save(content || ' ', filename, dataConsumer) }, uploadFile: async (src, dest, dataConsumer) => { - return board.fs_put(src, dest, dataConsumer) + return board.fs_put(src, dest.replaceAll(path.win32.sep, path.posix.sep), dataConsumer) }, downloadFile: async (src, dest) => { let contents = await Serial.loadFile(src) @@ -157,7 +157,8 @@ const Window = { }, beforeClose: (callback) => ipcRenderer.on('check-before-close', callback), confirmClose: () => ipcRenderer.invoke('confirm-close'), - isPackaged: () => ipcRenderer.invoke('is-packaged') + isPackaged: () => ipcRenderer.invoke('is-packaged'), + openDialog: (opt) => ipcRenderer.invoke('open-dialog', opt) } diff --git a/ui/arduino/main.css b/ui/arduino/main.css index 3a2eca0..2c94b7d 100644 --- a/ui/arduino/main.css +++ b/ui/arduino/main.css @@ -652,6 +652,7 @@ button.small .icon { transition: all 0.25s; pointer-events: none; opacity: 0; + text-align: center; } #overlay.open { diff --git a/ui/arduino/store.js b/ui/arduino/store.js index adb79d5..2c93a7e 100644 --- a/ui/arduino/store.js +++ b/ui/arduino/store.js @@ -8,6 +8,19 @@ const newFileContent = `# This program was created in Arduino Lab for MicroPytho print('Hello, MicroPython!') ` +async function confirm(msg, cancelMsg, confirmMsg) { + cancelMsg = cancelMsg || 'Cancel' + confirmMsg = confirmMsg || 'Yes' + let response = await win.openDialog({ + type: 'question', + buttons: [cancelMsg, confirmMsg], + cancelId: 0, + message: msg + }) + console.log('confirm', response) + return Promise.resolve(response) +} + async function store(state, emitter) { win.setWindowSize(720, 640) @@ -117,10 +130,6 @@ async function store(state, emitter) { // Recover from getting stuck in raw repl await serial.getPrompt() - // Make sure there is a lib folder - log('creating lib folder') - await serial.createFolder('/lib') - // Connected and ready state.isConnecting = false state.isConnected = true @@ -317,7 +326,7 @@ async function store(state, emitter) { } if (willOverwrite) { - const confirmation = confirm(`You are about to overwrite the file ${openFile.fileName} on your ${openFile.source}.\n\n Are you sure you want to proceed?`, 'Cancel', 'Yes') + const confirmation = await confirm(`You are about to overwrite the file ${openFile.fileName} on your ${openFile.source}.\n\n Are you sure you want to proceed?`, 'Cancel', 'Yes') if (!confirmation) { state.isSaving = false openFile.parentFolder = oldParentFolder @@ -370,11 +379,11 @@ async function store(state, emitter) { state.editingFile = id emitter.emit('render') }) - emitter.on('close-tab', (id) => { + emitter.on('close-tab', async (id) => { log('close-tab', id) const currentTab = state.openFiles.find(f => f.id === id) - if (currentTab.hasChanges && currentTab.parentFolder !== null) { - let response = confirm("Your file has unsaved changes. Are you sure you want to proceed?") + if (currentTab.hasChanges) { + let response = await confirm("Your file has unsaved changes. Are you sure you want to proceed?") if (!response) return false } state.openFiles = state.openFiles.filter(f => f.id !== id) @@ -478,7 +487,7 @@ async function store(state, emitter) { fileName: value }) if (willOverwrite) { - const confirmAction = confirm(`You are about to overwrite the file ${value} on your board.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') + const confirmAction = await confirm(`You are about to overwrite the file ${value} on your board.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') if (!confirmAction) { state.creatingFile = null emitter.emit('render') @@ -501,7 +510,7 @@ async function store(state, emitter) { fileName: value }) if (willOverwrite) { - const confirmAction = confirm(`You are about to overwrite the file ${value} on your disk.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') + const confirmAction = await confirm(`You are about to overwrite the file ${value} on your disk.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') if (!confirmAction) { state.creatingFile = null emitter.emit('render') @@ -549,7 +558,7 @@ async function store(state, emitter) { fileName: value }) if (willOverwrite) { - const confirmAction = confirm(`You are about to overwrite ${value} on your board.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') + const confirmAction = await confirm(`You are about to overwrite ${value} on your board.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') if (!confirmAction) { state.creatingFolder = null emitter.emit('render') @@ -578,7 +587,7 @@ async function store(state, emitter) { fileName: value }) if (willOverwrite) { - const confirmAction = confirm(`You are about to overwrite ${value} on your disk.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') + const confirmAction = await confirm(`You are about to overwrite ${value} on your disk.\n\nAre you sure you want to proceed?`, 'Cancel', 'Yes') if (!confirmAction) { state.creatingFolder = null emitter.emit('render') @@ -635,7 +644,7 @@ async function store(state, emitter) { } message += `Are you sure you want to proceed?` - const confirmAction = confirm(message, 'Cancel', 'Yes') + const confirmAction = await confirm(message, 'Cancel', 'Yes') if (!confirmAction) { state.isRemoving = false emitter.emit('render') @@ -723,7 +732,7 @@ async function store(state, emitter) { let message = `You are about to overwrite the following file/folder on your board:\n\n` message += `${value}\n\n` message += `Are you sure you want to proceed?` - const confirmAction = confirm(message, 'Cancel', 'Yes') + const confirmAction = await confirm(message, 'Cancel', 'Yes') if (!confirmAction) { state.isSaving = false state.renamingFile = null @@ -762,7 +771,7 @@ async function store(state, emitter) { let message = `You are about to overwrite the following file/folder on your disk:\n\n` message += `${value}\n\n` message += `Are you sure you want to proceed?` - const confirmAction = confirm(message, 'Cancel', 'Yes') + const confirmAction = await confirm(message, 'Cancel', 'Yes') if (!confirmAction) { state.isSaving = false state.renamingFile = null @@ -917,7 +926,7 @@ async function store(state, emitter) { } if (willOverwrite) { - const confirmation = confirm(`You are about to overwrite the file ${openFile.fileName} on your ${openFile.source}.\n\n Are you sure you want to proceed?`, 'Cancel', 'Yes') + const confirmation = await confirm(`You are about to overwrite the file ${openFile.fileName} on your ${openFile.source}.\n\n Are you sure you want to proceed?`, 'Cancel', 'Yes') if (!confirmation) { state.renamingTab = null state.isSaving = false @@ -1094,11 +1103,12 @@ async function store(state, emitter) { selectedFile.fileName ) ) + const bytesToSource = String.fromCharCode(...fileContent); file = createFile({ parentFolder: state.boardNavigationPath, fileName: selectedFile.fileName, source: selectedFile.source, - content: fileContent + content: bytesToSource }) file.editor.onChange = function() { file.hasChanges = true @@ -1178,7 +1188,7 @@ async function store(state, emitter) { willOverwrite.forEach(f => message += `${f.fileName}\n`) message += `\n` message += `Are you sure you want to proceed?` - const confirmAction = confirm(message, 'Cancel', 'Yes') + const confirmAction = await confirm(message, 'Cancel', 'Yes') if (!confirmAction) { state.isTransferring = false emitter.emit('render') @@ -1201,16 +1211,16 @@ async function store(state, emitter) { if (file.type == 'folder') { await uploadFolder( srcPath, destPath, - (e) => { - state.transferringProgress = e + (progress, fileName) => { + state.transferringProgress = `${fileName}: ${progress}` emitter.emit('render') } ) } else { await serial.uploadFile( srcPath, destPath, - (e) => { - state.transferringProgress = e + (progress) => { + state.transferringProgress = `${file.fileName}: ${progress}` emitter.emit('render') } ) @@ -1243,7 +1253,7 @@ async function store(state, emitter) { willOverwrite.forEach(f => message += `${f.fileName}\n`) message += `\n` message += `Are you sure you want to proceed?` - const confirmAction = confirm(message, 'Cancel', 'Yes') + const confirmAction = await confirm(message, 'Cancel', 'Yes') if (!confirmAction) { state.isTransferring = false emitter.emit('render') @@ -1328,9 +1338,9 @@ async function store(state, emitter) { }) win.beforeClose(async () => { - const hasChanges = !!state.openFiles.find(f => f.parentFolder && f.hasChanges) + const hasChanges = !!state.openFiles.find(f => f.hasChanges) if (hasChanges) { - const response = await confirm('You may have unsaved changes. Are you sure you want to proceed?', 'Yes', 'Cancel') + const response = await confirm('You may have unsaved changes. Are you sure you want to proceed?', 'Cancel', 'Yes') if (!response) return false } await win.confirmClose() @@ -1525,7 +1535,9 @@ async function uploadFolder(srcPath, destPath, dataConsumer) { await serial.uploadFile( disk.getFullPath(srcPath, relativePath, ''), serial.getFullPath(destPath, relativePath, ''), - dataConsumer + (progress) => { + dataConsumer(progress, relativePath) + } ) } } diff --git a/ui/arduino/views/components/overlay.js b/ui/arduino/views/components/overlay.js index 1b9389c..3f34507 100644 --- a/ui/arduino/views/components/overlay.js +++ b/ui/arduino/views/components/overlay.js @@ -10,7 +10,7 @@ function Overlay(state, emit) { if (state.isConnecting) overlay = html`
Connecting...
Loading files...
Saving file... ${state.savingProgress}
Transferring file... ${state.transferringProgress}
Transferring file
${state.transferringProgress}