fix(led-tool): harden Web Serial raw REPL connect

Improve boot wait, readUntil buffer handling, and settings editor
host/Web Serial flows after device reset.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-19 00:23:08 +12:00
parent f74e21f206
commit bd4d2060ae
3 changed files with 353 additions and 54 deletions

View File

@@ -24,8 +24,10 @@
let hostConnected = false;
let hostConnectedPort = '';
let transferBusy = false;
let webConnectBusy = false;
const webSerialOk = window.LedToolWebSerial && window.LedToolWebSerial.supported();
let webSerialGrantedCount = 0;
function setStatus(text, isError) {
if (!statusEl) return;
@@ -40,6 +42,17 @@
setTimeout(() => messageEl.classList.remove('show'), 5000);
}
function errorMessage(e) {
if (!e) return 'Unknown error';
if (typeof e === 'string') return e;
return e.message || String(e);
}
function onWebSerialTransferError(e) {
if (webClient) webClient.inRawRepl = false;
return errorMessage(e);
}
function formToObject() {
const out = {};
document.querySelectorAll('[data-setting]').forEach((el) => {
@@ -99,7 +112,11 @@
} else if (hostOpen) {
setStatus(`Host serial: ${hostConnectedPort} (use Download or Upload).`);
} else if (webSerialOk) {
setStatus('Connect host serial or Web Serial USB, then Download or Upload.');
if (webSerialGrantedCount === 1) {
setStatus('Connect USB (one remembered port) or host serial, then Download or Upload.');
} else {
setStatus('Connect host serial or Web Serial USB, then Download or Upload.');
}
} else {
setStatus(`Connect host serial (default ${DEFAULT_HOST_PORT}).`);
}
@@ -205,21 +222,31 @@
}
async function connectWebSerial() {
if (webConnectBusy) return;
if (!window.LedToolWebSerial || !window.LedToolWebSerial.supported()) {
flash('Web Serial not supported (use Chrome or Edge over HTTPS or localhost).', true);
return;
}
if (usingWebSerial()) {
flash('USB already connected.', false);
return;
}
if (usingHostSerial()) {
disconnectHostSerial();
}
log('connectWebSerial: requestPort');
setStatus('Opening USB port…');
webPort = await window.LedToolWebSerial.requestPort();
log('connectWebSerial: port selected', webPort);
webClient = new window.LedToolWebSerial.MicroPythonRawRepl();
await webClient.connect(webPort);
updateUi();
flash('USB port open. Use Download or Upload to talk to the device.', false);
webConnectBusy = true;
try {
log('connectWebSerial: requestPort');
setStatus('Opening USB port…');
webPort = await window.LedToolWebSerial.requestPort();
log('connectWebSerial: port selected', webPort);
webClient = new window.LedToolWebSerial.MicroPythonRawRepl();
await webClient.connect(webPort);
updateUi();
flash('USB port open. Wait for boot, then Download.', false);
} finally {
webConnectBusy = false;
}
}
async function disconnectWebSerial() {
@@ -284,9 +311,10 @@
}
} catch (e) {
console.error(LOG, 'download failed', e);
if (rawJson) rawJson.value = String(e.message || e);
flash(e.message, true);
setStatus(e.message, true);
const msg = usingWebSerial() ? onWebSerialTransferError(e) : errorMessage(e);
if (rawJson) rawJson.value = msg;
flash(msg, true);
setStatus(msg, true);
} finally {
transferBusy = false;
}
@@ -310,8 +338,9 @@
}
} catch (e) {
console.error(LOG, 'upload failed', e);
flash(e.message, true);
setStatus(e.message, true);
const msg = usingWebSerial() ? onWebSerialTransferError(e) : errorMessage(e);
flash(msg, true);
setStatus(msg, true);
} finally {
transferBusy = false;
}
@@ -374,7 +403,19 @@
}
});
async function refreshWebSerialGrantHint() {
if (!webSerialOk || !window.LedToolWebSerial.getGrantedPorts) return;
try {
const ports = await window.LedToolWebSerial.getGrantedPorts();
webSerialGrantedCount = ports.length;
updateUi();
} catch (_) {
/* ignore */
}
}
log('settings editor ready', { apiBase, webSerialOk });
updateUi();
loadHostPorts();
refreshWebSerialGrantHint();
})();