Files
ESP32_Serial_Swiss_Army_Knife/docs/phase8d17_implementation.md
T
Commander1024 9f6ebf2053 Add Serial and Wi-Fi quick settings
Reuse the existing settings view and typed controllers for accessible
quick editing while keeping network credentials out of quick mode.
Expand browser and layout coverage for focus, dismissal, bounds, and
expiry.
2026-09-13 14:22:31 +02:00

13 KiB

Phase 8D.17 — Serial / Wi-Fi quick popovers

Status and scope (2026-09-13)

User-authorized implementation complete; host tests and firmware build verified. Independent review complete; target/user sign-off pending. No M3 or memory-reserve approval implied. Initial git --no-optional-locks status --short was empty.

Only authored src/web_ui.c, UI regressions and phase/agent documentation changed. No backend/API, handler budget, dispatcher, task, stack, queue, schema, dependency, icon, generated asset, SDK or configuration change. No 8D.18 client/writer dialogs or 8D.15 diagnostics. Broker8D.16/Display behavior and the signed-off combined binary WS send at CPU160 are preserved; no upload, erase, commit or branch creation.

Independent-review P2 correction (2026-09-13)

Reviewer identified that closeQuick(false) restored terminal selection through an unconditional xterm focus, stealing keyboard focus during adjacent-trigger switching and timed departure to an outside control. selectTerminal now has an independent focus option (default unchanged for ordinary navigation); quick dismissal restores selection/input state with terminal focus suppressed. Automatic dismissal/switching preserves focus. Escape/Close deliberately focus only the originating trigger, never transiently xterm.

Terminal.focus() in the Node harness now updates activeElement and counts calls. New adjacent Wi-Fi→Serial→Wi-Fi keyboard regression reproduced the failure before the fix. Two added groups cover that switch, timed outside-focus and outside-pointer dismissal for both Serial/Admin backgrounds, and Escape/Close trigger-only focus. Final python3 tests/web_ui_session/run.py PASS 128 groups plus renderer/HTML/CSP; pio run PASS21.90s, 100,300 B RAM / 1,788,661 B flash (+0/+32 B versus initial8D.17 build; +0/+6,048 B versus phase baseline); git --no-pager diff --check PASS. Earlier126-group/build results below are pre-review history. Existing uncommitted phase work preserved. Independent reviewer confirmed the P2 resolved with no new actionable issue and independently reran128 UI groups+renderer/HTML/CSP and diff checks PASS. Parent final build confirmation PASS6.98s,100,300 B RAM/1,788,661 B flash; diff check PASS. Target sign-off remains pending; geometry not rerun, prior sandbox limitation stands.

UI and ownership contract

  • Existing Wi-Fi/Serial status values are native labeled buttons, initially disabled and enabled only after validated admin session adoption. Ordinary users retain status text but cannot open settings through them; server authorization is unchanged.
  • One existing serial-settings DOM host, never cloned or simultaneously mounted. Serial uses the completed framing/lifecycle/persistence form. Wi-Fi uses the completed AP/boot, four STA-profile, connection and mDNS controls, excluding password action/input/status from the quick presentation. No new settings fields or endpoint semantics.
  • The same snapshot validators, SSID UTF-8/hex conversion, edit validation, confirmations, single-flight request controllers, generation/currentness checks, operation IDs and bounded result polling serve full and quick views. Network fetch remains the strict secret-free typed snapshot, never a saved-credentials fetch. Password controls are hidden and disabled in quick mode; transient input is cleared on entry, navigation and promotion, with keep-existing semantics. No saved PSK can be returned/prefilled by these endpoints.
  • Mouse hover or keyboard focus opens without mutation. Click/native Enter/Space/tap enters the same content and focuses Close. Touch pointer-enter does not open preemptively. Repeated activation does not refetch/reset the current quick form. Opening performs session validation and one existing domain GET, not an operation POST or operation-result GET.
  • Nonmodal dialog semantics, accessible name, aria-controls, aria-haspopup=dialog, aria-expanded, visible focus outline, native controls and explicit Close/full-page link. No focus trap. Focused content resists unrelated hover; switching by explicit activation remains available.
  • A single 250ms pointer/focus departure timer bridges trigger-to-content movement. Departure closes only if neither region is hovered and focus is outside both. Escape closes and restores trigger focus; automatic reopening of that dismissed trigger is suppressed until departure, while explicit activation can reopen. Outside pointer-down closes without stealing focus back to the trigger.
  • One fixed top-right panel, 8px viewport inset, at most 600px wide and viewport-minus16px tall (dynamic viewport height with fallback), internally scrollable. No icons were changed. Geometry fixture checks are implemented but did not run successfully in this sandbox (see below).
  • Full-page link promotes the same host/controller and draft, without refetch, POST, result replay or poll cancellation. Password transient state is cleared. Close/switch instead clears drafts and aborts/fences reads and browser automatic checks; admitted backend work is not cancelled. Pending/unknown outcome gates remain retained for explicit Check Result on return; opening never resumes polling or replays a mutation.
  • When a full Settings page already owns a draft, hover/focus on a status trigger does nothing. Explicit click/Enter/tap navigates the existing full page to that domain and focuses its navigation control. This deliberate exception prevents hover from discarding a full-page draft or introducing a second DOM owner.
  • The previous Serial/Admin terminal remains visible behind the quick panel and continues receiving. Logical Settings selection disables both terminal input routes while quick content is open. Close restores the previous terminal selection; no socket, broker-client identity, lease or binary protocol change. Session revalidation also respects this visible-background selection. Expiry/logout/pagehide use existing clear/fence/close semantics; no secret or mutation replay on restore.
  • Apply changes RAM; Save persists device working values, not unsubmitted browser drafts. Existing Serial discard warnings, Network connection-loss confirmations/recovery guidance and uncertain-result handling remain visible. Network profile selection is only editing, never connection selection or mutation. Actual explicit Network operations can disrupt network clients; UART0 administration and native USB UART1 remain independent recovery paths.

Validation executed

All commands from the repository root, bounded at 120 seconds per tool invocation (geometry at 60 seconds, browser subprocess at 30 seconds). All below passed, except the explicitly failed optional geometry attempt:

python3 tests/web_ui_session/run.py
python3 tests/web_cookie_auth/run.py
python3 tests/web_cookie_auth/run.py --settings
python3 tests/web_cookie_auth/run.py --serial-settings
python3 tests/web_cookie_auth/run.py --accounts
python3 tests/web_cookie_auth/run.py --admin
python3 tests/web_cookie_auth/run.py --network
python3 tests/web_cookie_auth/run.py --display
python3 tests/web_cookie_auth/run.py --broker
python3 tests/web_admin_transport/server_lifecycle.py
python3 tests/admin_console_boundary/run.py
python3 tests/admin_console_boundary/accounts.py
python3 tests/admin_console_boundary/lifecycle.py
python3 tests/admin_ssh_policy/run.py
python3 tests/web_network_settings/run.py
python3 tests/session_broker_diagnostics/run.py
python3 tests/web_auth_parse/run.py
python3 tests/web_session_store/run.py
python3 tests/web_session_store/run.py --serial
python3 tests/web_admin_transport/run.py --tickets
python3 tests/web_httpd_idle/run.py
python3 tests/web_diagnostics/run.py
python3 tests/web_serial_performance/run.py
python3 tests/web_security/run.py
python3 tests/web_login_ui/run.py
pio run
git --no-pager diff --check

Final UI: 126 behavior groups (119 existing +7 quick-specific) executing production C-rendered JS, plus C response/header/failure, parsed HTML/labels/unique ownership and exact unchanged inline-loader CSP hash checks. New focus/containment doubles exercise hover/touch activation, stationary-pointer Escape suppression, input gating/binary drains, outside dismissal and delayed snapshot fencing, pagehide, full-page draft/promotion ownership, actual explicit Apply and Save bodies, pending-result non-replay, strict Network schema/secret exclusion and endpoint401. Full-page regression suites exercise the shared controllers' remaining validation/result/identity cases. These are deterministic DOM/fetch/clock/socket doubles, not real browser event dispatch, radio, NVS or target timing proof.

During test development, a new Serial acknowledgement fixture incorrectly included Network's fourth error field; strict production validation correctly rejected it, making the promotion-poll assertion fail. Fixed both new Serial fixtures to the existing exact three-field schema and reran the suite/build successfully. Self-review also added focused-editor hover protection and stationary-pointer Escape suppression; this is not independent review.

Optional real geometry attempted:

WEB_UI_CHROMIUM=/usr/bin/chromium-browser python3 tests/web_ui_session/run.py

Unavailable, not passed: Chromium failed before measurement with read-only ~/.config/chromium/Crash Reports/settings.dat and fatal process-singleton socket() failed: Operation not permitted; ptrace also denied. No sandbox escape or installed dependency change attempted. The optional fixture now covers 18 cases: existing four full settings views at320/600/1200px plus Serial/Wi-Fi quick views at those widths and360px height; checks panel bounds/overflow, scrolling and password/navigation exclusion. It strips the authenticated loader/assets and uses fixture content: even a future pass is not live-app interaction or target visual sign-off.

Build/resources

Measurement RAM Flash Time
Actual pre-edit baseline pio run 100,300 B 1,782,613 B 6.88 s
Final substantive source pio run 100,300 B 1,788,629 B 21.74 s
Delta 0 B +6,016 B incremental build times not comparable

CPU160 independently confirmed in tracked defaults, existing active sdkconfig and generated sdkconfig.h. PlatformIO's board banner says240MHz but does not override those actual settings. No new firmware tasks, buffers or endpoint handlers:33 handlers/six sockets retained. Browser costs are bounded quick state, listeners and one250ms departure timer; settings snapshots/controllers are reused. No runtime heap, HTTPD/dispatcher stack, responsiveness, transient reserve or loaded-client measurement was performed. Existing full-mix throughput acceptance is not reopened or expanded.

Pending target / parent checklist

  • Independent code/test review and P2 fix confirmation; no remaining actionable findings.
  • Desktop mouse: enter trigger, cross to scrollable content, move between fields, leave both; no flicker/refetch/draft loss. Escape while pointer remains stationary stays dismissed; Close and explicit reopen work.
  • Keyboard: Tab-focus disclosure, Enter/Space activation, all fields/actions/full-page link reachable, Shift-Tab departure, Escape focus return and no serial/admin key leakage. Screen reader names/expanded/nonmodal semantics verified.
  • Touch: tap both triggers, edit/select without unintended submission, scroll panel, outside-tap/Close, rotate/resize/zoom/software keyboard at narrow/short viewports; no inaccessible controls or horizontal overflow. Run optional geometry where Chromium can start.
  • Full-page draft (including transient Network password) untouched by mere trigger hover/focus; explicit navigation clears old context. Quick-to-full preserves only intended nonsecret draft/controller state.
  • Compare quick/full/CLI Serial settings and explicit Apply/Save; no Save of browser-only drafts. Reboot persistence and serial reconfiguration discard semantics verified.
  • AP/four STA profiles/SSID hex and UTF-8, omitted PSK preservation, explicit connection confirmations, accepted-versus-online distinction, mDNS and explicit persistence. No secret shown or cached; recover after actual network loss via STA/AP, UART0/native USB.
  • Delayed/lost acknowledgement, stale snapshot, concurrent CLI/tab edits, dismissed pending operation, Check Result/Refresh recovery; no opening/selection/restore replay. Session expiry, role change, logout, bfcache and late response isolation.
  • Both browser terminals plus physical USB/SSH/full mix at signed-off160MHz/230400 baud: sockets/lease survive UI actions and bounded output continues; capture drop/latency/heap/HTTPD-dispatcher margins without claiming new reserve approval.

No target checklist item was executed or signed off by this implementation.