aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/2026-07-02-timer-panel-spec.org
diff options
context:
space:
mode:
Diffstat (limited to 'docs/specs/2026-07-02-timer-panel-spec.org')
-rw-r--r--docs/specs/2026-07-02-timer-panel-spec.org221
1 files changed, 221 insertions, 0 deletions
diff --git a/docs/specs/2026-07-02-timer-panel-spec.org b/docs/specs/2026-07-02-timer-panel-spec.org
new file mode 100644
index 0000000..275bb2c
--- /dev/null
+++ b/docs/specs/2026-07-02-timer-panel-spec.org
@@ -0,0 +1,221 @@
+#+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 — redesign built and shipped to dotfiles in a no-approvals speedrun (5 commits =c7ac193=..=5a863b5=): the wtimer engine (timer repeat, recurring alarms with snooze/ringing/dismiss, =@half=/=@hour=/=+dur= alarm parse, the rebuilt configurable pomodoro cycle, bar-tooltip parity), the PanelModel view-data rebuild (=row_view=, ringing-first sort, per-type create options as flags, locked presets + half-past + named pomodoro cycles), the GTK hero-on-top panel (Cairo progress ring + stopwatch sweep dial, per-type create strips, one transport row, close ✕/Esc), and the bar tooltip parity. wtimer + timer suites 231 green, full =make test= green. Live GTK render is the manual checklist (todo.org). Stopwatch run-save deferred to vNext.
+- [2026-07-05 Sun] DOING — UI/UX redesign decided through a prototype process (research → brainstorm → several directions → iterate to final; see Prototype iterations below). The shipped v1 panel stands, but this rewrite supersedes its layout and adds functionality: a hero-on-top + rack layout, a live waybar-module preview at parity with =wtimer render=, per-type create-strip features (timer auto-repeat; alarm recurring weekdays + snooze + a ringing state; a configurable pomodoro cycle with work/rest short+long and a long-break interval; a stopwatch analog sweep dial + last-lap badge), presets (renamed from "chips") whose shipped defaults are locked and whose load flashes the fields, and a header close button. Stopwatch run-save is cut to a vNext. Rebuild pending — folds this decided design into the shipped =timer/= package.
+- [2026-07-05 Sun] IMPLEMENTED — v1 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). This is the base the 2026-07-05 redesign iterates on.
+- [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."
+
+** Prototype iterations
+The redesign ran through the UI/UX prototype process (see the =ui-prototyping= rule proposed to rulesets, 2026-07-05). Full working HTML prototypes over one shared engine, in the dupre instrument-console aesthetic; each iteration links here, newest last, so the design history is walkable.
+- [[file:../prototypes/2026-07-02-timer-panel-prototype-1.html][prototype-1]] — three initial directions over one shared engine: rack unit (faithful vertical list), transport deck (hero + track list), channel-strip board (vertical faders). Predates the formalized five-direction count.
+- [[file:../prototypes/2026-07-02-timer-panel-prototype-2.html][prototype-2]] — chose the rack direction; flipped to hero-on-top → create strip → list; made pomodoro a configurable cycle; locked the shipped presets (default cycle undeletable); dropped the stopwatch/pomodoro value entry.
+- [[file:../prototypes/2026-07-02-timer-panel-prototype-3.html][prototype-3]] — FINAL. Live waybar preview; hero donut moved right with one full-width button row; stopwatch sweep dial + ghost lap badge; alarm recurring days + snooze + ringing state; timer repeat; half-past alarm preset; presets flash on load; header close (Esc / bar-click reopen); verbatim tooltip labels; stopwatch save deferred.
+
+* 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 (decided in prototype-3)
+
+Top-to-bottom, one column:
+
+- Header: brand + live item count + Clear All + a flat circular close ✕
+ (tooltip "Close (Esc)"), matching the net/bt/audio panels. Esc closes;
+ clicking the bar's timer module reopens it (mirrors =on-click: timer-panel=).
+- Hero (the primary / bar-slot item): the info block (type badge, any feature
+ badges, pomodoro cycle dots, label, big countdown, subline) on the left with
+ the progress donut on the right, and all its controls in one full-width,
+ left-justified button row beneath. Countdown types show a filling progress
+ ring; a stopwatch shows an analog sweep dial (a gold second-hand, one
+ revolution per minute) with its last lap as a bordered ghost badge beside the
+ count — no fake progress ring for a count-up. The ‹ › keys cycle the primary
+ through the whole queue, wrapping at either end.
+- Create strip: the four type buttons, then a per-type body — presets (renamed
+ from "chips") + a freeform entry validated by wtimer's parsers + an optional
+ label, plus per-type extras (see Build scope). Loading a preset flashes the
+ target fields rather than toasting. Shipped presets are locked (no delete);
+ only presets you add carry a ×.
+- Queue list: the rest of the items (everything but the hero), soonest-fire
+ first, one rack row each — lamp, glyph, label, subline, countdown, and inline
+ pause / promote / cancel (two-stage arm). Stopwatches are promotable to the
+ hero like any other item. With a single item the list reads "Only one item is
+ queued. Add more above." Empty state: hero shows "No timers running", create
+ strip below.
+
+** Waybar module parity
+
+A live preview above the panel renders exactly what =wtimer render= emits for
+the bar: =<large glyph> <countdown>= plus =+N= for the other items, recoloured
+by state (urgent < 60 s terracotta, paused dim, pomodoro-work gold,
+pomodoro-break sage, idle silver), with the full per-item hover tooltip. Tooltip
+lines show each item's label verbatim — no phase word appended. The panel and
+the bar stay one source of truth via the wtimer watch subscription.
+
+** 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 (decided design — folds the prototype-3 redesign into the shipped =timer/= package)
+
+The panel is the existing =timer/= dotfiles package (src-layout, GTK4 + gtk4-layer-shell, humble-object PanelModel, instrument-console faceplate). wtimer stays the state engine; the panel is a view over it. This rebuild reshapes the layout (see Layout sketch) and adds the per-type functionality below. UI idioms draw from the widget gallery (=docs/prototypes/2026-07-03-panel-widget-gallery-prototype.html=); the reference build is prototype-3.
+
+Queue + primary:
+- Up to 10 items, auto-sorted by soonest fire time (four buckets: active countdown < paused countdown < active stopwatch < paused stopwatch). The soonest-firing is the hero/primary (the bar glyph slot). Promote via a row's promote key or by cycling ‹ ›; cycling and promotion include stopwatches and wrap around the whole list.
+- The hero shows the primary big; the list shows the rest. Clear All cancels everything.
+
+Types + create strip:
+- *Timer*: preset durations 5m / 25m / 10m / 15m / 30m / 60m / 2h (5m and 25m first), freeform entry (wtimer parser), optional label, and a *repeat* toggle — a repeating timer re-arms itself on fire instead of clearing.
+- *Alarm*: presets +30m / top-of-hour / *half-past* (next X:30) / 07:00, freeform HH:MM, optional label, a *recurring weekday* selector (S M T W T F S, with weekdays / daily shortcuts) and a *snooze* duration. An alarm fires into a *ringing* state rather than silently vanishing: the hero/row shows SNOOZE (re-arm by the snooze minutes) and DISMISS (a recurring alarm re-arms to its next matching day; a one-shot clears).
+- *Stopwatch*: no value entry — counts up from zero. Lap (unlimited) and Stop. The hero shows an analog sweep dial and the last lap as a ghost badge beside the count. *Run-save is deferred to a vNext* (cut from v1's org-save plan — see the status history).
+- *Pomodoro*: a configurable cycle — Work and Rest each with a short and a long duration, a "long break every N cycles" interval, and an auto-advance toggle. Every Nth ("deep") cycle uses the long work + long rest together. Cycle progress shows as dots in the hero and row. With auto-advance off, each phase end waits for a Start press. Preset cycles (Classic 25/5/15, Deep 50/10/30, Sprint 15/3/10) load the fields.
+- *Presets*: shipped defaults are locked (undeletable — the pomodoro default cycle can't be removed); presets you add carry a × and are deletable. Loading any preset flashes the target fields (no toast).
+
+Live updates + notifications:
+- A =wtimer watch= subcommand emits state on every change (state-file watch → JSON lines on stdout); the panel subscribes for push updates instead of polling (decision D). Notifications for alarms and timers go through the =notify= script — wtimer stays the single notification owner.
+
+Bar + window:
+- =custom/timer= left-click opens the panel; =wtimer render= stays the bar indicator (glyph + countdown + =+N=, state-coloured, verbatim tooltip labels). A header close ✕ and Esc close the panel; clicking the bar module reopens it. The =wtimer new= fuzzel creation flow is retired (decision B).
+
+* 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.
+
+The decisions below were resolved live through the prototype iteration (2026-07-05), each seen working in a prototype before being written down.
+
+** DONE Layout: hero on top, then create strip, then the queue list
+CLOSED: [2026-07-05 Sun]
+Resolved: the primary item rides a hero at the top (info left, donut right, all controls in one full-width button row), the create strip sits under it, the rest of the queue below. Chosen over the transport-deck and channel-strip directions in prototype-1.
+
+** DONE Stopwatch hero visual: analog sweep dial, not a progress ring
+CLOSED: [2026-07-05 Sun]
+Resolved: a count-up stopwatch shows a gold second-hand sweeping once per minute, with its last lap as a bordered ghost badge beside the count — not a fake progress ring (a stopwatch has no target to be a fraction of).
+
+** DONE Alarm: recurring weekdays + snooze + a ringing state; add a half-past preset
+CLOSED: [2026-07-05 Sun]
+Resolved: alarms carry a weekday-repeat selector and a snooze duration, and fire into a ringing state with SNOOZE / DISMISS rather than vanishing. A half-past preset joins top-of-hour (fires at the next X:30). Drawn from Alarm Clock Xtreme / Alarmy.
+
+** DONE Timer: auto-repeat toggle
+CLOSED: [2026-07-05 Sun]
+Resolved: a timer can repeat — it re-arms itself on fire instead of clearing. Drawn from MultiTimer / Multi Timer.
+
+** DONE Pomodoro: a fully configurable cycle
+CLOSED: [2026-07-05 Sun]
+Resolved: Work and Rest each get a short and a long duration, plus a long-break-every-N interval and an auto-advance toggle; every Nth deep cycle uses the long work + long rest; progress shows as cycle dots. The default cycle preset is locked (undeletable). Drawn from Pomofocus / the classic technique.
+
+** DONE Presets (formerly "chips"): lock defaults, flash on load
+CLOSED: [2026-07-05 Sun]
+Resolved: rename "chips" to "presets"; shipped defaults are locked (no delete), presets you add are deletable; loading a preset flashes the target fields instead of firing a toast.
+
+** DONE Stopwatch run-save: deferred to a vNext
+CLOSED: [2026-07-05 Sun]
+Resolved: v1's "save the run's splits to an org file on stop" is cut from this build. Stop just stops. Revisit in a vNext if the need is real.
+
+* Implementation phases (redesign rebuild)
+
+Folding prototype-3 into the shipped =timer/= package. TDD throughout — GTK-free
+logic first, GUI last — reviewing between phases. Each phase is a dotfiles commit
+under the archsetup-owns-dotfiles rule.
+
+1. wtimer engine: alarm recurring-days + snooze + a ringing state, timer repeat,
+ the configurable pomodoro cycle (work/rest short+long, long-break interval,
+ auto-advance, deep cycle), half-past parsing, and the watch/subscribe mode
+ (decision D). Extend wtimer's own suite per addition.
+2. PanelModel: the four-bucket soonest-fire sort, promote/cycle wrap (stopwatches
+ included), per-type create validation + presets (locked defaults, custom
+ delete, flash-on-load), and the row/hero view data (sweep fraction, cycle
+ dots, last lap, feature badges, ringing controls). GTK-free, unit-tested like
+ the net PanelModel.
+3. GTK GUI: the hero (progress ring / sweep dial, one full-width button row, lap
+ badge), the per-type create strip (timer repeat toggle; alarm weekday selector
+ + snooze; pomodoro config grid; presets that flash), the header close ✕,
+ Esc-to-close, and bar-click reopen.
+4. Bar parity: =wtimer render= tooltip labels verbatim, state classes confirmed;
+ panel and bar track one state via the watch subscription.
+5. AT-SPI smoke + a manual-testing checklist (todo.org). Retire the =wtimer new=
+ fuzzel flow (decision B) after the panel proves out.
+
+Deferred to a vNext: stopwatch run-save (an org log of splits).