Show the colour mixer as three runs of twelve, each row a colour
Build and test / Desktop (Linux) (push) Successful in 17m20s
Build and test / Layer separation (push) Successful in 33s
🐳 Android image / Build and push (push) Successful in 2s
Build and test / android-image (push) Successful in 2s
Traceability / Requirement traces (push) Failing after 26s
Build and test / Android (aarch64) (push) Failing after 8m59s

The mixer was thirty-six sliders reading "Hue / Sat / Lum" twelve times
over with nothing saying which band any row belonged to. The identity was
there all along — the descriptor declares param.mixer.orange.sat and BANDS
carries orange at 30° — and was discarded on the way out: labels.rs had no
mixer entries, so every key fell through to a derived label that yields the
bare channel name.

A parameter can now say which aspect it adjusts and which subject it
adjusts it on, with the subject's hue where the subject is a colour
(descriptor::Facet). That is data about what the operation does, not a
layout: the mixer genuinely weights pixels around 30°. What to draw from
30°, and in what order to stack the runs, stay in dr-ui (ARCH §4.3a) —
develop.rs brings rows sharing an aspect together and marks the first of
each, and adjust.slint names the run once and draws a swatch, a track and a
readout on one line.

Grouped by channel rather than by band because an edit is almost never
"everything about orange"; it is the saturation of the greens, made by
comparing one channel across neighbouring bands. Twelve band sections put
those twelve rows in twelve different places.

The swatch is the label, which is what makes twelve rows fit where four
did. The band name is not lost: it is the row's accessible label, so the
control is not colour-only, and labels.rs is where the mapping is written
down — including chartreuse as "Yellow-Green" and spring as "Blue-Green",
since nobody hunting foliage scans a list for "Spring".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 16:14:13 +02:00
co-authored by Claude Opus 5
parent ab4a7e00e7
commit 9b2ee0d0eb
10 changed files with 653 additions and 44 deletions
+50 -12
View File
@@ -170,7 +170,7 @@ thirty-six anonymous sliders reading `Hue 0 / Sat 16 / Lum 0` twelve times
over, with nothing saying which band any row belongs to. Three separate
defects meet here.
### M1 — Band identity is lost (a bug, not a style issue)
### M1 — Band identity is lost (a bug, not a style issue) — **done**
`labels.rs` has no `param.mixer.*` entries, so all thirty-six keys fall
through to a default that yields the bare channel name. The core is not at
@@ -181,11 +181,30 @@ discarded at resolution.
**Deliverable.** Resolve mixer keys to their band. A row reads `Orange · Sat`,
or `Sat` under a band heading — M3 decides which.
### M2 — Bands carry their centre hue as capability data
**Landed** as a `band.*` catalogue resolving the *subject* of a faceted
parameter (M2), rather than as entries for the thirty-six `param.mixer.*`
keys. Three bands are catalogued to something other than their key: chartreuse
reads "Yellow-Green" and spring "Blue-Green", because a photographer looking
for foliage does not scan a list for "Spring".
### M2 — Bands carry their centre hue as capability data — **done**
**Deliverable.** `ParamDescriptor` gains an optional band hue in degrees, set
by `band_params!` from `BANDS`. The UI converts degrees to a swatch.
**Landed** as `descriptor::Facet` — a little wider than "a band hue", and the
width is what M3 turned out to need. A parameter may say which **aspect** it
adjusts (the channel) and which **subject** it adjusts it on (the band), with
the subject's hue attached where the subject is a colour. `ParamDescriptor` is
otherwise unchanged and `faceted()` is a const builder step, so the thirty-six
descriptors stay `static` and every other operation says nothing at all.
The hue reaches the screen as `Swatch` in `widgets.slint`, which owns the
saturation and brightness. Those two are *not* in `style.yaml`: it holds
colours and lengths, and a third section for two floats used in one component
buys less than it costs. `Theme.swatch` — the square's size — is a length and
does live there.
**Why this is not a §4.3a violation.** A band's centre hue is a *fact about
the operation* — the mixer genuinely acts on the 30° band, and that number is
what it acts on. The core says "this parameter belongs to the band centred at
@@ -202,15 +221,33 @@ exactly as an image is data. That is categorically different from an accent
decorating a heading, which is what the palette rule forbids. Keep them small
and let them identify, never dominate.
### M3 — Structure: twelve collapsible bands
### M3 — Structure: three runs of twelve, not twelve of three — **done**
**Deliverable.** The mixer renders as twelve `Section`s — one per band, titled
by band name, carrying its swatch — each holding Hue, Sat and Lum. Collapsed
by default; the modified dot (Workstream C) shows which bands hold an edit
without expanding them.
~~The mixer renders as twelve `Section`s — one per band, titled by band name,
carrying its swatch — each holding Hue, Sat and Lum. Collapsed by default.~~
This needs no new `WidgetKind`: it is Workstream C's sections applied to a
grouping the frontend derives from parameter ids. Depends on C.
**Superseded, twice over.** The lids came off the develop column entirely
(Workstream C's sections are now plain headings), so "collapsed by default"
had nothing left to mean. And the grouping was the wrong way round: an edit is
almost never "everything about orange", it is "the saturation of the greens",
made by comparing one channel across neighbouring bands. Twelve band sections
put the twelve rows you want to compare in twelve different places.
**Landed.** Three runs — Hue, Saturation, Luminance — of twelve rows each,
under the operation's own heading. `develop.rs` stacks the rows by aspect
(`presentation_order`) and marks the first of each run; `adjust.slint` names
the run once and draws the rest. Each row is a swatch, a track and a readout on
one line: the swatch *is* the label, which is what makes twelve rows fit where
four did, and the band name lives on as the row's accessible label so the
control is not colour-only.
The mixer's declaration order is untouched — it declares band by band, which
is the order the shader wants. Rearranging it for the panel would have been
the core laying out a screen (§4.3a); doing it in `develop.rs` is the same
frontend-side derivation that decides there are groups at all.
Nothing here is mixer-specific: any operation whose parameters carry facets
groups this way, and one that carries none is untouched.
### M4 — A single-parameter operation should not cost a heading
@@ -222,9 +259,10 @@ parameters number one wants to *be* a named row, not a group containing one.
row labelled by the operation. Derived frontend-side from the parameter
count — the core says nothing about it, per §4.3a.
**Done when.** Every mixer row says which band it edits; bands collapse with
modified state visible while collapsed; Vibrance and Saturation are one row
each; and no hex colour appears in any descriptor.
**Done when.** Every mixer row says which band it edits; the rows for one
channel read as a run rather than as twelve unrelated sliders; Vibrance and
Saturation are one row each; and no hex colour appears in any descriptor.
**All four are met.**
---