From 092e635fd1c378baebb0a777772a26ed0a518873 Mon Sep 17 00:00:00 2001 From: Arcmyx Official Date: Thu, 26 Mar 2026 17:32:56 +0000 Subject: [PATCH] Test ANSI escape codes --- main.html | 106 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/main.html b/main.html index af120f4..7c8f1b2 100644 --- a/main.html +++ b/main.html @@ -72,7 +72,7 @@ // Commands (PBIO_PYBRICKS_COMMAND_*) const CMD_STOP_USER_PROGRAM = 0; const CMD_START_USER_PROGRAM = 1; - const CMD_WRITE_USER_PROGRAM_META = 3; // not used here + const CMD_WRITE_USER_PROGRAM_META = 3; const CMD_WRITE_USER_RAM = 4; // Events (PBIO_PYBRICKS_EVENT_*) @@ -83,6 +83,44 @@ let maxCharSize = 20; let lastStatusFlags = 0; let stdoutBuffer = ''; + + // ── Pynamics ────────────────────────────────────────────────────────────── + // Matches: ESC[?PYN;;;;...~ + const PYNAMICS_REGEX = /\x1b\[\?PYN;([^;~]+);([^~]*?)~/g; + + const PYNAMICS_EVENT_MAP = { + '1': 'click', + '2': 'focus', + '3': 'blur', + '4': 'navigate', + '5': 'state_change', + '6': 'notify', + '7': 'render', + '99': 'custom', + }; + + /** + * Strips all Pynamics escape codes from a string, fires console.log for + * each one, and returns the cleaned string (without the escape sequences). + */ + function processPynamics(text) { + let match; + // Reset lastIndex so repeated calls work correctly + PYNAMICS_REGEX.lastIndex = 0; + + while ((match = PYNAMICS_REGEX.exec(text)) !== null) { + const eventId = match[1]; + const args = match[2].split(';').filter(Boolean); + const name = PYNAMICS_EVENT_MAP[eventId] ?? `unknown(${eventId})`; + + console.log('[pynamics]', { event: name, eventId, args }); + } + + // Remove all escape sequences from the visible text + return text.replace(PYNAMICS_REGEX, ''); + } + // ───────────────────────────────────────────────────────────────────────── + function log(msg) { const el = document.getElementById('log'); el.textContent += msg + '\n'; @@ -104,10 +142,7 @@ const total = arrays.reduce((s, a) => s + a.length, 0); const out = new Uint8Array(total); let off = 0; - for (const a of arrays) { - out.set(a, off); - off += a.length; - } + for (const a of arrays) { out.set(a, off); off += a.length; } return out; } function hexpreview(data, n = 12) { @@ -135,40 +170,36 @@ function decodeStatus(flags) { lastStatusFlags = flags; const bits = { - battLow: !!(flags & (1 << 0)), + battLow: !!(flags & (1 << 0)), battCrit: !!(flags & (1 << 1)), - running: !!(flags & (1 << 6)), + running: !!(flags & (1 << 6)), shutdown: !!(flags & (1 << 7)), - btn: !!(flags & (1 << 5)), + btn: !!(flags & (1 << 5)), hostConn: !!(flags & (1 << 9)), - fileIO: !!(flags & (1 << 13)), + fileIO: !!(flags & (1 << 13)), }; return `running=${bits.running} btn=${bits.btn} hostConn=${bits.hostConn} fileIO=${bits.fileIO} shutdown=${bits.shutdown}`; } function isBusy() { - const running = !!(lastStatusFlags & (1 << 6)); - const fileIO = !!(lastStatusFlags & (1 << 13)); - return running || fileIO; + return !!(lastStatusFlags & (1 << 6)) || !!(lastStatusFlags & (1 << 13)); } function onNotify(event) { const data = new Uint8Array(event.target.value.buffer); - if (data.length === 0) { - log('[event] empty notification'); - return; - } + if (data.length === 0) { log('[event] empty notification'); return; } const type = data[0]; if (type === EVT_WRITE_STDOUT) { - // Buffer and reassemble split packets stdoutBuffer += new TextDecoder().decode(data.slice(1)); const lines = stdoutBuffer.split('\n'); - stdoutBuffer = lines.pop() || ''; // Keep incomplete line + stdoutBuffer = lines.pop() || ''; for (const line of lines) { - if (line.trim()) { - log('[stdout] ' + line); + // Strip Pynamics codes and fire console events before displaying + const clean = processPynamics(line); + if (clean.trim()) { + log('[stdout] ' + clean); } } } else if (type === EVT_STATUS_REPORT) { @@ -198,7 +229,6 @@ setStatus('Connecting GATT...'); server = await device.gatt.connect(); - // Device Information try { const di = await server.getPrimaryService(DI_SERVICE_UUID); const sw = new TextDecoder().decode( @@ -215,7 +245,6 @@ const svc = await server.getPrimaryService(PYBRICKS_SERVICE_UUID); - // Capabilities try { const capChar = await svc.getCharacteristic(PYBRICKS_CAPABILITIES_UUID); const raw = await capChar.readValue(); @@ -239,7 +268,7 @@ await cmdChar.startNotifications(); cmdChar.addEventListener('characteristicvaluechanged', onNotify); - await sleep(500); // wait for first status + await sleep(500); log(`Connected to ${device.name}`); setStatus(`Connected: ${device.name}`); @@ -265,11 +294,8 @@ } async function upload() { - if (!cmdChar) { - log('Not connected to hub.'); - return; - } - // Always stop any running program first + if (!cmdChar) { log('Not connected to hub.'); return; } + log('Stopping any running program...'); try { await writeCmd(new Uint8Array([CMD_STOP_USER_PROGRAM]), 'STOP'); @@ -277,39 +303,27 @@ } catch (e) { log('Stop command ignored (no program running)'); } - /*if (isBusy()) { - log('Hub is busy (program running or file IO), not uploading.'); - return; - }*/ document.getElementById('uploadBtn').disabled = true; try { const resp = await fetch('./main.bin'); - if (!resp.ok) { - throw new Error(`fetch main.bin: ${resp.status} ${resp.statusText}`); - } + if (!resp.ok) throw new Error(`fetch main.bin: ${resp.status} ${resp.statusText}`); const blob = new Uint8Array(await resp.arrayBuffer()); log(`\nLoaded main.bin: ${blob.length} bytes`); log(`Preview : ${hexpreview(blob, 20)}`); const maxProgSize = 261512; - if (blob.length > maxProgSize) { - throw new Error(`Program too large for hub (max ${maxProgSize} bytes)`); - } + if (blob.length > maxProgSize) throw new Error(`Program too large (max ${maxProgSize} bytes)`); setStatus('Uploading...'); - const PAYLOAD = maxCharSize - 5; // 1 cmd + 4 offset + const PAYLOAD = maxCharSize - 5; const chunks = Math.ceil(blob.length / PAYLOAD); log(`\n── Upload ${blob.length} bytes in ${chunks} chunks ──`); for (let offset = 0; offset < blob.length; offset += PAYLOAD) { const chunk = blob.slice(offset, Math.min(offset + PAYLOAD, blob.length)); - const packet = concat( - new Uint8Array([CMD_WRITE_USER_RAM]), - u32le(offset), - chunk - ); + const packet = concat(new Uint8Array([CMD_WRITE_USER_RAM]), u32le(offset), chunk); await writeCmd(packet, `RAM@${offset}`); } log('All chunks sent ✓'); @@ -342,9 +356,7 @@ } async function disconnect() { - if (device && device.gatt.connected) { - device.gatt.disconnect(); - } + if (device && device.gatt.connected) device.gatt.disconnect(); }