From 4e0c70b084ae214da540b55a5cca86db0dde3083 Mon Sep 17 00:00:00 2001 From: makermelissa-ai-assistant <276182804+makermelissa-ai-assistant@users.noreply.github.com> Date: Thu, 23 Jul 2026 11:54:00 -0700 Subject: [PATCH 1/3] Use serial file transfer on supported firmware --- js/workflows/usb.js | 62 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index 728c17e..7be44d7 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -25,6 +25,7 @@ class USBWorkflow extends Workflow { this._messageCallback = null; this._btnSelectHostFolderCallback = null; this._btnUseHostFolderCallback = null; + this._usbDriveDisabled = false; this.buttonStates = [ {request: false, select: false}, {request: true, select: false}, @@ -63,6 +64,12 @@ class USBWorkflow extends Workflow { } this._isDisconnecting = true; + // Newer CircuitPython firmware lets the editor temporarily make the + // filesystem writable over the REPL by taking the CIRCUITPY USB drive + // offline. Put the drive back before closing the serial connection so + // it is available to the host again after a clean disconnect. + await this._restoreUsbDrive(); + // If we got here because the underlying device was lost (NetworkError), // the writable stream is errored and any further writes will throw // unhandled NetworkErrors. Force reconnect=false so we don't loop @@ -321,6 +328,55 @@ class USBWorkflow extends Workflow { } } + async _trySerialFileTransfer() { + const disabledMarker = "WEB_EDITOR_USB_DRIVE_DISABLED"; + const unavailableMarker = "WEB_EDITOR_USB_DRIVE_UNAVAILABLE"; + const code = ` +try: + import storage + if hasattr(storage, "unsafe_disable_usb_drive"): + storage.unsafe_disable_usb_drive() + print("${disabledMarker}") + else: + print("${unavailableMarker}") +except Exception: + print("${unavailableMarker}") +`; + + try { + const result = await this.showBusy(this.repl.runCode(code)); + if (String(result || "").includes(disabledMarker)) { + this._usbDriveDisabled = true; + console.log("CIRCUITPY USB drive disabled; using serial file transfer"); + return true; + } + } catch (error) { + console.warn("Unable to disable the CIRCUITPY USB drive:", error); + } + return false; + } + + async _restoreUsbDrive() { + if (!this._usbDriveDisabled) { + return; + } + + try { + await this.repl.runCode( +`import storage +storage.enable_usb_drive()` + ); + console.log("CIRCUITPY USB drive restored"); + } catch (error) { + // A physical disconnect makes the REPL unavailable before this + // cleanup can run. The next hard reset restores the firmware's + // default USB drive setting. + console.warn("Unable to restore the CIRCUITPY USB drive:", error); + } finally { + this._usbDriveDisabled = false; + } + } + // Workflow specific Functions async _switchToDevice(device) { device.removeEventListener("message", this._messageCallback); @@ -366,6 +422,12 @@ class USBWorkflow extends Workflow { // At this point we should see if we should init the file client and check if have a saved dir handle let fileops = new FileOps(this.repl, false); if (await this.showBusy(fileops.isReadOnly())) { + if (await this._trySerialFileTransfer()) { + this.initFileClient(new ReplFileTransferClient(this.connectionStatus.bind(this), this.repl)); + this.onConnected(); + return; + } + // UID Only needed for matching the CIRCUITPY drive with the Serial Terminal await this.showBusy(this._getDeviceUid()); let modal = this.connectDialog.getModal(); From 607e2b1fc550999d4e8fcdde410a541a0cd5ecde Mon Sep 17 00:00:00 2001 From: makermelissa-ai-assistant <276182804+makermelissa-ai-assistant@users.noreply.github.com> Date: Thu, 23 Jul 2026 12:02:22 -0700 Subject: [PATCH 2/3] Use circuitpython-repl-js USB helpers --- js/workflows/usb.js | 27 ++++++--------------------- package-lock.json | 5 ++--- package.json | 2 +- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index 7be44d7..845211b 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -329,23 +329,8 @@ class USBWorkflow extends Workflow { } async _trySerialFileTransfer() { - const disabledMarker = "WEB_EDITOR_USB_DRIVE_DISABLED"; - const unavailableMarker = "WEB_EDITOR_USB_DRIVE_UNAVAILABLE"; - const code = ` -try: - import storage - if hasattr(storage, "unsafe_disable_usb_drive"): - storage.unsafe_disable_usb_drive() - print("${disabledMarker}") - else: - print("${unavailableMarker}") -except Exception: - print("${unavailableMarker}") -`; - try { - const result = await this.showBusy(this.repl.runCode(code)); - if (String(result || "").includes(disabledMarker)) { + if (await this.showBusy(this.repl.tryDisableUsbDrive())) { this._usbDriveDisabled = true; console.log("CIRCUITPY USB drive disabled; using serial file transfer"); return true; @@ -362,11 +347,11 @@ except Exception: } try { - await this.repl.runCode( -`import storage -storage.enable_usb_drive()` - ); - console.log("CIRCUITPY USB drive restored"); + if (await this.repl.enableUsbDrive()) { + console.log("CIRCUITPY USB drive restored"); + } else { + console.warn("CircuitPython could not restore the CIRCUITPY USB drive"); + } } catch (error) { // A physical disconnect makes the REPL unavailable before this // cleanup can run. The next hard reset restores the firmware's diff --git a/package-lock.json b/package-lock.json index fdd68eb..bb28abc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.5", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.3.0", + "@adafruit/circuitpython-repl-js": "github:makermelissa-ai-assistant/circuitpython-repl-js#98b7243", "@astral-sh/ruff-wasm-web": "^0.15.21", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.11", @@ -46,8 +46,7 @@ }, "node_modules/@adafruit/circuitpython-repl-js": { "version": "3.3.0", - "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#90b3f4e0856d9f355309b80457319fcce84524b2", - "integrity": "sha512-/pWFRIAwwwYXbHfNPw0v6YjX09EjC2yEXAZ98RnVhiQIGh6+N6W9hNKRyCPOBYJvIVXftlyLDHzHc2paV+Oefg==", + "resolved": "git+ssh://git@github.com/makermelissa-ai-assistant/circuitpython-repl-js.git#98b7243c9e8373174ce856717c27000ed5923d32", "license": "MIT" }, "node_modules/@astral-sh/ruff-wasm-web": { diff --git a/package.json b/package.json index cb6a9be..024edad 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.5", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.3.0", + "@adafruit/circuitpython-repl-js": "github:makermelissa-ai-assistant/circuitpython-repl-js#98b7243", "@astral-sh/ruff-wasm-web": "^0.15.21", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.11", From 710bed5efe0f0d1233e0bf2e0f616acf3e0fba6c Mon Sep 17 00:00:00 2001 From: makermelissa-ai-assistant <276182804+makermelissa-ai-assistant@users.noreply.github.com> Date: Thu, 23 Jul 2026 12:14:04 -0700 Subject: [PATCH 3/3] Use circuitpython-repl-js 3.4.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb28abc..e2d790a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.5", - "@adafruit/circuitpython-repl-js": "github:makermelissa-ai-assistant/circuitpython-repl-js#98b7243", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.4.0", "@astral-sh/ruff-wasm-web": "^0.15.21", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.11", @@ -46,7 +46,7 @@ }, "node_modules/@adafruit/circuitpython-repl-js": { "version": "3.3.0", - "resolved": "git+ssh://git@github.com/makermelissa-ai-assistant/circuitpython-repl-js.git#98b7243c9e8373174ce856717c27000ed5923d32", + "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#a36b73c07f28f447a4a54d7d03a85ef9b3c36be7", "license": "MIT" }, "node_modules/@astral-sh/ruff-wasm-web": { diff --git a/package.json b/package.json index 024edad..aebe1f3 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.5", - "@adafruit/circuitpython-repl-js": "github:makermelissa-ai-assistant/circuitpython-repl-js#98b7243", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.4.0", "@astral-sh/ruff-wasm-web": "^0.15.21", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.11",