feat(qbzd): ratatui setup TUI, six screens with explicit-save semantics

Implements owner requirement 2 (03-setup-tui.md): `qbzd setup`, a six-screen
raspi-config-grade configurator over the daemon's real stores — zero new
persistence. ratatui 0.30 (IV5: unifies on the lock's unicode-width 0.2.2;
0.29 forbidden). A synchronous event loop on a blocking thread; disk/HTTP only
at screen entry, `r`, save and the immediate actions, on workers with a spinner
(no I/O on keystrokes). Non-tty exits 2 with the verbatim copy; alt-screen +
panic-hook restore.

Screens: Account (T5 login engine; inline masked token paste, suspend-and-run
browser login, logout confirm), Audio (the §3.2.3 constraint matrix + 7
cascades verified 1:1 vs the desktop code, device picker re-deriving the
§3.2.2 grouping/BP-badge rules, DSD guard), Playback (ask->fallback rule,
infinite-radio read-only), QConnect (startup/name/volume_mode, live preview),
Network (whole-file qbzd.toml rewrite preserving every key), Import/Export
(pure renderer of the T12 bundle plan; interactive device re-pick; default-off
auth gate). Saves reuse T11 write_one + reload/status classification (§4.3);
dirty marker + Save/Discard/Stay. All user-facing strings centralized.

cc
This commit is contained in:
vicrodh committed 2026-07-14 20:42:42 -06:00
1 parent 8021393925
commit a1fbbafc79
15 files changed
+5554 -145

No files matched your search

+5 -1
View File
@@ -44,7 +44,11 @@ urlencoding = "2" # OAuth redirect_url encode + callback decode (in l
open = "5" # system-browser launch for `qbzd login` (in lock via qbz)
clap = { version = "4", features = ["derive"] }
clap_complete = "4"
# ratatui arrives in T13 — do NOT add it here
# T13 setup TUI. ratatui 0.30 (NEVER 0.29 — 0.29 pins unicode-width "=0.2.0",
# unresolvable against Slint's annotate-snippets 0.12.16 which needs >=0.2.2;
# ratatui-core 0.30 accepts >=0.2.0,<=0.2.2 → unifies on the lock's 0.2.2, IV5).
# The `crossterm` backend is a DEFAULT feature; re-exported as ratatui::crossterm.
ratatui = "0.30"
[dev-dependencies]
tempfile = "3"