···195195 wipe(8, 10, 18);
196196 cursor?.("native");
197197 connectWs();
198198+ // Quick bridge probe — reports whether jweb~ has injected window.max yet
199199+ // by the time boot runs.
200200+ const initialBridge = !!maxBridge();
201201+ console.log(`🎹 boot bridge=${initialBridge}`);
202202+ // And again one tick later in case jweb~ injects window.max just after boot.
203203+ setTimeout(() => {
204204+ console.log(`🎹 boot+50ms bridge=${!!maxBridge()}`);
205205+ }, 50);
206206+ setTimeout(() => {
207207+ console.log(`🎹 boot+500ms bridge=${!!maxBridge()}`);
208208+ }, 500);
198209}
199210200211function sim() {
201212 frame += 1;
202213 if (wsState === "closed" && frame >= reconnectAt) connectWs();
203214}
215215+216216+let eventsSeen = 0;
204217205218function act({ event: e }) {
206219 if (!e?.is) return;
220220+ // Dump first ~15 events of any kind so we can see what actually propagates
221221+ // into the piece from jweb~. `name` is the AC event type string.
222222+ if (eventsSeen < 15) {
223223+ eventsSeen += 1;
224224+ const name = e.name || "?";
225225+ const bridge = !!maxBridge();
226226+ console.log(`🎹 evt#${eventsSeen} ${name} key=${e.key || ""} bridge=${bridge}`);
227227+ }
207228 // Click-to-test-note: tap anywhere on the device UI fires C4 so we can
208229 // verify the Max bridge path without depending on keyboard focus.
209230 if (e.is("touch")) {
···213234 if (e.is("lift")) {
214235 releaseLocalKey("c");
215236 return;
216216- }
217217- // Best-effort debug: log raw keyboard events to Max console.
218218- if (e.is("keyboard:down") || e.is("keyboard:up")) {
219219- console.log(`🎹 kbd ${e.key || e.name || "?"}`);
220237 }
221238 for (const key of Object.keys(KEY_TO_PITCH)) {
222239 if (e.is(`keyboard:down:${key}`)) {