#+TITLE: Timer GTK Panel #+AUTHOR: Craig Jennings #+DATE: 2026-07-02 #+TODO: TODO | DONE #+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED * IMPLEMENTED Timer GTK Panel :PROPERTIES: :ID: 25ed5321-f035-42b3-b115-69364d775f41 :END: - 2026-07-04 Sat @ 12:36:56 -0500 — retrofitted by spec-sort; status set to DRAFT (evidence-based, human-confirmed) * IMPLEMENTED Status :PROPERTIES: :ID: 1770af2e-b093-4024-a512-ae4324a2869f :END: - [2026-07-05 Sun] IMPLEMENTED — built and shipped to dotfiles in a no-approvals speedrun (4 commits 1f4f270..78d3cbb): wtimer watch/lap/save; a new timer/ package with a GTK-free PanelModel (62 tests) + the GTK instrument-console panel; bar integration (custom/timer opens the panel, the fuzzel creation flow retired, Hyprland float rule added). Code-complete; live GTK behavior awaits Craig's manual pass (filed under "Manual testing and validation" in todo.org) — a failing check promotes to a bug. - [2026-07-05 Sun] DOING — Craig directed the build (no-approvals speedrun). Folded in the cj input from the sibling waybar-timer-module spec (GTK app styled like the panels; a queue/output-wall auto-sorted by fire time; stopwatch lap/stop + saveable runs; notify integration; 5/25-min configurable+deletable defaults; up to 10 timers; widget-gallery elements) — see Build scope below. Bypassed the READY spec-review step at Craig's direction; the four decisions were already resolved. - [2026-07-04 Sat] DRAFT — all four decisions resolved by Craig (standalone; retire fuzzel once the panel lands; timer chips gain 10m/30m/2h; wtimer watch mode over polling). Decision-complete; ready for a spec-review to flip it READY before build. - [2026-07-02 Thu] DRAFT — initial spec from Craig's roam capture "give the timer a gtk UI/UX like the network panel. spec this out." * Metadata | Field | Value | |--------+---------------------------------------------------| | Status | implemented | |--------+---------------------------------------------------| | Owner | Craig Jennings | |--------+---------------------------------------------------| | Repo | dotfiles | |--------+---------------------------------------------------| | Kin | net panel (architecture donor), wtimer (backing), | | | desktop-settings panel spec (sibling) | |--------+---------------------------------------------------| * Problem The timer's whole UI is a chain of three fuzzel prompts (type, value, label) plus a fourth for cancel. That flow can't show what's already running while you create, can't offer one-tap presets, gives no feedback on a typo until the add silently fails, and pomodoro state (phase, cycle) is only visible in a tooltip. The 2026-07-02 styling pass made the dialogs presentable, but the shape is still four blind modals for what is really one small control surface. * Goals 1. One panel, opened from the bar's timer module, that shows everything running (live countdowns, pomodoro phase/cycle, paused state) and creates new items without leaving it. 2. One-tap presets for the common cases (tea, pomodoro, quick alarm) next to freeform entry, with inline validation before the add. 3. Per-item controls: pause/resume, cancel, promote to primary (the bar glyph slot). 4. wtimer stays the single owner of timer state and the notification path; the panel is a view over it, never a second engine. * Design sketch ** Architecture — clone the net panel's proven stack - GTK4 + gtk4-layer-shell dropdown anchored under the timer module, Blueprint .blp compiled to committed .ui (=make ui=; compiler is dev-only). - Humble-object split: GTK-free PanelModel presenter, unit-tested to 100%, with thin widget bindings; one gated AT-SPI smoke via the run-panel-smoke.sh pattern. - Backing: shell out to the existing wtimer CLI (=add=, =toggle=, =cancel=, =cycle=, =render=). =render= already emits a JSON payload. Live state comes from a new wtimer watch/subscribe mode (decision D), which the panel subscribes to for push updates instead of polling =render= on a timer. wtimer's 89-case suite keeps owning the logic; panel tests fake the CLI like every dotfiles suite fakes binaries. - Dupre WIP palette CSS shared with the net panel (same factoring the desktop-settings spec calls for — one palette asset, three panels). ** Layout sketch - Header row: running-item count + a Clear All button (maps to cancel-all). - Item list: one row per item — type glyph, label, live countdown / clock time / phase+cycle for pomodoro, pause and cancel buttons, click-to-promote. - Create strip: four type buttons (the wtimer glyphs), preset chips per type (timer 5m / 10m / 15m / 25m / 30m / 60m / 2h; alarm +30m / top-of-hour / 07:00; pomodoro default cycle; stopwatch none — decision C), a freeform entry validated with wtimer's own parsers, an optional label field. - Empty state: the create strip alone, centered. ** What happens to the fuzzel flow Decision B (below) resolved this: the fuzzel chain retires once the panel lands. The panel becomes the single creation surface, replacing both the click-driven bar path and the keybind/fuzzel path. Until the panel ships the fuzzel flow stays (it's styled and tested); phase 4 removes it after the panel proves out. * Build scope (consolidated 2026-07-05 — the four decisions plus Craig's cj input) The panel is a new =timer/= dotfiles package mirroring =net/= and =audio/= (src-layout, GTK4 + gtk4-layer-shell, humble-object PanelModel, instrument-console faceplate aesthetic — machined plate, engraved section labels, status lamps, console keys). wtimer stays the state engine; the panel is a view over it. Create + queue: - A configure strip (top): pick timer / alarm / stopwatch / pomodoro, set the value (preset chips per decision C + a freeform entry validated by wtimer's parsers, optional label). A =+= adds the configured item to the queue. - The queue is an output-wall-style list (the instrument-console output well), *auto-sorted by soonest fire time* (the item that notifies next is on top). One row per item: type glyph, label, live countdown / clock time / pomodoro phase+cycle, pause/resume, cancel, click-to-promote (bar glyph slot). - Up to 10 timers; the two starting timer presets default to 5 min and 25 min, and the preset set is configurable and deletable. Stopwatch: - A running stopwatch row has a Lap button and a Stop button. Lap records the elapsed time at the press; unlimited laps; a lap can optionally be named (non-interruptive — naming never blocks further laps). On stop, the full run (splits + optional names) can be saved to review later. Save target: an org file (default =~/org/stopwatch-runs.org=, override via a config key) — one heading per run with a table of laps. Live updates + notifications: - A new =wtimer watch= subcommand emits state on every change (state-file watch → JSON lines on stdout); the panel subscribes for push updates instead of polling. The bar may adopt it later. - Notifications for alarms and timers go through the =notify= script (wtimer already fires notify on completion; keep that path the single notification owner). UI elements: draw from the panel widget gallery prototype (=docs/prototypes/2026-07-03-panel-widget-gallery-prototype.html= in the archsetup repo) for the console keys, lamps, output-well rows, and chips, matching the shipped net/bt/audio look. Retire the old timer: the bar's =custom/timer= on-click drives =wtimer new= (the fuzzel chain). Rewire the bar module's on-click to open this panel, and retire the =wtimer new= fuzzel creation flow (decision B). Keep =wtimer render= as the bar indicator and the wtimer engine as the state source. * Decisions (Craig) ** DONE Panel scope: standalone timer panel, or a page in the desktop-settings panel? CLOSED: [2026-07-04 Sat] Resolved (Craig, 2026-07-04): standalone, sharing the palette/css asset. Matches the net panel's one-domain-one-panel shape and keeps the timer dropdown small. ** DONE Fuzzel flow: keep as keyboard fast lane, or retire once the panel lands? CLOSED: [2026-07-04 Sat] Resolved (Craig, 2026-07-04): retire the fuzzel flow once the panel lands. The panel becomes the single creation surface; the keybind chain goes away rather than staying as a parallel path. (Implementation phase 4's "decide the fuzzel flow's future" is now decided — retire, don't keep.) ** DONE Presets: which chips per type? CLOSED: [2026-07-04 Sat] Resolved (Craig, 2026-07-04): timer chips are 5m / 10m / 15m / 25m / 30m / 60m / 2h (the strawman plus 10m, 30m, 2h). Alarm +30m / top-of-hour / 07:00, pomodoro default cycle only, stopwatch none — as the strawman. ** DONE Live updates: poll render (1s, like the bar) or a wtimer "watch" mode? CLOSED: [2026-07-04 Sat] Resolved (Craig, 2026-07-04): a wtimer watch/subscribe mode, not 1s polling. This grows wtimer with a new watch capability that the panel (and potentially the bar) subscribes to for live state, rather than reusing the poll cadence — cleaner at the cost of a wtimer addition. Fold the watch mode into the phase 1 CLI-backing seam. * Implementation phases 1. PanelModel presenter + CLI-backing seam (TDD, GTK-free, 100% like the net PanelModel), plus the wtimer watch/subscribe mode (decision D) the presenter subscribes to for live state. 2. Blueprint UI: item list + create strip, wired to the presenter; palette css factored to the shared asset. 3. Bar integration: timer module left-click opens the panel (replacing the fuzzel menu binding there); the panel and bar both track state via the wtimer watch subscription. 4. AT-SPI smoke + manual-testing checklist; retire the fuzzel flow (decision B) after the panel proves out over a week of real use.