The design of the full desktop app was still bad, and the reason it stayed bad
is that it had no business existing. Five pages — Timer, Tasks, Blocker, Report,
Settings — each a second view of the *server's* data: a second place to be
wrong, a second place to style, and a second place to keep in step with a web
app that already does lists, rules and reports properly in a browser, on any
device. So it is gone, along with 2,100 lines of QML and the controller surface
that fed it.
What is left is what only a program on your machine can do, plus the two
surfaces worth having in front of you:
* **the tray menu — unchanged**, as asked. All fourteen entries, plus
"Settings…". "Open Kadence" now brings the countdown up, because that is what
it meant all along.
* **the mini timer**, which is now the whole of this app's own interface and so
finally carries the house style on its own: warm surface, hairline instead of
shadow, a letterspaced eyebrow, the progress lane as a rule, and exactly one
filled button — whichever action is the obvious next one. The countdown is set
in the display serif on a **fixed advance**, because Fraunces has no tabular
figures and ships no `tnum`: each glyph now sits in a box as wide as the widest
digit, instead of the clock shifting sideways on every tick. The phone client
documents the same trap; the desktop had it too and nobody had looked.
It also says OFFLINE when the backend is away — the count and the clock are
both the server's, and this is the only window left to say it in.
* **a settings dialog**, because a fresh install has to be pointed at a backend
somewhere. One column, hairline sections: the backend and its token, where the
mini timer sits, what this machine enforces, and what it does to the session.
It edits `desktop.json` and nothing else — with one honest exception, view-only
mode, which is a server-side flag the other clients have to honour too.
Structurally, `app.py` no longer loads a QML root at all: a plain `QQmlEngine`
with the mini timer as a component, and the dialog built on demand. Everything
that used to ask "is the main window visible?" is simpler for not having one —
the mini timer's auto-show rule is now just "an interval is running", which is
what the setting always claimed.
The window-only controller surface goes with the pages: tasks and task writes,
stats, the daily report, the settings document, the presets, the cycle and
interruption counters, and their worker jobs and API calls. Two things were kept
deliberately: the task list is still read once a minute for one fact — which task
is pinned, so "Start focus" from the tray attaches what you pinned this morning —
and `timerEnabled` now comes from the timer state, which already carries
`timer_enabled`, rather than from a second fetch of a document this app can no
longer edit.
Also: `--settings` and a `Settings` D-Bus verb; toasts go to the tray's balloon
now that the snackbar is gone; the launcher entry starts the agent and grew a
Settings action; a systemd user unit is shipped rather than only written at
runtime; and both READMEs say what this piece is.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A hosts file cannot express a wildcard, so '*.youtube.com' was only ever the
base domain plus the guessed labels. The patterns the server now hands over
as unexpandable are answered by a stub on 127.0.0.1, and the helper owns one
systemd-resolved drop-in that points just those zones at it:
* the stub is listening before the drop-in is written, and the drop-in is
removed before the stub stops, so a zone's queries never land on a dead
port;
* stub and route live as long as the app, not the interval: between
intervals they forward everything, instead of restarting resolved (and
dropping its cache) on every pomodoro;
* a crashed run leaves a world-readable drop-in the next start adopts, and
the stub comes back before anything else;
* masks with nothing routable after their last '*' ('*gram*') are reported
as unenforceable, not silently dropped.
The Blocker page now reports what is really in place — the hosts region, the
routed zones, and the masks this machine cannot do — rather than the whole
expansion. 39 desktop tests, including the ordering invariants.
- Refactored TimerPage component to improve layout and user experience.
- Updated theme configuration to utilize design tokens for better maintainability.
- Introduced new tokens for colors and typography to align with design principles.
- Simplified state management and improved timer functionality.
- Enhanced accessibility and responsiveness of UI elements.