From 98b975202ced1940e642796f28a868e96dc492cc Mon Sep 17 00:00:00 2001 From: raldone01 Date: Sat, 5 Jul 2025 19:42:44 +0200 Subject: Minor bug fixes. Persist settings. --- .../device/webusb_serial/website/application.js | 136 +++++++++++++-------- examples/device/webusb_serial/website/index.html | 2 +- 2 files changed, 89 insertions(+), 49 deletions(-) (limited to 'examples') diff --git a/examples/device/webusb_serial/website/application.js b/examples/device/webusb_serial/website/application.js index a94a96714..8c01ce111 100644 --- a/examples/device/webusb_serial/website/application.js +++ b/examples/device/webusb_serial/website/application.js @@ -25,17 +25,60 @@ let lastCommandCount = 0; let lastCommandButton = null; - let sendMode = 'command'; // default mode + let sendMode = localStorage.getItem('sendMode') || 'command'; - let reconnectIntervalId = null; // track reconnect interval + // track reconnect interval + let reconnectIntervalId = null; - // Format incoming data to string based on newline mode + // Append sent command to sender container as a clickable element + const appendCommandToSender = (container, text) => { + if (text === lastCommand) { + // Increment count and update button + lastCommandCount++; + lastCommandButton.textContent = `${text} ×${lastCommandCount}`; + } else { + // Reset count and add new button + lastCommand = text; + lastCommandCount = 1; + + const commandEl = document.createElement('button'); + commandEl.className = 'sender-entry'; + commandEl.type = 'button'; + commandEl.textContent = text; + commandEl.addEventListener('click', () => { + commandLine.value = text; + commandLine.focus(); + }); + container.appendChild(commandEl); + lastCommandButton = commandEl; + + const distanceFromBottom = container.scrollHeight - (container.scrollTop + container.clientHeight); + if (distanceFromBottom < nearTheBottomThreshold) { + requestAnimationFrame(() => { + commandEl.scrollIntoView({ behavior: 'instant' }); + }); + } + } + }; + + // Restore command history + history.push(...(JSON.parse(localStorage.getItem('commandHistory') || '[]'))); + for (const cmd of history) { + appendCommandToSender(senderLines, cmd); + } + + // Restore auto reconnect checkbox + autoReconnectCheckbox.checked = localStorage.getItem('autoReconnect') === 'true'; + // Restore newline mode + const savedNewlineMode = localStorage.getItem('newlineMode'); + if (savedNewlineMode) newlineModeSelect.value = savedNewlineMode; + + // Format incoming data const decodeData = (() => { const decoder = new TextDecoder(); return dataView => decoder.decode(dataView); })(); - // Normalize newline if mode is ANY const normalizeNewlines = (text, mode) => { switch (mode) { case 'CR': @@ -67,37 +110,6 @@ } }; - // Append sent command to sender container as a clickable element - const appendCommandToSender = (container, text) => { - if (text === lastCommand) { - // Increment count and update button - lastCommandCount++; - lastCommandButton.textContent = `${text} ×${lastCommandCount}`; - } else { - // Reset count and add new button - lastCommand = text; - lastCommandCount = 1; - - const commandEl = document.createElement('button'); - commandEl.className = 'sender-entry'; - commandEl.type = 'button'; - commandEl.textContent = text; - commandEl.addEventListener('click', () => { - commandLine.value = text; - commandLine.focus(); - }); - container.appendChild(commandEl); - lastCommandButton = commandEl; - - const distanceFromBottom = container.scrollHeight - (container.scrollTop + container.clientHeight); - if (distanceFromBottom < nearTheBottomThreshold) { - requestAnimationFrame(() => { - commandEl.scrollIntoView({ behavior: 'instant' }); - }); - } - } - }; - // Update status text and style const setStatus = (msg, level = 'info') => { console.log(msg); @@ -120,7 +132,7 @@ }; // Connect helper - const connectPort = async (initial=false) => { + const connectPort = async (initial = false) => { try { let grantedDevices = await serial.getPorts(); if (grantedDevices.length === 0 && initial) { @@ -148,24 +160,27 @@ } await port.connect(); - lastPort = port; // save for reconnecting + // save for reconnecting + lastPort = port; setStatus(`Connected to ${port.device.productName || 'device'}`, 'info'); connectBtn.textContent = 'Disconnect'; commandLine.disabled = false; commandLine.focus(); + port.onReceiveError = async error => { + setStatus(`Read error: ${error.message}`, 'error'); + await disconnectPort(); + // Start auto reconnect on error if enabled + await tryAutoReconnect(); + }; + port.onReceive = dataView => { let text = decodeData(dataView); text = normalizeNewlines(text, newlineModeSelect.value); appendLineToReceiver(receiverLines, text, 'received'); }; - port.onReceiveError = error => { - setStatus(`Read error: ${error.message}`, 'error'); - // Start auto reconnect on error if enabled - tryAutoReconnect(); - }; return true; } catch (error) { setStatus(`Connection failed: ${error.message}`, 'error'); @@ -177,7 +192,7 @@ }; // Start auto reconnect interval if checkbox is checked and not already running - const tryAutoReconnect = () => { + const tryAutoReconnect = async () => { if (!autoReconnectCheckbox.checked) return; if (reconnectIntervalId !== null) return; // already trying setStatus('Attempting to auto-reconnect...', 'info'); @@ -238,13 +253,16 @@ }); // Checkbox toggle stops auto reconnect if unchecked - autoReconnectCheckbox.addEventListener('change', () => { + autoReconnectCheckbox.addEventListener('change', async () => { + localStorage.setItem('autoReconnect', autoReconnectCheckbox.checked); if (!autoReconnectCheckbox.checked) { stopAutoReconnect(); } else { // Start auto reconnect immediately if not connected - if (!port) { - tryAutoReconnect(); + console.log(port); + console.log(lastPort); + if (!port && lastPort) { + await tryAutoReconnect(); } } }); @@ -263,8 +281,16 @@ sendModeBtn.classList.add('send-mode-command'); sendModeBtn.textContent = 'Command mode'; } + localStorage.setItem('sendMode', sendMode); }); + // Set initial sendMode button state + if (sendMode === 'instant') { + sendModeBtn.classList.remove('send-mode-command'); + sendModeBtn.classList.add('send-mode-instant'); + sendModeBtn.textContent = 'Instant mode'; + } + // Send command line input on Enter commandLine.addEventListener('keydown', async e => { if (!port) return; @@ -314,7 +340,8 @@ await port.send(encoder.encode(sendText)); } catch (error) { setStatus(`Send error: ${error.message}`, 'error'); - tryAutoReconnect(); + await disconnectPort(); + await tryAutoReconnect(); } } @@ -344,6 +371,7 @@ // Add command to history, ignore duplicate consecutive if (history.length === 0 || history[history.length - 1] !== text) { history.push(text); + localStorage.setItem('commandHistory', JSON.stringify(history)); } historyIndex = -1; @@ -369,10 +397,15 @@ commandLine.value = ''; } catch (error) { setStatus(`Send error: ${error.message}`, 'error'); - tryAutoReconnect(); + await disconnectPort(); + await tryAutoReconnect(); } }); + newlineModeSelect.addEventListener('change', () => { + localStorage.setItem('newlineMode', newlineModeSelect.value); + }); + // Forget device button clears stored device info forgetDeviceBtn.addEventListener('click', async () => { if (port) { @@ -415,6 +448,13 @@ lastCommand = null; lastCommandCount = 0; lastCommandButton = null; + history.length = 0; + historyIndex = -1; + + // iterate and delete localStorage items + for (const key in localStorage) { + localStorage.removeItem(key); + } }); diff --git a/examples/device/webusb_serial/website/index.html b/examples/device/webusb_serial/website/index.html index 91d6e4fba..8d18ff8cf 100644 --- a/examples/device/webusb_serial/website/index.html +++ b/examples/device/webusb_serial/website/index.html @@ -50,7 +50,7 @@
- +
-- cgit v1.3.1