From aafc8754b611a12141fe8d3060efad47d5e8a024 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Wed, 15 Jul 2026 22:10:27 +0800 Subject: [PATCH] fix(wasm): unlock before listening for topics --- rynk/rynk-wasm/index.html | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/rynk/rynk-wasm/index.html b/rynk/rynk-wasm/index.html index 41524fd6e..2b4cdfce4 100644 --- a/rynk/rynk-wasm/index.html +++ b/rynk/rynk-wasm/index.html @@ -53,10 +53,12 @@

Rynk web demo

const v = await fn(); const isObj = v !== null && typeof v === "object"; logLine(`✓ ${pad} ${isObj ? JSON.stringify(v) : v}`, isObj); + return v; } catch (e) { const kind = (e && e.name) || "Error"; const glyph = kind === "Unsupported" ? "·" : kind === "Rejected" ? "⚠" : "✗"; log(`${glyph} ${pad} ${kind}: ${e && e.message}`); + return null; } } @@ -253,13 +255,20 @@

Rynk web demo

} // One live session across both transports. - let port = null, device = null, l = null, core = null, client = null, connected = false; + let port = null, device = null, l = null, core = null, client = null, connected = false, topicsStarted = false; + + function startTopicPump() { + if (!client || topicsStarted) return; + topicsStarted = true; + log("\n— listening for topics (type on the keyboard); click to disconnect —"); + pumpTopics(client); + } async function teardown() { // Closing the link EOFs the transport, ending any parked next_event() pump. if (l) await l.close(); else if (port) { try { await port.close(); } catch {} } - port = null; device = null; l = null; core = null; client = null; connected = false; + port = null; device = null; l = null; core = null; client = null; connected = false; topicsStarted = false; serialBtn.textContent = "Connect via Serial (USB)"; bleBtn.textContent = "Connect via BLE (WebHID)"; serialBtn.disabled = false; bleBtn.disabled = false; @@ -292,7 +301,7 @@

Rynk web demo

// Exercise the typed client surface. await show("version", () => client.get_version()); - await show("lock status", () => client.get_lock_status()); + const lockStatus = await show("lock status", () => client.get_lock_status()); await show("default layer", () => client.get_default_layer()); await show("current layer", () => client.get_current_layer()); await show("key L0(0,0)", () => client.get_key(0, 0, 0)); @@ -310,12 +319,16 @@

Rynk web demo

// Gated behind the lock: shows "⚠ Rejected: Locked" until unlocked. await show("matrix state", () => client.get_matrix_state()); - unlockBtn.hidden = false; // offer the unlock ceremony while connected - log("\n— listening for topics (type on the keyboard); click to disconnect —"); thisBtn.textContent = "Disconnect"; thisBtn.disabled = false; // re-enable so it can disconnect otherBtn.disabled = true; // one transport at a time - pumpTopics(client); // pull topics until the link closes + if (lockStatus?.locked && lockStatus.key_positions.length > 0) { + unlockBtn.hidden = false; + log("\n— unlock before starting the topic listener —"); + } else { + unlockBtn.hidden = true; + startTopicPump(); + } } catch (e) { log("ERROR: " + e); console.error(e); @@ -345,7 +358,12 @@

Rynk web demo

unlockBtn.disabled = true; try { const start = await client.get_lock_status(); - if (!start.locked) { log("· already unlocked"); return; } + if (!start.locked) { + log("· already unlocked"); + unlockBtn.hidden = true; + startTopicPump(); + return; + } if (start.key_positions.length === 0) { log("⚠ permanently locked — set [host].unlock_keys in keyboard.toml"); return; @@ -354,7 +372,12 @@

Rynk web demo

const deadline = Date.now() + 30000; for (;;) { const s = await client.unlock_poll(); - if (!s.locked) { log("✓ unlocked — retry the gated action"); break; } + if (!s.locked) { + log("✓ unlocked — retry the gated action"); + unlockBtn.hidden = true; + startTopicPump(); + break; + } log(` holding… ${s.remaining_keys} key(s) left`); if (Date.now() > deadline) { log("✗ unlock timed out"); break; } await new Promise((r) => setTimeout(r, 150));