From 8643b193a83ee1444ed90ca7746eb52678bf1568 Mon Sep 17 00:00:00 2001 From: Geoff McIntyre Date: Wed, 24 Jun 2026 17:23:37 -0600 Subject: [PATCH 1/2] Updated Web Compilation error --- package.json | 2 +- .../arduino/WebSocketArduinoService.ts | 73 ++++++++++++++++--- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 9409132..bb9e4a0 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/utils": "^4.0.0", "@hookform/resolvers": "^5.1.1", - "@mister-industries/shared": "^1.0.0", + "@mister-industries/shared": "^1.1.0", "@mister-industries/tinyservice": "^1.0.0", "@monaco-editor/react": "^4.7.0-rc.0", "@reduxjs/toolkit": "^2.8.2", diff --git a/src/renderer/src/services/arduino/WebSocketArduinoService.ts b/src/renderer/src/services/arduino/WebSocketArduinoService.ts index 7161c16..2e455d0 100644 --- a/src/renderer/src/services/arduino/WebSocketArduinoService.ts +++ b/src/renderer/src/services/arduino/WebSocketArduinoService.ts @@ -32,9 +32,37 @@ import { PlatformEntry, UploadResult } from './types' +import { isVirtualPath, virtualFileSystem } from '@renderer/lib/virtualFileSystem' const DEFAULT_SERVICE_URL = 'ws://localhost:3000' +/** + * Gather a `mem://` sketch's files into a flat `{ relativePath: content }` map + * plus a sketch folder name. The browser can't hand tinyService a real disk + * path (the File System Access API hides absolute paths, and examples live only + * in memory), so the web build ships the sketch's contents instead and the + * service materializes them to a temp dir to compile/upload. Desktop never + * calls this — it passes a real path straight through. + */ +async function collectVirtualSketch( + sketchDir: string +): Promise<{ files: Record; sketchName: string }> { + const root = sketchDir.endsWith('/') ? sketchDir.slice(0, -1) : sketchDir + const items = await virtualFileSystem.readDirectory(root, true) + const files: Record = {} + for (const item of items) { + if (item.isDirectory) continue + const rel = item.path.startsWith(root + '/') + ? item.path.slice(root.length + 1) + : item.name + files[rel] = await virtualFileSystem.readFile(item.path) + } + if (Object.keys(files).length === 0) { + throw new Error(`No files found in ${root} to compile`) + } + return { files, sketchName: root.slice(root.lastIndexOf('/') + 1) } +} + /** Resolve the tinyService URL, allowing a localStorage override for hosting. */ function resolveServiceUrl(): string { try { @@ -435,17 +463,20 @@ export class WebSocketArduinoService implements ArduinoService { throw new Error('Arduino client not initialized') } - if (workspacePath.startsWith('mem://')) { - const msg = - 'This example is open in the browser (in-memory) and has no folder on disk for the compiler. Open it in the desktop app, or save it to a local folder, to compile and upload.' - return { success: false, output: msg, errors: [{ message: msg, severity: 'fatal' }] } + // Browser: the sketch lives in the in-memory FS with no disk path, so + // ship its files for the service to materialize and build. Desktop passes + // the real path straight through. + let files: Record | undefined + let sketchName: string | undefined + if (isVirtualPath(workspacePath)) { + ;({ files, sketchName } = await collectVirtualSketch(workspacePath)) } // Compile the sketch console.log( `Starting compile operation for workspace: ${workspacePath}, FQBN: ${boardConfig.fqbn}` ) - this.client.compile(workspacePath, boardConfig.fqbn) + this.client.compile(workspacePath, boardConfig.fqbn, files, sketchName) // Wait for response with longer timeout for compilation const result = await this.waitForResponse('compile', 120000) @@ -480,17 +511,20 @@ export class WebSocketArduinoService implements ArduinoService { throw new Error('Arduino client not initialized') } - if ((workspacePathOrBinary || '').startsWith('mem://')) { - const msg = - 'This example is open in the browser (in-memory) and has no folder on disk for the compiler. Open it in the desktop app, or save it to a local folder, to compile and upload.' - return { success: false, output: msg, error: msg } + // Browser: ship the in-memory sketch's files; the service compiles them + // into a temp dir before uploading (arduino-cli's upload needs a build + // present). Desktop passes the real path and relies on its prior compile. + let files: Record | undefined + let sketchName: string | undefined + if (isVirtualPath(workspacePathOrBinary)) { + ;({ files, sketchName } = await collectVirtualSketch(workspacePathOrBinary!)) } // Upload the sketch console.log( `Starting upload operation to port: ${port}, FQBN: ${boardConfig.fqbn}, workspace: ${workspacePathOrBinary}` ) - this.client.upload(workspacePathOrBinary || '', boardConfig.fqbn, port) + this.client.upload(workspacePathOrBinary || '', boardConfig.fqbn, port, files, sketchName) // Wait for response with longer timeout for upload const result = await this.waitForResponse('upload', 90000) @@ -520,6 +554,25 @@ export class WebSocketArduinoService implements ArduinoService { boardConfig: BoardConfig ): Promise<{ compile: CompileResult; upload: UploadResult }> { try { + // Browser: the service compiles the shipped files into a temp dir as part + // of the upload, so a separate compile pass here would just build in a + // throwaway dir and double the (slow) compile. Send one upload instead. + if (isVirtualPath(workspacePath)) { + const uploadResult = await this.uploadSketch(port, boardConfig, workspacePath) + return { + compile: { + success: uploadResult.success, + output: uploadResult.success + ? 'Compiled and uploaded' + : uploadResult.output || '', + errors: uploadResult.success + ? undefined + : [{ message: uploadResult.error || 'Compilation failed', severity: 'fatal' }] + }, + upload: uploadResult + } + } + // Compile first const compileResult = await this.compileSketch(workspacePath, boardConfig) From 2f01b8f9316b1813e020363d239a90ca10dd3f7a Mon Sep 17 00:00:00 2001 From: Geoff McIntyre Date: Wed, 24 Jun 2026 17:34:27 -0600 Subject: [PATCH 2/2] Updated tinyService version --- package-lock.json | 22 +++++++++++----------- package.json | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4aacaec..6ad8b98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tinystudio", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "tinystudio", - "version": "0.1.0", + "version": "0.1.1", "hasInstallScript": true, "license": "GPL-3.0-or-later", "dependencies": { @@ -14,8 +14,8 @@ "@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/utils": "^4.0.0", "@hookform/resolvers": "^5.1.1", - "@mister-industries/shared": "^1.0.0", - "@mister-industries/tinyservice": "^1.0.0", + "@mister-industries/shared": "^1.1.1", + "@mister-industries/tinyservice": "^1.0.1", "@monaco-editor/react": "^4.7.0-rc.0", "@reduxjs/toolkit": "^2.8.2", "@tailwindcss/vite": "^4.1.8", @@ -1891,9 +1891,9 @@ } }, "node_modules/@mister-industries/shared": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@mister-industries/shared/-/shared-1.0.0.tgz", - "integrity": "sha512-orbMbKwhDDWkpgxUQx0CpOnEd3eoLJrditySvR1ixlw5JekKc5ikx4nh6YTu94/gV1AitgY4fdSfKoswKzgtAg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mister-industries/shared/-/shared-1.1.1.tgz", + "integrity": "sha512-CIBBaf24uZ49t/7gWDRCY1Ghq0bWVarHfJ76b5ZPbZoatZZHlFFWYmr6ZxCXY2WGItK5oXVNUqryHLmjJ/jr8w==", "license": "MIT", "peerDependencies": { "react": "^18.0.0 || ^19.0.0" @@ -1905,12 +1905,12 @@ } }, "node_modules/@mister-industries/tinyservice": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@mister-industries/tinyservice/-/tinyservice-1.0.0.tgz", - "integrity": "sha512-x5PmtcoVHxNTFdLrrlRmQSBXCR6KDSu1q09LcaX2AeP6GMwY/+VmB3E0cuxz5lA64Uf9LsI5j3Ns2AFtyXsAVA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@mister-industries/tinyservice/-/tinyservice-1.0.1.tgz", + "integrity": "sha512-TV4A1zpedGaIfxm9J7HhxlaVRwkVxoZrB7B7NA/dAfEogBcXAlyh7jXknyXnsegUw/jnzDGgqXoXwLIkSK3YmA==", "license": "MIT", "dependencies": { - "@mister-industries/shared": "^1.0.0", + "@mister-industries/shared": "^1.1.1", "express": "^4.18.2", "uuid": "^9.0.1", "ws": "^8.14.2" diff --git a/package.json b/package.json index bb9e4a0..8d2b037 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ "@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/utils": "^4.0.0", "@hookform/resolvers": "^5.1.1", - "@mister-industries/shared": "^1.1.0", - "@mister-industries/tinyservice": "^1.0.0", + "@mister-industries/shared": "^1.1.1", + "@mister-industries/tinyservice": "^1.0.1", "@monaco-editor/react": "^4.7.0-rc.0", "@reduxjs/toolkit": "^2.8.2", "@tailwindcss/vite": "^4.1.8",