1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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).
|