From c43a088ce8b2c854af25f808a5866f3cabd74fb4 Mon Sep 17 00:00:00 2001 From: ducoterra Date: Mon, 9 Nov 2026 11:00:27 -0500 Subject: [PATCH] phase: 04_controller_page_responsive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Final verification pass** (all three task reports PASS; tasks already in `complete/`). One real defect found and fixed. **Commands / results.** `bash tests/run_tests.sh` → `RESULT: all suites passed`, **5 104** checks (page **220**). `SANITIZE=1 bash tests/run_tests.sh` → passed. `bash .agents/validate.sh` → `validation OK`; bridge/console/rollback `SUCCESS`; `page bytes: 59262`; `README/AGENTS claims: OK`; `repo hygiene: OK`. Real-browser leg (headless Chromium, served assembled bytes): no horizontal scroll at 360/390/768/1440, ARM first-in-`main` and sticky, log height-capped, all 5 folds openable, all 3 changed page statements execute. **Fix.** The folded notes' `` — the only route to five pinned safety sentences — rendered **17 px** on coarse pointers, under the page's own 44 px thumb floor, while every byte-level assertion stayed green. `src/web_page.h`: `button.k,.pill,input,summary{min-height:44px}` + `summary{padding:13px 0}`; `tests/test_page.js` +1 check (219→220); mutation-tested (reverting the CSS fails host *and* browser legs, then restored byte-for-byte). **Criteria.** 1 ✓ browser-measured (physical phone still unobserved). 2 ✓ 220 checks + prose untouched. 3 ✓ page 59 286→**59 262 B** (−24 vs phase start), README = gate figure. 4 ✓ one `test_page` row at 220, intro 5 104 = measured. 5 ✓ gate green; phases 01–03 assertions unchanged apart from the re-pinned byte constant. **Docs.** Re-pinned 59 262 (README, `config.h`, `test_conn.cpp`), totals 5 103→5 104, README Tests row 220; re-measured footprint rows (flash 1 257 901 / 1 178 141 / 1 157 593, TLS +6 176/+100 308); fixed stale "48 KB"/"~50 KB" (×4 src comments, ×2 README) and the 51 626-byte page-delivery row; added **bug 14** with root cause and renamed *Thirteen*→*Fourteen bugs* in README + AGENTS.md's two stale references; added the Phase 04 browser+device row to `.agents/HW_VERIFICATION.md`. **Deviations.** Device leg stays open: live board serves 51 626 B (pre-phase image); no reflash attempted, per the standing no-unattended-reflash rule — no byte-equality claim made. `ruff check tools tests`: 29 findings, identical at HEAD and `--isolated`, `E9,F` clean, no repo lint config or gate step → left untouched. `.agents/validate.sh` is **untracked and has never been committed** (`git log --all --` empty) though README/AGENTS/PLAN cite it 13× — harness needs `git add` it; I did not stage anything. --- .agents/HW_VERIFICATION.md | 5 +- .../04_controller_page_responsive/00_phase.md | 0 .../01_responsive_layout.md | 0 .../02_prose_trim_pinned.md | 0 .../03_suite_size_and_docs.md | 0 ...ontroller_page_responsive__00_phase.a1.err | 0 ...controller_page_responsive__00_phase.a1.md | 11 + ...ler_page_responsive__00_phase.a1.md.status | 1 + ...ller_page_responsive__00_phase.a1.validate | 223 ++++++++++ ...ge_responsive__01_responsive_layout.a1.err | 0 ...age_responsive__01_responsive_layout.a1.md | 15 + ...ponsive__01_responsive_layout.a1.md.status | 1 + ...sponsive__01_responsive_layout.a1.validate | 215 ++++++++++ ...ge_responsive__02_prose_trim_pinned.a1.err | 0 ...age_responsive__02_prose_trim_pinned.a1.md | 9 + ...ponsive__02_prose_trim_pinned.a1.md.status | 1 + ...sponsive__02_prose_trim_pinned.a1.validate | 219 ++++++++++ ..._responsive__03_suite_size_and_docs.a1.err | 0 ...e_responsive__03_suite_size_and_docs.a1.md | 8 + ...nsive__03_suite_size_and_docs.a1.md.status | 1 + ...onsive__03_suite_size_and_docs.a1.validate | 223 ++++++++++ AGENTS.md | 6 +- README.md | 115 +++++- src/config.h | 6 +- src/conn.h | 2 +- src/control_server.cpp | 2 +- src/control_server.h | 2 +- src/web_page.h | 384 ++++++++++-------- tests/test_conn.cpp | 8 +- tests/test_page.js | 357 +++++++++++++++- 30 files changed, 1600 insertions(+), 214 deletions(-) rename .agents/phases/{todo => complete}/04_controller_page_responsive/00_phase.md (100%) rename .agents/phases/{todo => complete}/04_controller_page_responsive/01_responsive_layout.md (100%) rename .agents/phases/{todo => complete}/04_controller_page_responsive/02_prose_trim_pinned.md (100%) rename .agents/phases/{todo => complete}/04_controller_page_responsive/03_suite_size_and_docs.md (100%) create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.err create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md.status create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.validate create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.err create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.md create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.md.status create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.validate create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__02_prose_trim_pinned.a1.err create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__02_prose_trim_pinned.a1.md create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__02_prose_trim_pinned.a1.md.status create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__02_prose_trim_pinned.a1.validate create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__03_suite_size_and_docs.a1.err create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__03_suite_size_and_docs.a1.md create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__03_suite_size_and_docs.a1.md.status create mode 100644 .agents/reports/04_controller_page_responsive/04_controller_page_responsive__03_suite_size_and_docs.a1.validate diff --git a/.agents/HW_VERIFICATION.md b/.agents/HW_VERIFICATION.md index 2be9713..66009c2 100644 --- a/.agents/HW_VERIFICATION.md +++ b/.agents/HW_VERIFICATION.md @@ -94,8 +94,9 @@ identity. The instrumentation stays worth having — it is what makes the next c | claim | command | expected observation | outcome | |---|---|---|---| -| page still byte-exact after the restructure | the generic `curl … \| wc -c` row above | equals the gate's new `page bytes:` count (the count moves when the page does — that is fine; equality with the gate is the check) | | -| one column on a phone, ARM thumb-reachable | open `https://keyrelay.local/` on a phone at ~360 px | single column, no horizontal scroll, sticky ARM bar on screen, log collapsed and not pushing ARM off screen | | +| page still byte-exact after the restructure | the generic `curl … \| wc -c` row above | equals the gate's new `page bytes:` count (the count moves when the page does — that is fine; equality with the gate is the check) | **skipped (board not reflashed), 2026-11-09 (task 01)** — one capped attempt was run *before* the page edit: `timeout 15 curl -sk https://keyrelay.local/ \| wc -c` → **51 626** and `sha256 dd16e195…f04e9`, byte-identical to the phase-02 close record, so the live board still runs pre-phase-03 firmware and the measurement path works. The 60 780-byte page of this tree is only served after `run -e bridge -t upload`, deliberately not run here: the board is a live HID keyboard into the machine running this pipeline and `to_bootloader.py` strands it if anything goes wrong unattended (same call phase 02 recorded). To close: reflash, then `timeout 15 curl -sk https://keyrelay.local/ \| wc -c` must equal the gate's `page bytes:` line for this tree. **Task 02** (the prose fold) re-ran that one capped read-only probe after the edit: **51 626** bytes, `sha256 dd16e1954a68…` — identical to the task-01 and phase-02 records, so the live board still runs the pre-phase-03 image and this tree's **59 223**-byte page (the gate's `page bytes:` line, equal in both builds) is not on it yet. Measurement path proven, byte comparison still open until a flash. **Task 03** (docs and suite) re-ran the same single capped probe: `timeout 15 curl -sk https://keyrelay.local/ | wc -c` → **51 626**, `sha256 dd16e1954a68…f04e9` — byte-identical to the task-02 record, and this tree's locally assembled page hashes `dc26b441…b634` at **59 223** B (the count the gate prints and the count README's Controller-page section now quotes with this exact `curl` command named as the device-side measurement). README == gate == assembled bytes; device equality still opens by the standing no-unattended-reflash call (live HID keyboard into the pipeline host) | **Final pass, 2026-11-09:** one more capped read-only probe — `timeout 15 curl -sk https://keyrelay.local/ \| wc -c` → **51 626**, `sha256 dd16e195…f04e9`, byte-identical to the task-01/02/03 records (the live board has not been reflashed all phase), while this tree assembles to **59 262 B** / `b9414d2f9847b774…` after the `summary` thumb-floor fix; README (×2), `config.h` (×2) and `test_conn.cpp`'s `kServedPageBytes` are re-pinned to 59 262 and the gate's `page bytes:` line agrees. | +| one column on a phone, ARM thumb-reachable | open `https://keyrelay.local/` on a phone at ~360 px | single column, no horizontal scroll, sticky ARM bar on screen, log collapsed and not pushing ARM off screen | **skipped (no device), 2026-11-09 (task 01); still skipped for task 02, 2026-11-09** — no phone is reachable headlessly, and the page must be reflashed first (row above). Task 02 adds one thing for whoever runs it: the folded notes (`
` under Special keys, Connection, Mouse, Send text and the footer) must be readable and tappable at 360 px, and the State card's counters must wrap into a couple of lines rather than stacking eight rows — the counters stay on screen because they are the "is it typing" evidence. Host-side the layout is pinned instead: 17 new `test_page.js` checks assert the served bytes carry `viewport-fit=cover` with pinch zoom intact, the one 720 px breakpoint (one column; `main` as flex column, which is what makes the sticky ARM bar actually stick), the 16 px input floor (focus-zoom), the log height cap + `overscroll-behavior:contain`, the 44 px `pointer:coarse` floors, safe-area bottom padding, and ARM still first in `main`. To close: reflash `bridge`, open `https://keyrelay.local/` on a ~360 px phone — one column, no horizontal scroll at 360/768/1440 px, ARM bar visible while scrolling, focusing the passphrase field does not zoom the page. **Task 03** (2026-11-09): still `skipped (no device)` — no phone is reachable headlessly and the page must be reflashed first. The phone checklist for whoever runs it, from the phase's completion criteria: 360 px width, one column, no horizontal scroll, ARM bar reachable with a thumb at the top of the scroll, focusing the passphrase input does not zoom, and each folded note (Special keys, Connection, Mouse, Send text, footer shortcuts) expands with a tappable summary. Host-side task 03 adds the structural teeth for the last one: `test_page.js` pins exactly five `
`, each with a summary naming what is behind the tap and real sentences inside, and proves the fold checks fail on deletion (mutation-run, then restored byte-for-byte) | +| the responsive layout, observed in a real browser at phone widths (final pass) | assemble the served bytes from `src/web_page.h` (raw-string chunks + each marker branch in turn), serve them on `127.0.0.1`, and measure them in headless Chromium at 360×640, 390×844, 768×1024 and 1440×900 with touch/`pointer:coarse` emulated | no horizontal scroll at any width; ARM reachable without scrolling and sticky at the bottom of a 400-line log; one column and a height-capped, self-scrolling log below the breakpoint; every press target ≥ 44 px on a coarse pointer; the five folds open on a tap and stay inside the viewport; no script error; the safety sentences present in *rendered text* — and the same measurement against the pre-phase-04 page, where the sticky/flex-column/log-cap/thumb-floor rows must fail | **ok (browser, not a phone), 2026-11-09 (phase-04 final pass)** — all five viewports pass on this tree's page (59 262 B, both marker branches equal); the same script on the phase-start page (59 286 B) fails exactly the four rows the phase changed: ARM not sticky (`z-index:auto`, scrolled to −1989 px), `main` still `display:grid`, no log cap, and 19 targets at 34 px. **It also found a defect the byte-level suite could not see, now fixed:** the five `` elements task 02 introduced rendered **17 px** tall on a coarse pointer — the only route to five pinned safety sentences, on a control below the page's own 44 px thumb floor, with every served-byte assertion green. `summary` is now in the `pointer:coarse` rule (page 59 223 → 59 262 B, re-pinned in `config.h`/`test_conn.cpp`/README); the floor is asserted in `test_page.js` (219 → 220) **and** measured here, and deleting the rule fails both (demonstrated, then restored byte-for-byte). This is not the phone row below: no hand-held device was involved, `env(safe-area-inset-*)` is 0 on a desktop screen (so the inset is checked in the declared CSS, where it means something), and iOS Safari's focus-zoom stays unobserved. | ### Phase 05 — soft keyboard and touch pad diff --git a/.agents/phases/todo/04_controller_page_responsive/00_phase.md b/.agents/phases/complete/04_controller_page_responsive/00_phase.md similarity index 100% rename from .agents/phases/todo/04_controller_page_responsive/00_phase.md rename to .agents/phases/complete/04_controller_page_responsive/00_phase.md diff --git a/.agents/phases/todo/04_controller_page_responsive/01_responsive_layout.md b/.agents/phases/complete/04_controller_page_responsive/01_responsive_layout.md similarity index 100% rename from .agents/phases/todo/04_controller_page_responsive/01_responsive_layout.md rename to .agents/phases/complete/04_controller_page_responsive/01_responsive_layout.md diff --git a/.agents/phases/todo/04_controller_page_responsive/02_prose_trim_pinned.md b/.agents/phases/complete/04_controller_page_responsive/02_prose_trim_pinned.md similarity index 100% rename from .agents/phases/todo/04_controller_page_responsive/02_prose_trim_pinned.md rename to .agents/phases/complete/04_controller_page_responsive/02_prose_trim_pinned.md diff --git a/.agents/phases/todo/04_controller_page_responsive/03_suite_size_and_docs.md b/.agents/phases/complete/04_controller_page_responsive/03_suite_size_and_docs.md similarity index 100% rename from .agents/phases/todo/04_controller_page_responsive/03_suite_size_and_docs.md rename to .agents/phases/complete/04_controller_page_responsive/03_suite_size_and_docs.md diff --git a/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.err b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md new file mode 100644 index 0000000..49023ad --- /dev/null +++ b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md @@ -0,0 +1,11 @@ +**Final verification pass** (all three task reports PASS; tasks already in `complete/`). One real defect found and fixed. + +**Commands / results.** `bash tests/run_tests.sh` → `RESULT: all suites passed`, **5 104** checks (page **220**). `SANITIZE=1 bash tests/run_tests.sh` → passed. `bash .agents/validate.sh` → `validation OK`; bridge/console/rollback `SUCCESS`; `page bytes: 59262`; `README/AGENTS claims: OK`; `repo hygiene: OK`. Real-browser leg (headless Chromium, served assembled bytes): no horizontal scroll at 360/390/768/1440, ARM first-in-`main` and sticky, log height-capped, all 5 folds openable, all 3 changed page statements execute. + +**Fix.** The folded notes' `` — the only route to five pinned safety sentences — rendered **17 px** on coarse pointers, under the page's own 44 px thumb floor, while every byte-level assertion stayed green. `src/web_page.h`: `button.k,.pill,input,summary{min-height:44px}` + `summary{padding:13px 0}`; `tests/test_page.js` +1 check (219→220); mutation-tested (reverting the CSS fails host *and* browser legs, then restored byte-for-byte). + +**Criteria.** 1 ✓ browser-measured (physical phone still unobserved). 2 ✓ 220 checks + prose untouched. 3 ✓ page 59 286→**59 262 B** (−24 vs phase start), README = gate figure. 4 ✓ one `test_page` row at 220, intro 5 104 = measured. 5 ✓ gate green; phases 01–03 assertions unchanged apart from the re-pinned byte constant. + +**Docs.** Re-pinned 59 262 (README, `config.h`, `test_conn.cpp`), totals 5 103→5 104, README Tests row 220; re-measured footprint rows (flash 1 257 901 / 1 178 141 / 1 157 593, TLS +6 176/+100 308); fixed stale "48 KB"/"~50 KB" (×4 src comments, ×2 README) and the 51 626-byte page-delivery row; added **bug 14** with root cause and renamed *Thirteen*→*Fourteen bugs* in README + AGENTS.md's two stale references; added the Phase 04 browser+device row to `.agents/HW_VERIFICATION.md`. + +**Deviations.** Device leg stays open: live board serves 51 626 B (pre-phase image); no reflash attempted, per the standing no-unattended-reflash rule — no byte-equality claim made. `ruff check tools tests`: 29 findings, identical at HEAD and `--isolated`, `E9,F` clean, no repo lint config or gate step → left untouched. `.agents/validate.sh` is **untracked and has never been committed** (`git log --all --` empty) though README/AGENTS/PLAN cite it 13× — harness needs `git add` it; I did not stage anything. diff --git a/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md.status b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.validate b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.validate new file mode 100644 index 0000000..d59532f --- /dev/null +++ b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__00_phase.a1.validate @@ -0,0 +1,223 @@ +== host suites: bash tests/run_tests.sh == +compiler: g++ inside localhost/ai-sandbox:ubuntu-9a08b1d500b0 (repo mounted at /work) +keystate 40 checks, 0 failures +proto 2379 checks, 0 failures +text 459 checks, 0 failures +mouse 48 checks, 0 failures +output 153 checks, 0 failures +ws_codec 135 checks, 0 failures +test_tls_pem 862 checks, 0 failures +test_tls_session 118 checks, 0 failures +test_conn 280 checks, 0 failures +test_claim 102 checks, 0 failures +handshake +frame encoding +frame decoding +key table +pasted text +mouse +protocol strings +the fail-safe wiring +the bridge handover (phase 03) +one routing path, two transports +the client side of pinning +the busy board and --take-over +the target-side verifier + t= 0.00s >>> Ctrl+Alt+Del <<< +relayctl 308 checks, 0 failures +the log stays readable +characters +key names in the log +sending text +refusing what it cannot type +stopping +the mouse +the connect path +the channel pill and the certificate line +the busy board and the take-over +arming and the S= state frames +responsive layout +the safety statements that must never be tidied away +the folded notes are folded, not deleted +the page's constants are the board's constants +element ids +page 220 checks, 0 failures +RESULT: all suites passed + +== host suites: SANITIZE=1 bash tests/run_tests.sh == +compiler: g++ inside localhost/ai-sandbox:ubuntu-9a08b1d500b0 (repo mounted at /work) +sanitizers: on +keystate 40 checks, 0 failures +proto 2379 checks, 0 failures +text 459 checks, 0 failures +mouse 48 checks, 0 failures +output 153 checks, 0 failures +ws_codec 135 checks, 0 failures +test_tls_pem 862 checks, 0 failures +test_tls_session 118 checks, 0 failures +test_conn 280 checks, 0 failures +test_claim 102 checks, 0 failures +handshake +frame encoding +frame decoding +key table +pasted text +mouse +protocol strings +the fail-safe wiring +the bridge handover (phase 03) +one routing path, two transports +the client side of pinning +the busy board and --take-over +the target-side verifier + t= 0.00s >>> Ctrl+Alt+Del <<< +relayctl 308 checks, 0 failures +the log stays readable +characters +key names in the log +sending text +refusing what it cannot type +stopping +the mouse +the connect path +the channel pill and the certificate line +the busy board and the take-over +arming and the S= state frames +responsive layout +the safety statements that must never be tidied away +the folded notes are folded, not deleted +the page's constants are the board's constants +element ids +page 220 checks, 0 failures +RESULT: all suites passed + +== firmware: platformio run -e bridge -e console -e rollback == +Processing bridge (platform: espressif32@8.0.0; board: esp32-s3-devkitc-1; framework: arduino) +-------------------------------------------------------------------------------- +Verbose mode can be enabled via `-v, --verbose` option +CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32-s3-devkitc-1.html +PLATFORM: Espressif 32 (8.0.0) > Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM) +HARDWARE: ESP32S3 240MHz, 320KB RAM, 8MB Flash +DEBUG: Current (esp-builtin) On-board (esp-builtin) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa) +PACKAGES: + - framework-arduinoespressif32 @ 4.30312.261024 (3.3.12) + - framework-arduinoespressif32-esp32s3-libs @ 3.3.12 + - tool-esptool @ 2.50400.0 (5.4.0) + - tool-riscv32-esp-elf-gdb @ 12.1.0+20221002 + - tool-xtensa-esp-elf-gdb @ 12.1.0+20221002 + - toolchain-riscv32-esp @ 14.2.0+20260121 + - toolchain-xtensa-esp-elf @ 14.2.0+20260121 +LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf +LDF Modes: Finder ~ chain, Compatibility ~ soft +Found 45 compatible libraries +Scanning dependencies... +Dependency Graph +|-- Adafruit NeoPixel @ 1.15.5 +|-- ESPmDNS @ 3.3.12 +|-- USB @ 3.3.12 +|-- Preferences @ 3.3.12 +|-- WiFi @ 3.3.12 +Building in release mode +Generating partitions .pio/build/bridge/partitions.bin +Retrieving maximum program size .pio/build/bridge/firmware.elf +Checking size .pio/build/bridge/firmware.elf +Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" +RAM: [=== ] 30.4% (used 99744 bytes from 327680 bytes) +Flash: [==== ] 40.0% (used 1257901 bytes from 3145728 bytes) +========================= [SUCCESS] Took 0.79 seconds ========================= + +Processing console (platform: espressif32@8.0.0; board: esp32-s3-devkitc-1; framework: arduino) +-------------------------------------------------------------------------------- +Verbose mode can be enabled via `-v, --verbose` option +CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32-s3-devkitc-1.html +PLATFORM: Espressif 32 (8.0.0) > Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM) +HARDWARE: ESP32S3 240MHz, 320KB RAM, 8MB Flash +DEBUG: Current (esp-builtin) On-board (esp-builtin) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa) +PACKAGES: + - framework-arduinoespressif32 @ 4.30312.261024 (3.3.12) + - framework-arduinoespressif32-esp32s3-libs @ 3.3.12 + - tool-esptool @ 2.50400.0 (5.4.0) + - tool-riscv32-esp-elf-gdb @ 12.1.0+20221002 + - tool-xtensa-esp-elf-gdb @ 12.1.0+20221002 + - toolchain-riscv32-esp @ 14.2.0+20260121 + - toolchain-xtensa-esp-elf @ 14.2.0+20260121 +LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf +LDF Modes: Finder ~ chain, Compatibility ~ soft +Found 45 compatible libraries +Scanning dependencies... +Dependency Graph +|-- Adafruit NeoPixel @ 1.15.5 +|-- ESPmDNS @ 3.3.12 +|-- USB @ 3.3.12 +|-- Preferences @ 3.3.12 +|-- WiFi @ 3.3.12 +Building in release mode +Generating partitions .pio/build/console/partitions.bin +Retrieving maximum program size .pio/build/console/firmware.elf +Checking size .pio/build/console/firmware.elf +Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" +RAM: [== ] 20.6% (used 67352 bytes from 327680 bytes) +Flash: [==== ] 37.5% (used 1178141 bytes from 3145728 bytes) +========================= [SUCCESS] Took 0.78 seconds ========================= + +Processing rollback (platform: espressif32@8.0.0; board: esp32-s3-devkitc-1; framework: arduino) +-------------------------------------------------------------------------------- +Verbose mode can be enabled via `-v, --verbose` option +CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32-s3-devkitc-1.html +PLATFORM: Espressif 32 (8.0.0) > Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM) +HARDWARE: ESP32S3 240MHz, 320KB RAM, 8MB Flash +DEBUG: Current (esp-builtin) On-board (esp-builtin) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa) +PACKAGES: + - framework-arduinoespressif32 @ 4.30312.261024 (3.3.12) + - framework-arduinoespressif32-esp32s3-libs @ 3.3.12 + - tool-esptool @ 2.50400.0 (5.4.0) + - tool-riscv32-esp-elf-gdb @ 12.1.0+20221002 + - tool-xtensa-esp-elf-gdb @ 12.1.0+20221002 + - toolchain-riscv32-esp @ 14.2.0+20260121 + - toolchain-xtensa-esp-elf @ 14.2.0+20260121 +LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf +LDF Modes: Finder ~ chain, Compatibility ~ soft +Found 45 compatible libraries +Scanning dependencies... +Dependency Graph +|-- Adafruit NeoPixel @ 1.15.5 +|-- ESPmDNS @ 3.3.12 +|-- USB @ 3.3.12 +|-- Preferences @ 3.3.12 +|-- WiFi @ 3.3.12 +Building in release mode +Generating partitions .pio/build/rollback/partitions.bin +Retrieving maximum program size .pio/build/rollback/firmware.elf +Checking size .pio/build/rollback/firmware.elf +Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" +RAM: [=== ] 28.6% (used 93568 bytes from 327680 bytes) +Flash: [==== ] 36.8% (used 1157593 bytes from 3145728 bytes) +========================= [SUCCESS] Took 0.79 seconds ========================= + +Environment Status Duration +------------- -------- ------------ +bridge SUCCESS 00:00:00.791 +console SUCCESS 00:00:00.781 +rollback SUCCESS 00:00:00.790 +========================= 3 succeeded in 00:00:02.361 ========================= + +== page size: src/web_page.h raw strings == +page bytes: 59262 (a reflashed device must serve exactly this many; kIndexHtmlLen is sizeof(kIndexHtml)-1 of these same bytes, both builds) + +== README/AGENTS claims vs what this run measured == +README/AGENTS claims: OK (5 104 checks quoted and measured in both runs; 12 Tests-table rows match their suites; 59 262 page bytes quoted; Backlog contiguous; every ws:// sentence names the rollback) + +== repo hygiene == +repo hygiene: OK (131 tracked files, no ignored-tracked files, no key-material shapes, no PII hits) + +== hardware context (informational; cannot change the gate result) == +hw-probe serial /dev/ttyACM0 present, readable (NOT opened: opening toggles DTR/RTS and resets the board) +hw-probe mdns keyrelay.local resolves -> 10.2.0.22 +hw-probe https https://keyrelay.local/ -> HTTP 200 (TLS listener is up) +hw-probe http http://keyrelay.local/ -> HTTP 301, redirects to https://keyrelay.local/ (redirect-only: the post-phase-02 shape) +hw-probe tls 10.2.0.22:443 negotiated TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384 (a self-signed verify failure is the expected shape; the fingerprint to compare it against is on the serial console) +hw-probe uv /home/ducoterra/.local/bin/uv — firmware builds available +hw-probe node /usr/bin/node — test_page.js runs +hw-probe evdev board 046d:c33f visible: keyboard=/dev/input/event19 (readable) mouse=/dev/input/event20 (readable) — python3 tools/watch_target.py runs ON THIS machine (the target) +validation OK (suites + sanitised suites + bridge/console/rollback builds + page size + + the numbers README/AGENTS quote + hygiene) diff --git a/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.err b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.md b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.md new file mode 100644 index 0000000..52ff6b8 --- /dev/null +++ b/.agents/reports/04_controller_page_responsive/04_controller_page_responsive__01_responsive_layout.a1.md @@ -0,0 +1,15 @@ +**Task 01 — responsive layout: done.** + +**Implemented** +- `src/web_page.h` `
-

keyrelay

+

keyrelay

network target @@ -173,11 +229,11 @@ body.off{opacity:.92}

Special keys

-
-
+
@@ -187,41 +243,40 @@ body.off{opacity:.92}
-
- - - +
+ + +
-

- These are here because the laptop's window manager grabs them before the browser - ever sees them — press Super+Left on the laptop and the laptop tiles its own - window. So the bridge types the chord instead and the machine it is plugged into - is the one that reacts. Which window does what is that machine's own keybinding - (these three are KDE Plasma's: window left, window right, fullscreen). They need - the bridge armed, and each one is a single deliberate click. +

The laptop's window manager grabs these chords before the browser + sees them, so the bridge types them instead — armed, one deliberate click each.

+
why these are buttons

Press Super+Left on + the laptop and the laptop tiles its own window — the browser never sees that + chord. What a chord does on the target is that machine's own keybinding; these three + are KDE Plasma's: window left, window right, fullscreen.

State

mode
—
-
address
—
-
uptime
—
-
keys sent
0
-
text sent
0
+
ip
—
+
up
—
+
keys
0
+
text
0
chords
0
-
mouse reports
0
+
mouse
0
dropped
0
-

Held now

-
nothing
+

Held now

+
nothing

Connection

@@ -252,35 +306,34 @@ body.off{opacity:.92}

Mouse

- + not captured
click here to hand this tab the mouse
-
- buttons held -
none
+
+ buttons held +
none
0 reports
-

- Movement is relative, like a real mouse: the board says how far the pointer - moved, never where it is, so the target's own pointer speed and acceleration decide - where that lands. Buttons are restated on every frame, which is why a frame lost in - the WiFi cannot leave one held down. Esc frees the cursor and lets go of - every button — the browser keeps that key for itself, so it never reaches the +

Movement is relative, like a real mouse. Esc frees + the cursor and lets go of every button.

+
how a forwarded mouse behaves

The board says + how far the pointer moved, never where it is: the target's own pointer speed decides where + it lands. Buttons are restated on every frame, which is why a frame lost in the WiFi + cannot leave one held down. The browser keeps Esc, so it never reaches the target, and losing the tab does the same. No auto-click, no macro, no saved gesture, - nothing scheduled: if nobody is moving a mouse, nothing moves. Capture needs the - bridge armed and this board's mouse interface on (mouse on over serial). -

+ nothing scheduled. Capture needs the bridge armed and mouse on over + serial.

Send text

-
+
idle
-

- The board types these characters as keystrokes, at a pace it keeps itself, so the - target sees a keyboard and not a paste. It needs the bridge armed, and one press of - this button — there is no history, nothing is saved, and nothing is ever typed - later. stop typing or Ctrl+Alt+Esc abandons the rest of a run. - Layout matters: characters are positions, so the target must be set to a US layout - for these to land as they read here. -

+

The board types these as keystrokes, while armed. + stop typing or Ctrl+Alt+Esc abandons the rest of a run. Characters are + positions, so the target must be set to a US layout.

+
what a run is and is not

The pace is the + board's own, so the target sees a keyboard, not a paste, and that button is the only + trigger: no history, nothing is saved, nothing typed later. Disarming, losing this tab + or a board error ends a run too.

@@ -311,11 +363,11 @@ body.off{opacity:.92}
- Shortcuts while armed: Ctrl+Alt+Esc release every key and every mouse button - and disarm · Ctrl+Alt+d the Ctrl+Alt+Del confirm · while the - pointer is captured Esc belongs to the browser and frees the cursor · - every other key is forwarded to the target untouched. Nothing is forwarded while disarmed; reload, focus loss and socket loss all disarm. +
shortcuts while armed

+ Ctrl+Alt+Esc releases every key and every mouse button and disarms · + Ctrl+Alt+d is the Ctrl+Alt+Del confirm · while captured, Esc + belongs to the browser · every other key goes to the target untouched.