4.4 KiB
TASK 31 — The radial dial
Status: component and invocation live 2026-07-28 — geometry, haptic
detents, gesture routing table, and a squeeze that opens it through sessiond.
Left: the contents. DialHost.qml:75 still holds a hand-written entries
list; it must come from the verb tables (TASK-30), which is the whole argument
below. Size: one session for the contents. Repo: ~/Projects/souveraine
(surfaces/quickshell).
Goal
A radial dial of options, summoned by a gesture, reachable by thumb without looking. The phone's fastest path from "I want a thing" to the thing.
The idea worth getting right first
The dial and the agent's verb table are the same vocabulary. One rendered for a thumb, one for a composer. A dial whose entries are a hand-written list is a third place device capabilities are enumerated — after the verb table and the manifests — and it will drift from both within a month.
So the dial's contents come from the surfaces' describe (TASK-30): each entry
is a verb that mutates, is permitted in the current state, and has somewhere to
say what it does. A verb the agent cannot reach should not appear on the dial
either, and vice versa. That is the check that keeps them one system.
This also settles what happens on a refused entry: the dial shows the refusal's
code as its disabled reason (refused_by_state → "not while locked"),
because the surface already had to say so.
Priority order — the component before the contents
TASK-18 learned this the hard way and wrote it down: "the stevia keyboard shipped glitchy because the surface work was rushed." A dial whose actions are stubs is fine. A janky dial is not.
- The component. Geometry, anchor, open/close animation, thumb reach,
what happens when it opens near a screen edge, how it dismisses. Built from
Souveraine-owned AGPL primitives — this is a first user of the primitive set
souveraine-lock-primitives(archive/from-substrate) specifies, and a good reason to build them, since a dial needs shapes and ripples that ii's widgets would otherwise supply. - Invocation. Squeeze is the natural one (TASK-13) and is not the only one — long-press and an edge gesture want to reach it too. Every path registers through the gesture/keyboard routing system, never a hardcoded binding (INTERFACE-ARCHITECTURE §4).
- Contents. From the verb tables, per above.
Constraints inherited, not invented
- Layer and state. The dial declares its layer and lets shell state gate
it (
FIRST-CLASS-APPS§4). It must not paint over a locked screen, fight the OSK, or survive an overview. - Lock and tier. Ambient entries may appear on the lock surface; personal
ones and any mutation gate on
screenLockSecureplus a fresh grant (FIRST-CLASS-APPS§5). A dial is exactly the shape of thing that leaks by being convenient. - Update management. It ships as part of the shell surfaces, which are
not packaged today — they live in
$HOMEand arrive by rsync. TASK-25 names this; the dial does not get an exception, and "how does this reach a device" is part of its acceptance, not an afterthought.
Acceptance
- Opens under the thumb from any app, at any screen edge, without clipping.
- Dismisses by the same gesture, by tapping away, and by pressing back.
- Entries are derived from a surface's verb table, not a list in the QML.
- A disabled entry says why, in the words the refusal already used.
- Does not appear over a locked screen with anything personal on it.
- Reaches a device by a mechanism that survives the next update.
Visual references (recon 2026-08-11)
ay-hooshan/circular-menu— a ring of Qt Quick Shapes wedges with a center cutout, elastic open animation, per-wedge icon color and click. No screenshots upstream — unproven visuals, treat as geometry reference only, not a look to copy. ~664 lines.nextsigner/timelineview— Qt 5.14 "Zooland": concentric-circle chart rendering (bodies/houses/signs/aspect arcs) plus aZmMoveTimemotion dial. The concentric ring composition and arc math are the sample; the astrology content is not.
Connects to
TASK-13 (squeeze — the first invocation), TASK-30 (the vocabulary),
TASK-18 (selection menu — the other summoned surface; they should not become
two frameworks), FIRST-CLASS-APPS.md, INTERFACE-ARCHITECTURE.md §4,
souveraine-lock-primitives.md (the owned widget set this needs).