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>
59 lines
1.9 KiB
QML
59 lines
1.9 KiB
QML
// The countdown, in the display serif, on a fixed advance.
|
|
//
|
|
// Fraunces has **no tabular figures**: its digits are proportional (`1` is 1024
|
|
// units against `0` at 1461) and the font ships no `tnum` feature, so a clock
|
|
// set in it jitters sideways on every tick — the one thing a clock must not do.
|
|
// `font.features` cannot fix what the font does not contain, so each glyph is
|
|
// laid in a box as wide as the widest digit instead. The separators keep their
|
|
// natural width, because a colon in a digit-wide box reads as a gap.
|
|
//
|
|
// The Android client's `TabularFigures` solves the same problem the same way;
|
|
// Inter *does* ship `tnum`, which is why the sans numbers elsewhere only need a
|
|
// font feature.
|
|
import QtQuick
|
|
|
|
import ".."
|
|
|
|
Row {
|
|
id: figures
|
|
|
|
property string text: ""
|
|
property int pixelSize: 34
|
|
property color ink: Theme.ink
|
|
|
|
readonly property font figureFont: Qt.font({
|
|
family: Theme.displayFamily,
|
|
pixelSize: figures.pixelSize
|
|
})
|
|
|
|
// Measured rather than assumed: with Fraunces absent the fallback serif may
|
|
// already be tabular, in which case this is simply the digit width.
|
|
readonly property real cell: {
|
|
var widest = 0
|
|
for (var digit = 0; digit < 10; digit++)
|
|
widest = Math.max(widest, metrics.advanceWidth(String(digit)))
|
|
return widest
|
|
}
|
|
|
|
FontMetrics { id: metrics; font: figures.figureFont }
|
|
|
|
Repeater {
|
|
model: figures.text.length
|
|
delegate: Item {
|
|
required property int index
|
|
readonly property string glyph: figures.text.charAt(index)
|
|
readonly property bool isDigit: glyph >= "0" && glyph <= "9"
|
|
|
|
width: isDigit ? figures.cell : metrics.advanceWidth(glyph)
|
|
height: metrics.height
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: parent.glyph
|
|
font: figures.figureFont
|
|
color: figures.ink
|
|
}
|
|
}
|
|
}
|
|
}
|