aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/design/2026-07-03-instrument-console-panels-spec.org139
-rw-r--r--todo.org73
2 files changed, 204 insertions, 8 deletions
diff --git a/docs/design/2026-07-03-instrument-console-panels-spec.org b/docs/design/2026-07-03-instrument-console-panels-spec.org
new file mode 100644
index 0000000..3c4be4c
--- /dev/null
+++ b/docs/design/2026-07-03-instrument-console-panels-spec.org
@@ -0,0 +1,139 @@
+#+TITLE: Instrument-console rebuild — net + bluetooth panels
+#+DATE: 2026-07-03
+#+TODO: TODO | DONE
+#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
+
+* DOING Status
+:PROPERTIES:
+:ID: e73877f5-4f5b-4f81-b946-dbaa6145e0d5
+:END:
+- 2026-07-03 Fri @ 02:07 -0400 :: DRAFT → READY → DOING in one stroke: Craig
+ approved the design through five interactive prototype iterations and
+ authorized the no-approvals speedrun ("let's build them now... go"). The
+ review gate was the live prototype session itself.
+- 2026-07-03 Fri @ 02:07 -0400 :: Created (DRAFT) from the prototype session.
+
+* Metadata
+
+| Field | Value |
+|---------+--------------------------------------------------|
+| Status | doing |
+| Owner | Craig Jennings |
+| Repos | dotfiles (net/, bluetooth/, themes), archsetup |
+| Normative reference | [[file:../../working/panel-redesign/panel-console-v3.html][working/panel-redesign/panel-console-v3.html]] |
+
+* Summary
+
+Rebuild both GTK layer-shell panels (net, bluetooth) from the tabbed layout
+to the instrument-console design: one screen, no tabs, a faceplate with a
+state word + badges + radio switch + close, engraved section labels, lamp
+rows that act on click, dial meters under the console keys, and a doctor
+that does it all. The interactive prototype =panel-console-v3.html= is the
+normative design reference — when this spec and the prototype disagree on a
+visual or interaction, the prototype wins.
+
+* Decisions (all resolved — Craig, prototype session 2026-07-02/03)
+
+- Replace the tabbed panels outright. No fallback flag; git history is the
+ rollback. Net panel first, bluetooth second.
+- Advanced repair tiers leave the panel entirely. DOCTOR runs the full
+ diagnose → classify → lightest-repair → re-verify escalation (the engine
+ already does this). The surgical tiers stay CLI-only (=net repair ...=).
+- Faceplate (both panels): state lamp + state word, badges, unit label
+ (NET·01 / BT·01), radio switch (wifi radio / bt adapter power), close ✕.
+ Badges: TUNNEL (gold, net), AIRPLANE (gold, both), LOW BATT (red, bt).
+- Sections in order — net: CHANNEL, NETWORKS (+ hidden action), TUNNELS,
+ CONSOLE (DOCTOR / SPEED TEST keys), meters, output. bt: ADAPTER (with
+ clickable =discoverable= chip), PAIRED, NEARBY (+ scanning note), CONSOLE
+ (DOCTOR / SCAN), battery gauges, output.
+- Section row budgets, half-row peek, internal scroll (thin slate
+ scrollbar): NETWORKS 5.5 rows, TUNNELS 4.5, PAIRED 5.5, NEARBY 4.5.
+ In-range networks sort active-first then strongest-signal-first. Counts on
+ the engraved headers ("networks · 12 in range", "tunnels · 1 up of 9",
+ "paired · 3", "nearby · 12"). The panel silhouette never grows with list
+ length; only the output well is variable and it caps at ~170px.
+- Lamp-row grammar: green = live/connected, gold = available/actionable,
+ off = down/stored, red = failed; busy = pulsing gold during transitions.
+ Rows act on click (tunnels toggle, networks join, paired devices
+ connect/disconnect toggle, nearby devices pair).
+- Arm-first for anything disruptive or destructive, 3s auto-disarm:
+ - forget (network or bt device): hover reveals ✕; first click arms the
+ row terracotta ("forget? click ✕ again"), second fires. No dialog.
+ - disconnect (active network): click the active row; first click arms in
+ GOLD ("disconnect? click again") — disruptive, not destructive — second
+ fires.
+- Meters (net): two dials, RX·DOWN / TX·UP, gold needles, mode tag top-left
+ (LIVE green / TEST gold), HOLD tag top-right. Idle: live link throughput.
+ Speed test: cards flash gold, needles sweep the measured rate, then PIN
+ the final value with HOLD; clicking a held meter releases it to LIVE.
+ Scale 0–100 Mbps, auto-relabel to 0–1000 when a reading exceeds 100.
+ Dial top inset ~13px so the corner tags never touch the arc.
+- Speed test output well gets ONLY: location line ("location: <city> by
+ <sponsor>"), ping (+jitter), final line, conditioned tip(s). The rates
+ live in the meters, not the text.
+- Battery gauges (bt): same dial chrome; one per connected device (two
+ slots; empty slot dim "NO DEVICE"/"ADAPTER OFF"); needle+value red under
+ 15% and the LOW BATT faceplate badge lights.
+- Output well: doctor streams the checks with their narration lines
+ (viewmodel.STEP_NARRATION) and repair steps in gold; verdict line closes
+ (olive for pass/fixed). A dismiss ✕ appears in the well's corner whenever
+ it has content. Both panels.
+- WiFi radio switch: =nmcli radio wifi on|off=. Off empties NETWORKS to one
+ dim "wifi radio off" row, drops the connection, kills tailscale rows'
+ reachability; on rejoins the last network (NM autoconnect does this for
+ real). Airplane mode is system-level (Super+Shift+A owns it): both panels
+ reflect it (state word AIRPLANE, gold badge, switches down); a switch
+ flipped under airplane refuses with a toast naming the exit. A routed
+ ethernet link keeps the net panel ONLINE through airplane mode.
+- Ethernet: presence-based row pinned atop NETWORKS when a cable is up
+ ("enp… · active · wired · 1.0 Gbps" / "wired · standby"); CHANNEL swaps
+ the signal ladder for "wired · <speed> full-duplex" when routed; clicking
+ the row toggles route ownership via device disconnect/connect.
+- Pairing (bt): nearby row click → busy → passkey-confirm dialog (large
+ gold digits) → device moves to PAIRED and connects. SCAN key refreshes
+ with a "scanning…" note on the header.
+- Rename (bt): hover ✎ on a paired row → dialog prefilled → bluez
+ =set-alias= (closes the filed rename task).
+- Tooltips: any ellipsized row label carries its full text as the tooltip.
+- Dialogs (join / hidden SSID / passkey / rename) keep the in-panel dupre
+ dialog style (gold border, dark well inputs, gold caret).
+- Close: ✕ on the faceplate + Esc (already shipped; keep).
+- Folded tasks: "Network panel redesign", "Bluetooth panel: switch placement
+ + panel title", "Bluetooth panel: rename devices" — all close with this
+ build's phases.
+
+* Engine gaps (small, close during phases)
+
+- radio verb: =nmcli radio wifi on|off= helper (manage or sysio) + tests.
+- hidden-SSID join: =manage.add= grows a hidden flag
+ (=802-11-wireless.hidden yes=).
+- ethernet: device rows from =nmcli dev= (type ethernet) + disconnect/
+ connect verbs (device-level; =net down --iface= already disconnects).
+- bt rename: btctl =set-alias= one-shot verb + verify-after read.
+- bt battery: already exposed (indicator uses it).
+- speedtest meters: =run_speedtest_stream= on_update already ticks (pty).
+- link speed for wired channel line: =ethtool=-free read from
+ =/sys/class/net/<dev>/speed=.
+
+* Implementation phases
+
+1. [X] Spec + task wiring (this file; todo.org parent task with :SPEC_ID:).
+2. [ ] Net GTK-free layer (TDD): viewmodel row composers for the console
+ sections (network rows sorted+counted, tunnel rows, channel facts,
+ faceplate state word derivation, meter scale logic, arm state machines
+ for forget/disconnect), PanelModel restructure (sections, no tabs).
+ Engine gaps: radio verb, hidden join, ethernet rows, wired link speed.
+3. [ ] Net view rebuild: window_content.blp + gui.py single-page console
+ (faceplate, engraved scrolled sections, console keys, cairo dial meters
+ with mode/hold tags, output well + dismiss), panel.css additions
+ (engrave, lamps, dial, badges, arm tints). AT-SPI smoke rewritten for
+ the console layout.
+4. [ ] Net interactions: join/hidden/forget/disconnect/radio switch/
+ ethernet toggle/doctor stream/speed-test-drives-meters, toasts.
+5. [ ] Bluetooth panel: same treatment end to end (faceplate + power
+ switch, adapter chip, paired/nearby lamp rows, pair passkey flow,
+ rename via set-alias, forget arm, battery gauges + LOW BATT, DOCTOR /
+ SCAN keys, output). bt smoke rewritten.
+6. [ ] Live verification both panels on velox + all suites + smokes green;
+ summary of findings written to file; folded tasks closed; dead code
+ removed; session context finalized.
diff --git a/todo.org b/todo.org
index d0b0771..6a6611b 100644
--- a/todo.org
+++ b/todo.org
@@ -21,6 +21,11 @@ The vocabulary is open — topic tags are coined as needed — so these are conv
- *Effort / autonomy*: =:quick:= a spare-moment fix (minutes, not a sitting); =:solo:= Claude can carry it end to end — there's a build path, a test path, and no upfront decision needed (a leftover manual spot-check doesn't disqualify it).
- *Topic / area* (open): the subsystem a task touches — e.g. =:hyprland:= =:waybar:= =:mpd:= =:music:= =:network:= =:tooling:= =:llm:= =:eask:= =:pocketbook:= =:cmail:=. Coin a new one when it aids filtering.
* Archsetup Open Work
+** DOING [#B] Instrument-console rebuild: net + bluetooth panels :feature:waybar:network:bluetooth:solo:
+:PROPERTIES:
+:SPEC_ID: e73877f5-4f5b-4f81-b946-dbaa6145e0d5
+:END:
+The no-approvals speedrun build of the console design Craig approved through five prototype iterations (2026-07-02/03). Spec: [[file:docs/design/2026-07-03-instrument-console-panels-spec.org]] — the interactive prototype =working/panel-redesign/panel-console-v3.html= is the normative design reference. Folds three open tasks: network panel redesign, bt switch placement + title, bt rename devices. Code in ~/.dotfiles (net/, bluetooth/, themes/dupre/panel.css). Final step: flip the spec to IMPLEMENTED, write the findings summary to file, finalize session context.
** DONE [#B] Net diagnostics: narrate every step :feature:network:solo:
CLOSED: [2026-07-02 Thu]
:PROPERTIES:
@@ -50,22 +55,52 @@ net doctor: fixable
:END:
Work Craig's ask (roam inbox, 2026-07-02) into a spec, net/bt-panel kin: an audio panel replacing the pypr audio scratchpad (Super+A) with the same functionality — change the default/active output (speaker) and input (mic), volume control for both. The one new capability: a push-to-talk mic mode for meetings — mic stays muted except while the space bar is held, releasing re-mutes. (Hold-to-talk under Wayland needs a global key grab — likely a hyprland bind pair on press/release or an evdev listener; feasibility research belongs in the spec.) Related current bindings: Super+M audio-cycle ring, Super+Shift+A mic-toggle.
-** TODO [#B] Network panel: identify tunnel backends + richer connection info :feature:waybar:network:
-Craig's ask (roam inbox, 2026-07-02): the Tunnels rows all look alike — no way to tell which is tailscale, which is an NM wireguard/openvpn profile, and which is proton CLI without prior knowledge (e.g. when you want to bounce tailscale specifically). Fix the identification first. Second half: improve the stats under each connection — the panel is the only place to learn about a connection, effectively its info page; think through all the useful fields (backend, iface, addresses, endpoint, rx/tx, state detail) and show them.
+** DONE [#B] Network panel: identify tunnel backends + richer connection info :feature:waybar:network:
+CLOSED: [2026-07-02 Thu]
+Shipped (dotfiles =405235f=). Identification: every Tunnels row's caption now leads with its backend — "tailscale", "WireGuard (NetworkManager)", "openvpn (NetworkManager)" (from the profile's =vpn.service-type=, resolved on the panel path only), "Proton VPN CLI" — via =viewmodel.tunnel_kind_label=. Found and fixed a real gap: NM vpn-type profiles (openvpn etc.) weren't listed at all, only wireguard type. Active tunnels now carry their device's IP4 address. Info page: the active connection's live subtitle gains IP, gateway, and DNS via =build_status(full=True)= (panel poll only — the bar's one-nmcli hot path is untouched). Live-verified on velox: all 9 tunnel rows correctly labeled (tailscale w/ tailnet + peers, 7 WireGuard NM profiles, Proton CLI), live subtitle shows IP/gw/DNS on hotel wifi. 523 net tests / 45 suites / panel smoke green.
+
+Craig's ask (roam inbox, 2026-07-02): the Tunnels rows all look alike — no way to tell which is tailscale, which is an NM wireguard/openvpn profile, and which is proton CLI without prior knowledge (e.g. when you want to bounce tailscale specifically). Second half: improve the stats under each connection — the panel is effectively a connection's info page.
+
+** DONE [#B] Timer: alarm am/pm input silently fails :bug:waybar:solo:
+CLOSED: [2026-07-02 Thu]
+Fixed (dotfiles =8dd36c4=). Two root causes: =parse_alarm= only accepted 24h =HH:MM=, and =cmd_new= suppressed the ValueError, so any 12h input silently created nothing. Now accepts 24h (=14:30=, bare =14=) and all common 12h shapes (=2:30pm=, =2:30 PM=, =7:15p=, =7p=; any case, optional space, bare a/p; 12am = midnight), and input that still doesn't parse fires a fail notification instead of vanishing. 107 wtimer tests green (10 new parse cases + notify-on-error CLI tests). Manual test filed (live dialog run).
+
+Craig's report (roam inbox, 2026-07-02): when setting an alarm, entering am or pm in any fashion makes the timer silently fail. It should accept 24h and 12h variants — capitalization, spaces, bare "a"/"p" — all common forms.
-** TODO [#B] Timer: alarm am/pm input silently fails :bug:waybar:solo:
-Craig's report (roam inbox, 2026-07-02): when setting an alarm, entering am or pm in any fashion makes the timer silently fail. It should accept 24h and 12h variants — capitalization, spaces, bare "a"/"p" — all common forms. Severity Major (silent failure of a core input path) × most uses → P2.
+** DONE [#B] Timer: escape doesn't cancel the dialog flow :bug:waybar:solo:
+CLOSED: [2026-07-02 Thu]
+Fixed (dotfiles =8dd36c4=). Root cause: =_fuzzel= ignored fuzzel's exit code, so Escape (fuzzel exits 2 on a dmenu abort — confirmed in its changelog) returned "" and the flow fed it onward to the next prompt. =_fuzzel= now returns None on any non-zero exit and =cmd_new= aborts the whole flow on None at any step (type, duration/alarm, label). Escape-at-each-step covered by CLI tests against a fake fuzzel exiting 2. Manual test filed (real keyboard Escape).
-** TODO [#B] Timer: escape doesn't cancel the dialog flow :bug:waybar:solo:
-Craig's report (roam inbox, 2026-07-02): hitting cancel via escape at the step after choosing "timer" does nothing but proceed to the next step — likely the same for the other dialog steps. Escape at any step should abort the whole flow. Minor-to-Major severity × every cancel attempt → P2.
+Craig's report (roam inbox, 2026-07-02): hitting cancel via escape at the step after choosing "timer" does nothing but proceed to the next step — likely the same for the other dialog steps.
+
+** DONE [#B] Network panel: stream speedtest results live :feature:waybar:network:solo:
+CLOSED: [2026-07-02 Thu]
+FIX-UP (dotfiles =60707be=, 2026-07-03, caught by Craig): the first shipped version didn't actually stream — speedtest-go buffers all phase lines to process exit when piped (per-line arrival timestamps proved it: 25s of silence, then everything at once; the original "live" verification never checked arrival timing). The stream now runs the binary under a pty, where terminal mode redraws continuously: in-flight rates tick (download climbing like a speedometer), ANSI/spinner noise is stripped, and on_update fires per changed value. CLI closes with a "final:" settled-numbers line. Re-verified with timestamps (server +1s, ping +2s, download first tick +4s, upload +19s, final +29s) AND an AT-SPI probe of the live panel that sampled the results box mid-run: ping filled at 4s, download ticking at 12s, upload at 24s, final rows + conditioned tips at the end. 529 net tests / 45 suites green.
-** TODO [#B] Network panel: stream speedtest results live :feature:waybar:network:solo:
-Craig's ask (roam inbox, 2026-07-02): the speedtest only shows results at the end; typical speedtest UIs report the numbers as they come in. Stream the CLI's progress into the results box as it arrives, then the final numbers at the end. Related finding: the "Tip" row under the results (screenshot ~/pictures/screenshots/2026-07-02_225441.png) is a static hardcoded string shown after every run, not derived from the results — replace it with result-aware guidance or drop it (Craig asked what criteria produced it; the answer is none).
+Shipped (dotfiles =38171e8=). =run_speedtest_stream= runs speedtest-go's plain mode, whose lines land one per completed phase (parser written against a real captured hotel-wifi run). Panel: a checklist fills in as ping → download → upload arrive, final rows at the end. =net speedtest= streams the same lines at the terminal (=--json= keeps the one-shot envelope). Bonus from the text mode: jitter (rides the Ping row) and packet loss (own row, warns >1%) — the JSON mode never reported either. The static Tip is gone; =speedtest_tips= derives guidance from the numbers (high ping >100ms, download < half of upload, <10 Mbps both ways, loss >1%), each tip naming its trigger values — that's the answer to Craig's "what criteria" question: the old tip had none, the new ones are stated rules. 509 net tests / 45 suites green; live CLI run streamed correctly and fired the asymmetric-download tip on real numbers (33 down / 76 up). Manual test filed for the in-panel run.
+
+Craig's ask (roam inbox, 2026-07-02): the speedtest only shows results at the end; typical speedtest UIs report the numbers as they come in. Stream the CLI's progress into the results box as it arrives, then the final numbers at the end. Screenshot: ~/pictures/screenshots/2026-07-02_225441.png.
+
+** DONE [#C] Bluetooth bar icon: gray instead of the bar's white :bug:waybar:bluetooth:solo:
+CLOSED: [2026-07-03 Fri]
+Fixed (dotfiles =27d8eda=). Root cause: the =on= state sat in the css dim group with off/absent/degraded (a deliberate "idle dims" choice that read as broken). Removed =.on= from the dim rule in all three css copies (dupre, hudson, live style.css — the theme-drift guard suite pins them together); indicator docstring updated. Live-verified: SIGUSR2 css reload, bar screenshot shows the bt glyph in the bar's resting white alongside battery/text.
+
+Craig's report (roam inbox, 2026-07-03): the bluetooth waybar icon renders gray, not the same white as the other bar module icons.
+
+** DONE [#B] Bluetooth panel: close button like the net panel :feature:waybar:bluetooth:solo:
+CLOSED: [2026-07-03 Fri]
+Shipped (dotfiles =42c93d6=): a flat circular Close button right of the tab switcher (accessible "Close" label, "Close (Esc)" tooltip), wired to window.close. The bt smoke asserts it exists AND that clicking it exits the panel (run green live). Plot twist answered in-session: the net panel had no close button either — Craig's leaner-chrome pass removed it 2026-07-01 (787b475) on the Esc-suffices theory; he asked where it went, so it was restored with the same tab-row button (=6a0aff7=, net smoke extended the same way). Both panels match again.
+
+Craig's ask (roam inbox, 2026-07-03): the bt panel needs a close button matching the network panel's.
** TODO [#B] Bluetooth panel: switch placement + panel title :feature:waybar:bluetooth:solo:
+FOLDS INTO the instrument-console rebuild (spec e73877f5, DOING above) — closes with that build's phases.
+
Craig's ask (roam inbox, 2026-07-02): move the bluetooth on/off switch above all the buttons. "Bluetooth" becomes the panel's title, with the on/off switch right-justified on that title row. Panel code in ~/.dotfiles =bluetooth/= (GTK4 + Blueprint, phase-2 PanelModel/presenter — see the shipped panel task in Resolved). Presenter tests + the AT-SPI smoke likely need their layout assertions updated.
** TODO [#B] Bluetooth panel: rename devices :feature:waybar:bluetooth:solo:
+FOLDS INTO the instrument-console rebuild (spec e73877f5, DOING above) — closes with that build's phases.
+
Craig's ask (roam inbox, 2026-07-02): the panel should be able to rename a device. bluez supports per-device aliases (=bluetoothctl= device menu =set-alias=; the one-shot invocation shape needs verifying at the btctl boundary). Wire it through the engine (=bluetooth/src/bt/=) with a verify-after read, and surface a rename affordance on the device row consistent with the panel's existing patterns.
** DONE [#B] Network panel: other network interfaces (tailscale, VPNs, wireguard) :feature:waybar:network:
@@ -150,6 +185,8 @@ Refiled from the archsetup task audit (2026-06-28), landed via ~/.dotfiles/inbox
:PROPERTIES:
:LAST_REVIEWED: 2026-07-02
:END:
+FOLDS INTO the instrument-console rebuild (spec e73877f5, DOING above) — closes with that build's phases.
+
Major evolution of the shipped =custom/net= module ([[file:docs/design/2026-06-29-waybar-network-module-spec.org]]).
Reverses the spec's "privileged tiers run in a net-popup terminal" decision. Origin:
design conversation 2026-06-30.
@@ -841,6 +878,26 @@ Parse yay errors and provide specific, actionable fixes instead of generic error
Enhance existing indicators to show what's happening in real-time
** TODO Manual testing and validation
+*** Timer dialog: Escape cancels at every step
+What we're verifying: Escape in the real fuzzel dialogs aborts the whole flow (the fix rides fuzzel's abort exit code; the unit tests fake it, this is the live confirmation).
+- Left-click the timer module on the bar, pick "timer", then hit Escape at the Duration prompt.
+Expected: no Label prompt appears and no timer is created (bar count unchanged).
+- Left-click again and hit Escape at the very first Timer-type menu, and once more at the Label prompt after entering a duration.
+Expected: each Escape ends the flow with nothing created.
+
+*** Timer alarm: 12-hour time accepted, bad input notifies
+What we're verifying: the alarm prompt takes 12h shapes live and errors are audible instead of silent.
+- Left-click the timer module, pick "alarm", enter 2:30pm (or any 12h form), Enter through the label.
+Expected: an alarm appears on the bar showing 14:30 as its fire time.
+- Repeat with garbage like 99:99.
+Expected: a fail notification names the bad input; nothing is created.
+- Cancel the test alarm (right-click the module, pick it).
+
+*** Speed test streams in the panel
+What we're verifying: the panel's speed test fills in as phases complete instead of dumping everything at the end (the CLI path is live-verified; this is the GTK rendering).
+- Open the net panel, Diagnostics → Network Performance → Run Speedtest.
+Expected: a Ping/Download/Upload checklist appears under the running row; ping lands within seconds, download mid-run, upload near the end; then the final rows (with jitter on Ping, a Packet loss row) replace it. A Tip row appears only if a rule fired, and it names the numbers that triggered it.
+
*** Proton VPN CLI sign-in (velox now, ratio on its trip)
What we're verifying: the proton CLI has its own account store (separate from the retired GTK app's), so the panel's proton rows can't toggle until you sign in once per machine.
- Run in a terminal: protonvpn login <your-proton-username> (it prompts for the password).