desktop: an agent and a mini timer, not an application window
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>
This commit is contained in:
26 files changed
+752
-2690
No files matched your search
@@ -10,7 +10,7 @@ Four pieces, one backend:
|
||||
| --- | --- | --- |
|
||||
| **Backend** | FastAPI + Postgres: the clock, the task mirror, the blocking policy, the reports, the Telegram bot | Docker |
|
||||
| **Web app** | React + Material UI — the whole thing in a browser, on any device | Docker (same container) |
|
||||
| **Desktop app** | A **native Qt/QML** app: tray, D-Bus, real notifications, and the part that actually blocks websites and apps | Your machine |
|
||||
| **Desktop agent** | A **native Qt/QML** tray agent: D-Bus, real notifications, a floating mini timer, and the part that actually blocks websites and apps | Your machine |
|
||||
| **Android app** | A native Compose client for the Nothing Phone (3): the interval as an Android 16 Live Update, and the countdown on the rear Glyph Matrix | Your phone |
|
||||
|
||||
The server owns the clock. Start a pomodoro on your phone's browser, watch it
|
||||
@@ -54,8 +54,8 @@ list is mirrored into the database on every refresh, so the UI is instant and
|
||||
the daily report still works when a source is down.
|
||||
|
||||
### A focus guard that actually blocks
|
||||
Rules live on the server (edit them in the web app or the desktop app); the
|
||||
native desktop app enforces them on your machine:
|
||||
Rules live on the server and are edited in the web app; the native desktop
|
||||
agent enforces them on your machine:
|
||||
|
||||
* **Websites** — `/etc/hosts` is rewritten through a tiny privileged helper under
|
||||
polkit, and the masks a hosts file cannot express are answered by a local DNS
|
||||
@@ -140,7 +140,7 @@ cp .env.example .env # fill in Jira / Todoist / Telegram (optional)
|
||||
docker compose up -d --build # → http://localhost:8000
|
||||
```
|
||||
|
||||
That is the backend and the web app. Then the native desktop app:
|
||||
That is the backend and the web app. Then the desktop agent:
|
||||
|
||||
```bash
|
||||
# Arch: the only dependency is PyQt6, which is a system package
|
||||
@@ -152,8 +152,10 @@ sudo ./desktop/helper/install.sh # the privileged helper, for website bl
|
||||
kadence-desktop
|
||||
```
|
||||
|
||||
Point it at the backend in **Settings → Backend** (it defaults to
|
||||
`http://localhost:8000`).
|
||||
It has no application window: it sits in the tray, and the countdown is a
|
||||
floating pill you can put in a corner. Point it at the backend from the tray's
|
||||
**Settings…** (or `kadence-desktop --settings`); it defaults to
|
||||
`http://localhost:8000`.
|
||||
|
||||
### Without Docker
|
||||
|
||||
@@ -201,7 +203,7 @@ and you can fill everything in from **Settings**.
|
||||
└──────────────┘ │ • task mirror + write-back │ │ Google Tasks │
|
||||
│ • blocking policy │ │ Laundry │
|
||||
┌──────────────┐ │ • reports + achievements │ └───────────────┘
|
||||
│ Desktop app │──/api──│ • scheduler (briefing, │
|
||||
│ Desktop agent│──/api──│ • scheduler (briefing, │
|
||||
│ Qt/QML, D-Bus│ │ report, nudges) │───────▶ Telegram ⇄ you
|
||||
│ hosts+DNS+ │ └───────────────────────────────┘
|
||||
│ /proc │ │
|
||||
@@ -216,11 +218,15 @@ and you can fill everything in from **Settings**.
|
||||
served from the same origin (so there is no CORS to configure).
|
||||
* **The look** — warm paper, ink type, hairline rules instead of boxes and
|
||||
shadows, Fraunces for figures and headings, Inter for everything else, one
|
||||
ember accent. The desktop app's `Theme` singleton carries the same tokens as
|
||||
the web app's `tokens.ts`, so the two clients are one product. Chart colours
|
||||
ember accent. The desktop agent's `Theme` singleton carries the same tokens as
|
||||
the web app's `tokens.ts`, so the clients are one product. Fraunces has no
|
||||
tabular figures, so every countdown set in it — the desktop pill, the phone's
|
||||
dial — lays each glyph on a fixed advance rather than letting the clock jitter. Chart colours
|
||||
come from a separate, validated data palette: the brand is warm, but a chart
|
||||
has to stay readable for colour-blind readers in both themes.
|
||||
* `desktop/` — the native app. See [`desktop/README.md`](desktop/README.md).
|
||||
* `desktop/` — the native agent: tray, mini timer, one settings dialog, and the
|
||||
enforcement. There is deliberately no window with pages in it — lists, rules
|
||||
and reports are the web app's job. See [`desktop/README.md`](desktop/README.md).
|
||||
* `android/` — the phone client. Everything with interesting behaviour lives in a
|
||||
plain-JVM `:core` module, so 234 tests run on a laptop with nothing but a JDK. See
|
||||
[`android/README.md`](android/README.md).
|
||||
|
||||
Reference in new issue
Block a user