Build and test / Desktop (Linux) (push) Failing after 1h14m38s
Build and test / Layer separation (push) Successful in 48s
🐳 Android image / Build and push (push) Successful in 16m30s
Build and test / android-image (push) Successful in 16m31s
Traceability / Requirement traces (push) Successful in 1m47s
Build and test / Android (aarch64) (push) Successful in 1h0m21s
Crop, Local and Repair were chips at the head of the develop column, sharing a row with the adjustment groups and told apart from them by the shape of their highlight. Three things followed from that, and only the last is cosmetic: the column closes, so the way out of a mode went away with the way in — hence the duplicate "Done Cropping" over the canvas; the chips are generated from the operation set, so the widest thing in the sidebar was a row nobody had chosen the contents of; and a mode and a filter are different kinds of state wearing one control. They are a fixed 60px rail down the left now, generated from a single table in toolrail.slint. A tool is one row of it plus a drawing plus a ViewMode variant; nothing in app.slint is touched to add one. What is left of the strip is the group filters, so it is GroupStrip. The column stops measuring itself. Every panel published a content-width and declared it as min-width, and the column took the largest — which spent the photograph's pixels on whatever happened to be widest, and moved the image sideways when switching tools swapped one set of panels for another. It is panel-width now, one number in style.yaml. That number is 360 and it is measured, not picked: the contents report a minimum of 344 in every mode, and they do not compress below it because a Text that does not elide reports the same minimum as preferred. 320 was tried and sliced Paste down the middle. The Flickable's viewport is floored at the layout's minimum rather than its preferred width for the same reason — content that is never told how much room it has cannot adapt to having less. Removing the eight content-width declarations repairs three comments an earlier edit had spliced sentences into. The raw histogram's note on keeping its hint short is rewritten rather than dropped: an over-long hint no longer widens the column, it pushes the column's minimum past the width it has and clips the panel, which makes that constraint sharper rather than obsolete. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
247 lines
9.8 KiB
YAML
247 lines
9.8 KiB
YAML
# The source of truth for the UI's colour and length tokens.
|
||
#
|
||
# `build.rs` reads this file and generates `theme.slint` into OUT_DIR at
|
||
# compile time; nothing here is read at runtime unless the `live-style`
|
||
# feature is on. Edit this file, never the generated one.
|
||
#
|
||
# Leaf values only. A token that binds several values into one concept — a
|
||
# panel heading's colour *and* size *and* weight — is a Slint component, not
|
||
# a row in a YAML file (see widgets.slint).
|
||
#
|
||
# Structure:
|
||
# preamble — prose emitted at the top of the generated file
|
||
# colors: — name -> "#RRGGBB", or `alias:` naming another colour token
|
||
# lengths: — name -> pixels
|
||
# Any token may carry `note:` (a `//` comment above it) or `doc:` (a `///`
|
||
# doc comment, which Slint surfaces to editors).
|
||
#
|
||
# Two entries are dividers rather than tokens, because YAML discards the blank
|
||
# lines between map entries and the generated file's grouping has to survive:
|
||
# `section: <title>` — a headed run, optionally with its own `note:`
|
||
# `break: true` — a bare blank line between related tokens
|
||
|
||
preamble: |
|
||
Near-neutral dark palette, achromatic signalling.
|
||
|
||
Two commitments, both about not lying to the photographer.
|
||
|
||
**Dark ground.** A light UI surrounding an image biases how that image is
|
||
judged — the eye adapts to the brightest thing in view, and a white panel
|
||
makes a correctly-exposed photograph look dark.
|
||
|
||
**Near-neutral greys.** The earlier palette was a warm "darkroom safelight"
|
||
brown (ground #14120F, R twelve points above B). That is worse than it
|
||
sounds: simultaneous contrast pushes perception of the image *away* from the
|
||
surround, so a warm chrome makes a neutral photograph read cool, and the
|
||
photographer corrects toward warm to compensate. Every export drifts yellow.
|
||
It is the same reason a print viewing booth is neutral grey rather than
|
||
whatever colour the room happens to be.
|
||
|
||
These greys carry a 2–3 point blue lift rather than being flatly achromatic.
|
||
Pure R=G=B reads as dead to most eyes; a trace of cool reads as instrument
|
||
rather than absence, and biases far less than warmth because the eye is more
|
||
tolerant of a cool surround. The lift is small enough not to matter
|
||
perceptually and deliberate enough not to be mistaken for drift.
|
||
|
||
**No hue in the chrome.** Active and modified states are signalled by
|
||
brightness alone. An accent sitting beside the image competes with it for
|
||
attention and shifts the perception of nearby colours; a photo editor cannot
|
||
afford either. The one exception is `warn-ink` — a caution is genuinely a
|
||
different kind of thing from an active state, and hue is the fastest way to
|
||
say so.
|
||
|
||
colors:
|
||
ground: "#121314"
|
||
surface: "#1B1C1E"
|
||
surface-raised: "#252629"
|
||
rule: "#323438"
|
||
|
||
_inks: { break: true }
|
||
ink: "#EDEEF0"
|
||
ink-dim: "#9EA1A6"
|
||
ink-faint: "#71747A"
|
||
|
||
hover:
|
||
value: "#2E3034"
|
||
note: |
|
||
Interactive states for surfaces. Named rather than written inline so a
|
||
button, a section header and a list row cannot drift apart: hover lifts
|
||
toward the light, press sinks back past the resting surface so the
|
||
control reads as depressed rather than merely lit.
|
||
pressed: "#17181A"
|
||
|
||
_signalling:
|
||
section: signalling
|
||
note: |
|
||
Achromatic, so the separation has to come from luminance. These are
|
||
spaced further apart than a coloured palette would need: with hue
|
||
unavailable, a two-step brightness difference is invisible, and a
|
||
modified marker that cannot be spotted at a glance is not a marker.
|
||
|
||
active:
|
||
value: "#FFFFFF"
|
||
doc: |
|
||
An active or engaged control: a slider fill, a curve line, a checked
|
||
box. Brighter than `ink` so it reads as lit rather than merely present.
|
||
active-dim:
|
||
value: "#C6C9CE"
|
||
doc: Active, at rest — a filled control that is not under the pointer.
|
||
active-pressed:
|
||
value: "#8E9298"
|
||
doc: Active, pressed. Sinks rather than lifts, matching `pressed`.
|
||
|
||
modified:
|
||
alias: active
|
||
note: |
|
||
"This differs from its default." Deliberately the brightest thing in the
|
||
chrome: it is the one piece of state the photographer scans for, and
|
||
with no hue to carry it, brightness is all there is.
|
||
|
||
selected:
|
||
value: "#383B40"
|
||
doc: |
|
||
A selected grid cell. A lifted neutral rather than a tint — distinct
|
||
from `hover` because a multi-selection must stay legible after the
|
||
pointer has moved on, which is the whole point of selecting several
|
||
before dragging them.
|
||
selected-ring:
|
||
value: "#D5D8DD"
|
||
doc: |
|
||
The ring around a selected cell. Brighter than the fill so selection
|
||
survives against a pale thumbnail, where the fill alone would vanish.
|
||
|
||
warn-ink:
|
||
value: "#C9A05A"
|
||
note: |
|
||
Semantic, and the only hue in the palette. A caution is not an active
|
||
state, and it is worth the one exception to say that instantly. Muted
|
||
rather than saturated so it does not shift perception of a nearby image.
|
||
|
||
_plot:
|
||
section: plot series
|
||
note: |
|
||
The histogram's four traces (FR-DSP-7). Hue here is the same exception the
|
||
swatch takes and not a second one: a per-channel histogram has to say
|
||
*which channel*, and no achromatic treatment can distinguish red from blue
|
||
— so the colour is data, exactly as the image beside it is.
|
||
|
||
Held well back from full strength, and darker than `ink`, for the reason
|
||
the theme preamble gives: three saturated traces sitting a few centimetres
|
||
from the photograph would compete with it and shift how its colours read.
|
||
These are legible against `ground` at a glance and no louder than that.
|
||
|
||
plot-red: "#C4626A"
|
||
plot-green: "#6BA867"
|
||
plot-blue: "#5F8CCB"
|
||
|
||
plot-luma:
|
||
value: "#4E5257"
|
||
doc: |
|
||
The luminance trace, drawn filled and behind the three channels. Neutral
|
||
because luminance is not a channel — it is the axis exposure is read off,
|
||
and a hue would imply it were one series among four rather than the one
|
||
the other three are decomposing.
|
||
|
||
lengths:
|
||
_gaps: { break: true }
|
||
gap-sm: 6
|
||
gap: 12
|
||
gap-lg: 20
|
||
|
||
_text: { break: true }
|
||
text-sm: 11
|
||
text: 13
|
||
text-lg: 17
|
||
text-xl: 24
|
||
|
||
radius-sm:
|
||
value: 3
|
||
note: |
|
||
Corner radii. Two steps only: `radius-sm` for things that sit inside
|
||
other things, `radius` for the controls themselves.
|
||
radius: 4
|
||
|
||
touch-target:
|
||
value: 44
|
||
note: "FR-UI-3: minimum 44pt hit target under touch."
|
||
|
||
row-height:
|
||
value: 26
|
||
note: |
|
||
One row of the collections tree, and one level of nesting. Both are
|
||
tokens because a tree's indentation has to stay proportional to its row
|
||
height; hard-coding either makes the hierarchy read wrong when the other
|
||
changes.
|
||
indent: 14
|
||
|
||
control-height:
|
||
value: 28
|
||
note: |
|
||
The drawn height of a button or section header. Deliberately shorter
|
||
than `touch-target` — chrome this tall in every row would crowd the
|
||
photograph — so controls grow their TouchArea past their own bounds to
|
||
meet FR-UI-3 rather than growing their ink.
|
||
control-min-width:
|
||
value: 88
|
||
doc: |
|
||
Floor on button width, so a one-word label is still a comfortable
|
||
target and a row of buttons has an even rhythm.
|
||
|
||
_develop:
|
||
section: the develop view's two fixed columns
|
||
note: |
|
||
Both are *mandated* sizes rather than measured ones, which is the
|
||
opposite of how the rest of this interface is laid out, and the reason
|
||
is that these two flank the photograph. Everything else may grow to fit
|
||
its contents; a column beside the image cannot, because the pixels it
|
||
takes come out of the picture and a control gaining a word is not a
|
||
reason to give the photograph less room.
|
||
|
||
What that costs is that a panel wider than the number below clips, and
|
||
the Flickables inside it are what make the overflow reachable. That is
|
||
the trade being made deliberately: a control you may have to scroll to
|
||
is worse than one you can see, and a photograph that changes size when
|
||
you switch tools is worse than both.
|
||
|
||
rail-width:
|
||
value: 60
|
||
doc: |
|
||
The tool rail down the left of the develop view. Wide enough for a 20px
|
||
icon over a `text-sm` label at the longest name in the table, and narrow
|
||
enough to stay a rail rather than becoming a third panel.
|
||
rail-entry-height:
|
||
value: 54
|
||
doc: |
|
||
One tool in that rail. Over `touch-target`, because unlike the controls
|
||
inside a panel these are not crowding anything — the rail holds four
|
||
entries on a screen with room for a dozen.
|
||
|
||
panel-width:
|
||
value: 360
|
||
doc: |
|
||
The develop column on the other side. One number for tablet and desktop
|
||
alike, replacing the 280 the first was drawn for and the 380 the second
|
||
was — the alternative is a column that changes width with the window,
|
||
which is a photograph that changes size when you resize by a pixel.
|
||
|
||
**Measured, not chosen.** The column's contents report a minimum width of
|
||
344 in every mode — photo, local and repair alike — and below that they
|
||
do not compress, they clip: a `Text` that does not elide reports the same
|
||
minimum as preferred, and most of this column is text. 320 was tried
|
||
first and sliced "Paste" down the middle. So this is that floor plus
|
||
enough not to be sitting on it.
|
||
|
||
To re-measure after changing a panel, bind a `Text` in `app.slint` to
|
||
`column.min-width` and read it off the running app; there is no way to
|
||
get the number out of the layout engine short of asking it.
|
||
|
||
|
||
swatch:
|
||
value: 12
|
||
note: |
|
||
The colour square identifying which hue band a row edits. Small on
|
||
purpose: a swatch is data, not chrome — the one sanctioned exception to
|
||
an achromatic palette — and it earns that exception by identifying the
|
||
row without competing with the photograph beside it. Big enough to name
|
||
a hue at a glance, and no bigger.
|