Derive the interface palette instead of hardcoding eleven colours

main.rs held eleven `const … : u32` colours, and spikes/floem-shell held a
byte-identical copy of the same eleven. Every one was a judgement call made once,
and no user could change any of them without recompiling.

lumbridge-theme takes a syntax theme's five anchors — background, foreground,
comment, and the git added/deleted/modified colours where the theme has them —
and derives the whole role set. The frame is the editor background pushed one
logarithmic contrast step away from the content, so the work surface is the
brightest thing on screen; a theme already at black lifts its surface instead of
sinking its frame, which is why a pitch-black theme still shows a seam.

Adapted from Buzz's adaptive-theme.ts (block/buzz, Apache-2.0) as a
specification, not as copied code. The golden vectors were taken by running the
original under Node — a research pass had supplied Python-derived vectors and
claimed they reproduced it byte-exactly, and they did not: Python rounds
half-to-even, JavaScript rounds half-up, they disagree on exactly one channel
value of 22.5, and that decides whether the luminance bisection converges a step
early. github-dark's chrome is #171a1d, not #191c20.

Provenance colours are separate roles from state colours, with a test holding
them pairwise distinct in every theme, because decision 0012 colours a usage
reading by where its number came from and never by how alarming it is.

This changed no pixels, and that was verified rather than asserted: the only
difference between before-and-after screenshots is the digits of a process ID.
The check earned its keep — the mechanical rename had rewritten three user-facing
strings, turning the sidebar's "ATTENTION · 0" into "theme.attention · 0" and
"+ ADD PANEL" into "+ ADD theme.surface". A literal-by-literal diff now confirms
zero strings changed.

The default theme pins its roles to the previous constants to make that true;
the anchors underneath are real, and a test bounds how far the pure derivation
sits from them. The terminal ANSI palette keeps its own table, so 29 colour
literals remain in main.rs, all terminal. The catalog, its attribution, and the
picker are separate work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Metal Agent
2026-08-31 23:23:30 -07:00
co-authored by Claude Opus 5
parent 2e349282c8
commit 1556b87f37
13 changed files with 1585 additions and 275 deletions
+19 -1
View File
@@ -27,6 +27,7 @@ messages should be real from the beginning.
- `lumbridge-runtime`: session ownership, supervision, recovery, IPC server.
- `lumbridge-pty`: portable PTY and process-tree adapters.
- `lumbridge-terminal`: VT parsing, scrollback, selection, search, render model.
- `lumbridge-theme`: derives the interface palette from a syntax theme's anchors.
- `lumbridge-acp`: ACP client, capability negotiation, transcript normalization.
- `lumbridge-harness`: manifests, launch profiles, hooks, PTY fallback adapters,
and documented status/usage probes.
@@ -43,7 +44,7 @@ messages should be real from the beginning.
The scaffold currently contains `lumbridge-core`, `lumbridge-storage`,
`lumbridge-buzz`, `lumbridge-pty`, `lumbridge-runtime`, `lumbridge-terminal`,
`lumbridge-harness`, `lumbridge-ui-fixture`, and the `lumbridge` application
`lumbridge-harness`, `lumbridge-theme`, `lumbridge-ui-fixture`, and the `lumbridge` application
itself. The GPUI shell was a spike in an excluded workspace until it graduated
into `apps/lumbridge`; `scripts/ci.sh` now takes `--headless` and `--ui` so the
non-UI crates still build in seconds, and `cargo deny check licenses` gates the
@@ -207,6 +208,23 @@ typed command plans. Suggestions have no authority. Plan execution is routed
through the same command plane as human actions, and trace export to a hosted
model requires explicit scope and destination consent. See decision 0007.
## Theme model
Every colour in the interface is a named semantic role derived from a syntax
theme's five anchors, not a constant. `lumbridge-theme` holds the derivation and
no framework types, so the arithmetic is tested headless; the shell converts a
derived palette into GPUI colours once per theme change and owns the result.
The frame is the editor background pushed one logarithmic contrast step away
from the content, so the work surface is the brightest thing on screen; a theme
already at black lifts its surface instead. Provenance colours are separate
roles from state colours, with a test holding them pairwise distinct, because
decision 0012 colours a usage reading by its source and never by its value.
The default theme pins its roles to the eleven constants that preceded it, so
introducing the engine changed no pixels. The terminal ANSI palette and the
theme catalog are still fixed tables. See decision 0018.
## Usage model
Usage is an append-only observation stream, not a mutable percentage field.