a19d645a49
Corrects theme.h's large-text thresholds, names accent/secondary's real binding limiter, and adds compositeOver so the loop-span fill's 2.25:1 under-floor pair is asserted rather than assumed.
764 lines
53 KiB
Markdown
764 lines
53 KiB
Markdown
# Visual design language — ReaSampler + ReaSampler 9000 (Phase L)
|
||
|
||
System-wide look-and-feel framing. This is the **why** and the visual spec for the
|
||
whole-system look-and-feel effort, now carried as its own **Phase L** (Look-and-feel):
|
||
the shared LICE drawing kit (**L1**), the extension's docked bank-panel layout redesign
|
||
(**L2**), and the ReaSampler 9000 VST editor + embed-strip restyle (**L3**). It was
|
||
written after Daniel DAW-tested the S1–S6 instrument:
|
||
|
||
> "this shit looks like temple os. it's 2026, we need something more modern and sleek.
|
||
> speed is the selling point. […] If we are going to enhance look and feel, we need to
|
||
> think big and full. Scope it out for the whole system. […] the VST is dogshit. Does
|
||
> Cockos have a toolkit for this type of visual design?"
|
||
|
||
**Status:** framed by product-designer (2026-07-26); the DS-1/DS-2/DS-3 forks are
|
||
**SETTLED (Daniel, 2026-07-26)** — recorded in §6 with the reasoning preserved. **DS-2 was
|
||
revised 2026-07-26 (Daniel)** from the single electric-cyan accent to a **three-accent
|
||
pastel system** (primary/secondary/tertiary = pastel lime green / pastel teal / pastel
|
||
purple) — see §6. (A stylish-font upgrade was considered and **declined** by Daniel — no
|
||
font bundling/redistribution; the kit keeps its current cached-font face.) The build points it
|
||
fed have landed; their history is in **`docs/ARCHIVE.md`** ("Phase L — Look-and-feel", **L1** the
|
||
shared LICE drawing kit, **L2** the dock-panel layout redesign, **L3** the VST editor +
|
||
embed-strip restyle) and the architecture now lives in **`src/core/ui/CLAUDE.md`** /
|
||
**`src/shell/panel/CLAUDE.md`** (the design-system implementation). Toolkit facts below are
|
||
**verified against the vendored `vendor/WDL` tree**, not lore.
|
||
|
||
> **Phase-boundary note (2026-07-26).** This look-and-feel effort was originally drafted
|
||
> as Phase S points **S0-DS** (the shared kit) and **S14** (the panel refresh). It was
|
||
> **lifted out of Phase S into its own Phase L** so a parallel team can take it up
|
||
> without Phase S context and so Phase S feature work (S7–S13) proceeds ungated. The old
|
||
> `S0-DS` content is now **L1**; the old `S14` content is now **L2** (expanded from a
|
||
> light re-skin into a thorough layout redesign — see DS-3 below); the VST editor +
|
||
> embed-strip restyle, which S10–S13 originally folded in, is now the explicit **L3**
|
||
> point, **gated on Phase S landing on dev**. Phase S's S10–S13 build their interaction
|
||
> UX with the current drawing and **adopt the L1 kit when it lands** — they are not
|
||
> gated on Phase L.
|
||
|
||
---
|
||
|
||
## 0. TL;DR
|
||
|
||
- **Cockos ships exactly the toolkit Daniel asked about**, and it's already vendored.
|
||
Two layers: **LICE** (the immediate-mode 2D rasterizer — gradients, anti-aliased
|
||
lines/circles/round-rects/beziers, alpha blending, and a real font engine with
|
||
shadow/outline/glow effects) and **WDL VirtualWnd / vwnd** (a retained-mode LICE-drawn
|
||
*widget* layer — buttons, sliders, knobs, listboxes, skins — the thing REAPER and SWS
|
||
themselves are built on). **The current VST editor uses almost none of it** — it draws
|
||
flat `LICE_FillRect` blocks and pipes text through raw GDI `DrawTextA`. *That* is the
|
||
"temple os" look: no gradients, no anti-aliasing, no rounded corners, no hover, aliased
|
||
system text. The ceiling is not the problem; we're drawing at the floor.
|
||
- **DS-1 SETTLED (2026-07-26): LICE, and anything useful in WDL is free game.** "Don't
|
||
reinvent the wheel" — if vwnd (or any WDL machinery: skin/image helpers, draw idioms)
|
||
offers a usable piece, use it; otherwise build on LICE. **No external frameworks**
|
||
(not JUCE, not iPlug2, not VSTGUI). LICE can deliver a 2026-modern dark synth UI —
|
||
REAPER's own themed UI and SWS prove the ceiling. This is fully consistent with the
|
||
settled **D-A** decision (bare SDK + LICE, no framework); the fork is recorded in §6.
|
||
- **The real work is L1 — a shared drawing kit** — one small LICE-based
|
||
component/palette/type layer that the VST editor, the embed strip, and the bank panel
|
||
all draw through. It is the one source of drawing for the whole system.
|
||
- **DS-2 SETTLED (2026-07-26), REVISED (2026-07-26): Direction B ("Neon Console") + Direction
|
||
C's spectral keyboard strip** as the signature surface — now with a **three-accent pastel
|
||
system** (primary = pastel lime green, secondary = pastel teal, tertiary = pastel purple)
|
||
in place of the original single electric-cyan accent. (A stylish-font upgrade was
|
||
considered and **declined** — no font bundling; the kit keeps its current face.) Three
|
||
visual directions are in §4; B+spectral with the pastel accents is the settled target.
|
||
"Speed is the selling point" is a **design constraint**, not a tagline: the UI must *feel*
|
||
instant (§3.5).
|
||
- **DS-3 SETTLED (2026-07-26): the dock-panel work is a real, thorough layout design, not
|
||
a light reskin.** L2 lays out the *full button inventory* (including M11's additions)
|
||
intuitively, uncluttered, and useful — §5.2.
|
||
|
||
---
|
||
|
||
## 1. The toolkit question, answered from the vendored source
|
||
|
||
Daniel: *"Does Cockos have a toolkit for this type of visual design?"* Yes — two layers,
|
||
both already in `vendor/WDL`. Verified by reading the headers, not from memory.
|
||
|
||
### 1.1 LICE — the rasterizer (this is the load-bearing answer)
|
||
|
||
`vendor/WDL/WDL/lice/` is Cockos's own 2D graphics library — the same one REAPER's UI,
|
||
the theme engine, and SWS draw with. The current `bank_panel` and VST editor already link
|
||
it. What it actually offers, beyond the flat `LICE_FillRect` the editor currently limits
|
||
itself to (all verified in `lice/lice.h` / `lice/lice_text.h`):
|
||
|
||
| Capability | Function (verified) | We use it today? |
|
||
|---|---|---|
|
||
| **Gradient fills** | `LICE_GradRect` (per-axis R/G/B/A ramps) | **No** — this alone kills the flat look |
|
||
| **Anti-aliased lines** | `LICE_Line`/`LICE_FLine`/`LICE_ThickFLine` (`aa=true` default) | No |
|
||
| **Rounded rectangles** | `LICE_RoundRect` (corner radius, AA) | No |
|
||
| **AA circles / knobs** | `LICE_Circle`/`LICE_FillCircle` | No |
|
||
| **Bezier curves** | `LICE_DrawCBezier` (AA envelope/curve draw) | No |
|
||
| **Triangles / polygons** | `LICE_FillTriangle`/`FillConvexPolygon`/`FillTrapezoid` | No |
|
||
| **Alpha compositing** | every primitive takes `alpha` + blend `mode` | Barely (opaque only) |
|
||
| **Real font engine** | `LICE_CachedFont` / `LICE_IFont`: `SetFromHFont`, AA glyph cache, `SetTextColor`, `DrawText` | **No — we use raw GDI `DrawTextA`** |
|
||
| **Text effects** | shadow / outline / glow / blur (`LICE_FONT_FLAG_FX_SHADOW`/`_OUTLINE`/`_BLUR`) | No |
|
||
|
||
**The single biggest visual lever is text.** The editor's `drawText` does
|
||
`GetDC` → `SetTextColor` → `DrawTextA` — native, un-anti-aliased-in-the-bitmap GDI text
|
||
that reads as "1998." Switching to a `LICE_CachedFont` (created once from a chosen
|
||
`HFONT`, cached, AA glyphs, optional soft shadow) is the difference between "temple os"
|
||
and "modern synth." Small, mechanical change, outsized payoff, and it belongs in L1 (§5)
|
||
so every surface gets it at once. `bank_panel` already routes some text through GDI too;
|
||
the kit fixes both.
|
||
|
||
**Conclusion:** LICE is a genuine, sufficient 2D toolkit for a modern dark synth UI. The
|
||
gap between the current editor and a sleek one is entirely in *which LICE calls we make*,
|
||
not in LICE's ceiling. **No new dependency is required to get the look Daniel wants.**
|
||
|
||
### 1.2 WDL VirtualWnd (vwnd) — the retained-mode widget layer, and DS-1's "free game" rule
|
||
|
||
`vendor/WDL/WDL/wingui/virtwnd*.{h,cpp}` is the layer above LICE: `WDL_VWnd` (a virtual
|
||
window / control that draws itself into a shared LICE bitmap), with concrete controls —
|
||
`virtwnd-iconbutton.cpp` (buttons), `virtwnd-slider.cpp` (sliders +
|
||
`vwnd_slider_drawknobstack` knob rendering), `virtwnd-listbox.cpp` (scrolling lists),
|
||
`virtwnd-skin.h` (image-based skins), plus `WDL_STYLE_*` hooks for background gradients
|
||
and slider/knob bitmaps (`virtwnd-controls.h`). **This is the toolkit REAPER and SWS
|
||
themselves use for custom UI.**
|
||
|
||
**DS-1's settled rule: WDL is free game — don't reinvent the wheel.** Where a WDL piece
|
||
(a skin/image helper, a draw idiom, a specific control's rendering) is genuinely usable
|
||
and cheaper than re-deriving it, **use it**; otherwise build on LICE directly. The one
|
||
standing caution — not a prohibition — is the **retained-mode object model**: vwnd's
|
||
controls own their hit-test/drag/value internally, whereas the house discipline keeps
|
||
layout/hit-test in **pure geometry modules** (`mode_switch`, `embed_strip`,
|
||
`editor_geometry`, the planned `keyboard_strip`) so hit-test math stays CTest-covered
|
||
outside the DAW. So the default is to **borrow vwnd's drawing craft** (read
|
||
`virtwnd-slider.cpp` / `vwnd_slider_drawknobstack` as the reference for a good LICE
|
||
slider/knob and reproduce the look procedurally over pure geometry we own) and to reach
|
||
for a vwnd *object* only where it carries no bespoke-geometry requirement and re-deriving
|
||
it is genuinely wasteful. The plain **scrolling listbox** (`virtwnd-listbox.cpp`) for a
|
||
long sample list is the archetypal "just use it" case — no bespoke drawing, tedious to
|
||
re-derive; adopt it if it fits at build time. The **keyboard strip and waveform** are
|
||
bespoke surfaces vwnd has no widget for — custom-drawn regardless. The rule is a
|
||
build-time judgment call per piece, made under one principle: **reuse what WDL gives us,
|
||
keep hit-test geometry testable.**
|
||
|
||
### 1.3 The frameworks we are NOT taking (and why — DS-1 settled: no external frameworks)
|
||
|
||
- **IPlug (WDL's own `IPlug/`)** — the VST2-era plugin framework in WDL. **Dated and
|
||
irrelevant.** VST2 is deprecated; it predates VST3; it's not a UI toolkit anyway. Ignore.
|
||
- **iPlug2** (the modern community fork — NanoVG/Skia GPU renderers, web-UI option) — a
|
||
genuinely capable modern framework, but it is a **whole new plugin-framework dependency**
|
||
that would **replace** the bare-SDK + LICE architecture **D-A** settled on 2026-07-26. It
|
||
brings its own graphics stack (NanoVG/Skia), its own build integration, and its own
|
||
editor/parameter model — none of which we need, because LICE already clears the bar.
|
||
**Rejected** (DS-1 settled: no external frameworks).
|
||
- **VSTGUI** (bundled with the Steinberg SDK) — the noted D-A fallback rung *if the
|
||
IPlugView↔LICE bridge had proven gnarly*. On the phase-s worktree it didn't — S1 landed
|
||
the bridge, the editor draws in LICE today. VSTGUI is moot; we're past the point it hedged.
|
||
|
||
**Net:** the toolkit question resolves cleanly. Use **LICE** (already in hand, already the
|
||
draw surface) as the toolkit, **reuse WDL/vwnd pieces where useful** (DS-1's "don't
|
||
reinvent the wheel"), and **take no external framework.** Fully consistent with D-A.
|
||
|
||
---
|
||
|
||
## 2. Palette — dark, modern, punchy
|
||
|
||
Daniel's standing taste: **visual punch over conservative contrast** — push vibrancy to
|
||
the WCAG floor for each element class, not to the past-safe gray. The current palette
|
||
(shared between `bank_panel.cpp` and the VST editor) is a muted forest-green-on-charcoal:
|
||
background `RGB(28,28,30)`, waveform/accent `RGB(120,200,160)`, cell `RGB(44,44,48)`. It's
|
||
*coherent* but *timid* — one desaturated accent, flat fills, hairline borders.
|
||
|
||
### 2.1 Structural roles (direction-independent)
|
||
|
||
A modern dark UI is built from **elevation layers**, not borders. Define the palette by
|
||
role, then let the direction set the accent.
|
||
|
||
**Neutral surfaces — REAPER-theme-matching mid-grey (DS-2 revised, 2026-07-26, Daniel).**
|
||
The neutral role ladder is **not near-black** — it sits in **REAPER's own mid-grey theme
|
||
family** so the dock reads as *part of REAPER*, not a black slab bolted on beside it.
|
||
Ground truth from Daniel's theme: REAPER's arrange/track background is a mid grey
|
||
(`~#373737`), window chrome / ruler / transport a touch darker (`~#2b2b2b`), body text a
|
||
light grey (`~#c8c8c8`–white). The DS-2 neutral ladder below matches that family. **The
|
||
elevation-ladder discipline is unchanged:** base < panel < cell, each a few percent
|
||
lighter, with the micro-gradient (§2.2) + 1px inner highlight/shadow still doing the
|
||
elevation work — REAPER differentiates surfaces by subtle steps, not hard borders, which
|
||
is exactly DS-2's "elevation layers, not borders" principle. Only the *values* moved up
|
||
from near-black into the grey range; the *structure* is identical:
|
||
|
||
| Role | Purpose | Current | Direction (DS-2 revised — REPER-grey neutrals) |
|
||
|---|---|---|---|
|
||
| `bg/base` | window canvas | `28,28,30` | **REAPER chrome grey ≈ `43,43,43` (`#2b2b2b`)** + a **subtle top-down `LICE_GradRect`** (base look, not flat) |
|
||
| `bg/panel` | a raised region (list, waveform pane) | — (implicit) | **≈ `51,51,51` (`#333333`)** — one step lighter than base; elevation reads without a border |
|
||
| `bg/cell` | a control/row surface | `44,44,48` | **≈ `58,58,58` (`#3a3a3a`)** (matches REAPER track bg `~#373737`–`#3c3c3c`), with a 1px inner top highlight (the vwnd trick) |
|
||
| `line/hairline` | separators | `60,60,66` | **≈ `74,74,74` (`#4a4a4a`)** — a subtle step above cell; use sparingly, elevation should carry most separation |
|
||
| `text/primary` | labels, values | `210,230,220` | **REAPER body light grey ≈ `220,220,220` (`#dcdcdc`)**, **must clear WCAG AA (4.5:1) on `bg/panel`/`bg/cell`** (clears ~8:1 — comfortable) |
|
||
| `text/dim` | secondary / units | — | a dimmer light-grey step ≈ `~160,160,160` (`#a0a0a0`), **must clear AA 4.5:1 on the greyer surfaces if used for body** (see §2.1 WCAG note — this is the newly tight pair) |
|
||
| `accent/primary` | live / active / selected layer | `120,200,160` | **pastel lime green** (§4) — the one that means "this is live"; where the punch lives |
|
||
| `accent/secondary` | supporting categorical role A | — | **pastel teal** — a second categorical hue (e.g. secondary zones, non-primary selection state) |
|
||
| `accent/tertiary` | supporting categorical role B | — | **pastel purple** — a third categorical hue (e.g. tertiary zones, a distinct affordance class) |
|
||
| `accent/hot` | hover / live / drag feedback | — (absent) | a brighter tint **of the primary accent**; **today there is no hover color at all** |
|
||
| `warn` | clip / destructive (prune, delete) | — | a red/amber reserved *only* for byte-deleting or clip states |
|
||
|
||
**Three-accent role assignment (DS-2 revised).** The accent layer is now *three* pastel
|
||
hues, not one, so the palette can carry categorical meaning without leaning on saturation
|
||
Daniel finds too loud. The coherent assignment:
|
||
- **`accent/primary` = pastel lime green** — the single "this is live/active/selected"
|
||
signal. It does the same job the old single cyan did: it is the eye-magnet, used with
|
||
discipline for the live layer only (selection, active fill, focus-adjacent, the active
|
||
zone's glow). One primary keeps the "accent = live, never chrome" rule intact.
|
||
- **`accent/secondary` = pastel teal** and **`accent/tertiary` = pastel purple** — the
|
||
*categorical* distinctions that a single-accent palette had to fake with brightness. They
|
||
mark **kinds** of thing, not intensity: e.g. non-active zone bands on the keyboard strip,
|
||
a second/third action-cluster identity, or a supporting-state fill. They never out-shout
|
||
primary — primary is always the "what's live now" answer; secondary/tertiary are "what
|
||
category is this." The three read as a family (all pastel, adjacent-ish in the cool→warm
|
||
band), so the UI stays coherent, not carnival.
|
||
|
||
Primary/secondary/tertiary is a **role trio in the same constants block** as the rest —
|
||
adding two accents did not change the "one file, one direction" property (§2.1 opener).
|
||
|
||
**WCAG-floor discipline (the "punch" rule, re-read for pastels):** for each text-on-surface
|
||
pair, pick the value that still clears its contrast floor — AA 4.5:1 for body text, 3:1 for
|
||
large text and UI-state indicators (selection borders, active fills). With the old vivid
|
||
cyan the rule was "push to the floor *from the vibrant side*." **Pastels are the inverse
|
||
approach:** a pastel is intrinsically less saturated and (as a light-ish tint on a dark
|
||
surface) usually *brighter* than a vivid accent, so it clears the *large/state* floor more
|
||
easily than a vivid hue — though on the DS-2-revised **grey** surfaces the cushion is much
|
||
smaller than on near-black (see the grey re-read below). The tight case flips — **text
|
||
drawn on a pastel fill** (dark text on a light pastel, or the pastel as fill behind
|
||
`text/primary`) is where contrast can fail. So the rule becomes:
|
||
**take the most *pastel* (softest, least-saturated) value that still clears the floor,
|
||
approached from the soft side** — do not over-saturate a pastel back toward neon "to be
|
||
safe," and do not wash it out so far it drops a text pair below floor. The implementer runs
|
||
the theme module's WCAG tests and nudges each accent *within the pastel intent* until every
|
||
drawn pair clears. See §4 for concrete starting values and the pairs expected to be tight.
|
||
|
||
**WCAG re-read for the greyer neutrals (DS-2 revised — the load-bearing check).** Moving
|
||
the neutral surfaces *up* from near-black into REAPER's mid-grey makes two pairs *harder*,
|
||
because contrast against a lighter surface is smaller. Both must be re-verified against the
|
||
new grey ladder, not the old near-black:
|
||
- **`text/dim` on `bg/panel`/`bg/cell` — the classic mid-grey-on-mid-grey floor failure.**
|
||
A mid-grey dim text against a mid-grey surface is exactly where AA fails. At the starting
|
||
values a `~#9a9a9a`-`#a0a0a0` dim on `#333333`/`#3a3a3a` lands around **~4:1–4.4:1** — under
|
||
the AA 4.5:1 body floor and only just clearing the 3:1 large floor. **`text/dim` must be
|
||
lifted light enough (toward `~#a8a8a8`+) to clear AA 4.5:1 on the greyest surface it draws
|
||
body text on**, or be confined to large/UI-label use where the 3:1 floor applies. The
|
||
implementer locks the exact value against the test; the intent is "dim, but never below
|
||
the floor it is actually drawn against."
|
||
- **The three pastel accents on the greyer surfaces — the cushion shrank.** Pastels have
|
||
*less* contrast against mid-grey than against near-black. On near-black the three cleared
|
||
the 3:1 large/indicator floor by a huge margin (~15:1); on `bg/cell` `#3a3a3a` the same
|
||
pastels land around **~6:1–7:1** — still clearing 3:1, but the comfortable cushion is
|
||
gone. Every pastel used as a **state indicator / active fill** (selection border, active
|
||
zone, focus-adjacent) **must be re-verified to still clear the 3:1 large/indicator floor
|
||
on `bg/cell`, not on the old near-black.** If any pastel would drop below floor on grey,
|
||
the "punch from the soft side" rule now means **nudge that hue slightly deeper / more
|
||
saturated until it clears** — the hues stay pastel lime / teal / purple; only their
|
||
value/chroma may move a little. This is a real tension: the greyer background pulls the
|
||
pastels *toward* the floor at the same time the "keep it soft" rule pulls them away from
|
||
saturation. The implementer resolves it per-hue against the test, staying within the
|
||
pastel intent.
|
||
|
||
### 2.2 Motion of color, not just color
|
||
|
||
The flat look isn't only the hue — it's that every fill is a **single opaque rectangle**.
|
||
A 2026 dark UI reads as dimensional because surfaces have a **micro-gradient** (a few
|
||
percent lighter at the top, `LICE_GradRect`) and controls have a **1px top inner-highlight
|
||
+ 1px bottom inner-shadow**. This is the single cheapest "modern" upgrade after AA text,
|
||
and it's exactly what `virtwnd`'s button/slider drawing does. Bake it into the kit's
|
||
`fillSurface()` primitive so every button, row, and cell gets it for free.
|
||
|
||
---
|
||
|
||
## 3. Typography, spacing, interaction, motion
|
||
|
||
### 3.1 Typography within LICE's reality
|
||
|
||
- **Use `LICE_CachedFont`, retire raw GDI `DrawTextA`.** Create a small set of cached
|
||
fonts once (below), owned by the kit, reused across VST + embed + panel. AA glyphs in
|
||
the bitmap are the "temple os → modern" switch.
|
||
- **Two or three weights/sizes, no more.** A tool UI wants a **type scale**, not
|
||
typographic variety: `title` (~15px semibold), `label` (~12px regular), `value`/`mono`
|
||
(~12px, tabular for note numbers / ms / dB so digits don't jitter during a drag),
|
||
`micro` (~10px, dim, for units/counts). Four cached fonts total.
|
||
- **Font face — keep the kit's current cached-font face (DS-2: no font change).** A
|
||
stylish/bundled-font upgrade was considered and **declined by Daniel** — he does not want
|
||
to bundle and redistribute a font. So the kit keeps its **current** face; this doc
|
||
specifies no new typeface. The win at §3.1 is entirely the *mechanism* — routing all text
|
||
through a `LICE_CachedFont` (AA glyph cache, one weight/size scale, optional soft shadow
|
||
on titles) instead of raw GDI `DrawTextA` — not a new type family. The `HFONT` fed to
|
||
`SetFromHFont` stays the kit's existing choice; the "temple os → modern" jump is the AA
|
||
cached-glyph pipeline plus the type scale above, not the face.
|
||
- **Soft shadow on title/accent text only** (`LICE_FONT_FLAG_FX_SHADOW`) — subtle depth,
|
||
used sparingly; body text stays crisp and flat for legibility.
|
||
|
||
### 3.2 Spacing & grid
|
||
|
||
- **An 8px base unit** (4px for tight internal padding). Row heights, gaps, and pane
|
||
insets are multiples of it — this is what makes a UI read as "designed" not "placed."
|
||
- **Generous row height** for list/zone rows (~28–32px). In the editor, S10 deletes the
|
||
seven mini-buttons per row so rows can breathe.
|
||
- **Consistent 8px pane inset** everywhere (panel edges, between panes). The current
|
||
editor pins controls flush to edges — a big part of the cheap feel.
|
||
|
||
### 3.3 Interaction states (the editor currently has NONE)
|
||
|
||
A control looks identical whether the mouse is over it, on it, or nowhere near. **This is
|
||
half of why it feels dead.** Define a state model every kit component honors:
|
||
|
||
| State | Visual | Cost |
|
||
|---|---|---|
|
||
| **rest** | `bg/cell` + inner highlight | — |
|
||
| **hover** | lighten fill toward `accent/hot`, ~8–12% | requires `WM_MOUSEMOVE` tracking (§3.5) |
|
||
| **active/selected** | `accent` fill + `accent` border | already partly present |
|
||
| **pressed** | darken + 1px inset (the surface "pushes in") | on `WM_LBUTTONDOWN` |
|
||
| **dragging** | live element follows cursor; ghost/marker at target; rest dims slightly | S10 drag machine |
|
||
| **focus (keyboard)** | 1px `text/primary` ring (distinct from selection) | already present in panel (`kColFocusBorder`) |
|
||
| **disabled** | desaturate + 40% alpha | rare (stale zone, no bank) |
|
||
|
||
Hover is the highest-value addition — it's what makes a UI feel *alive* and responsive,
|
||
and the visible proof of "speed is the selling point" (§3.5). It requires
|
||
`WM_MOUSEMOVE` (and `TrackMouseEvent` for `WM_MOUSELEAVE`). The panel's `wndProc` already
|
||
runs a timer; adding hover tracking is cheap. In the editor, the S10 drag-state machine
|
||
already introduces the move tracking, so hover rides in on that work at near-zero cost.
|
||
|
||
### 3.4 Component patterns (reused across VST + embed + panel)
|
||
|
||
The kit defines a **small, shared vocabulary** of drawn components, each: (a) drawn by a
|
||
kit function taking a `Rect` + state, (b) hit-tested by a **pure geometry** helper (the
|
||
house discipline). The set:
|
||
|
||
- **Button** — rounded (`LICE_RoundRect`), gradient fill, inner highlight, hover/pressed
|
||
states, centered `LICE_CachedFont` label. Replaces the flat `LICE_FillRect` +
|
||
`DrawRect` + GDI-text buttons in both artifacts (the whole M11 action-button
|
||
inventory, panel toggles, prune button, Add Zone, etc.).
|
||
- **List row** — selectable, hover, focus, optional leading thumbnail (the panel's
|
||
waveform cell is a specialization). Shared by the sample list (VST + panel) and zone
|
||
list.
|
||
- **Slider / knob** — for ADSR (S12) and any level control; drawn in the
|
||
`virtwnd-slider` idiom (track + filled portion + handle with highlight), hit-test pure.
|
||
- **Marker / handle** — the draggable grip primitive: keyboard-strip zone edges (S10),
|
||
waveform loop markers (S11). A consistent grip look (a bright vertical bar + a small
|
||
triangular tab) so "this is draggable" reads instantly.
|
||
- **Segmented switch** — the panel already has this (`mode_switch`); restyle it through
|
||
the kit (gradient segments, lit-active, hover).
|
||
- **Tab (named-banks strip)** — a specialization of the segmented/button idiom with the
|
||
overflow/scroll affordance already specced in Phase B; drawn through the kit.
|
||
- **Waveform** — peaks envelope (`peaks` module) drawn as a filled AA shape
|
||
(`LICE_FillTriangle`/trapezoids or a bezier top), midline, over `bg/panel`. Shared
|
||
between the panel thumbnail and the S11 editor waveform.
|
||
- **Keyboard strip** — the bespoke S10 surface: white/black key lanes, zone bars as
|
||
translucent colored overlays, root markers. No prior art in the kit; it *defines* new
|
||
kit primitives (key-lane fill, zone overlay). **Direction C's spectral treatment
|
||
(§4, DS-2 settled + revised) lives here** — the hue-mapped keyboard is the signature
|
||
surface, now a **pastel** sweep anchored on the three accent hues (§4).
|
||
|
||
### 3.5 Motion policy — "speed is the selling point," read as a constraint
|
||
|
||
Daniel named speed as *the* selling point. The design reading: **the UI must feel
|
||
instant, and no decoration may cost that.** Concretely —
|
||
|
||
- **Sub-frame response.** Hover/press/drag feedback repaints immediately on the input
|
||
message (no timer latency). This is why hover tracking is a *feature*, not overhead —
|
||
instant visual acknowledgment is the *perception* of speed.
|
||
- **Zero-jank drawing.** Keep the existing **double-buffer** discipline (draw to a
|
||
`LICE_SysBitmap`, single `BitBlt`) the editor and panel already use. Every kit
|
||
component draws into the offscreen bitmap; nothing draws direct-to-DC.
|
||
- **Repaint only what changed where cheap.** Full-panel repaint is fine at these sizes,
|
||
but during a drag, invalidate the affected region (`InvalidateRect` with a rect, not
|
||
`nullptr`). Build-time residual, flagged not mandated.
|
||
- **NO decorative animation.** No easing tweens, no fades, no animated meters beyond a
|
||
direct level follow. Animation that costs a frame of latency **violates** the speed
|
||
promise. The one permitted case: a level/meter readout that follows the audio directly
|
||
(already how the embed strip's level band works). Motion = immediate feedback, never
|
||
ornament. **Direction C's glow/bloom is a drawn *state*, never a pulse** — it must stay
|
||
static.
|
||
- **The "fast" feeling is mostly typography + hover + no-jank**, not motion. A crisp AA
|
||
label that lights on hover and commits a drag with zero flicker *reads* faster than any
|
||
animation.
|
||
|
||
---
|
||
|
||
## 4. Three visual directions (DS-2 SETTLED: B + C's spectral keyboard strip; REVISED to pastel accents)
|
||
|
||
Each is a complete palette + treatment, not a shade of the same thing. All three are
|
||
LICE-drawable today (no new dependency); all three honor the WCAG-floor "punch" rule.
|
||
Ordered safe → bold. **Daniel settled (2026-07-26) on Direction B as the target, with
|
||
Direction C's spectral keyboard strip as the signature surface** — then **revised the
|
||
accent layer (2026-07-26) from a single electric cyan to a three-accent pastel system**
|
||
(primary pastel lime green / secondary pastel teal / tertiary pastel purple), and
|
||
**revised the neutral surfaces (2026-07-26) from near-black up into REAPER's mid-grey theme
|
||
family** so the dock reads as part of REAPER, not a black slab (§2.1). Only the accent layer
|
||
and the neutral ladder changed; the direction (B + C's spectral strip) is unchanged.
|
||
|
||
### Direction A — "Studio Rack" (evolutionary; not selected)
|
||
|
||
The current forest-green-on-charcoal, but *executed properly*: gradient surfaces, AA
|
||
text, rounded controls, a hover state, inner highlights. Same hue family — accent stays
|
||
green (brightened to `~140,220,170`), background gains a subtle vertical gradient,
|
||
controls get the vwnd inner-highlight/shadow treatment.
|
||
|
||
- **Feel:** a well-made piece of pro audio hardware. Restrained, "REAPER-native."
|
||
- **Why not selected:** it's the *conservative* answer — fixes "temple os" but doesn't
|
||
make anyone say "sleek." Retained as the fallback if B/C ever feel too loud for a
|
||
pro-audio context, but Daniel's taste points away from it.
|
||
|
||
### Direction B — "Neon Console" (contemporary synth; SETTLED default, REVISED to pastel accents)
|
||
|
||
A modern soft-synth / plugin look: **REAPER-theme mid-grey neutrals** (`bg/base` ≈
|
||
`#2b2b2b`, `bg/panel` ≈ `#333333`, `bg/cell` ≈ `#3a3a3a` — the grey ladder in §2.1, matching
|
||
Daniel's REAPER theme so the dock is not a black box beside it), and the **accent layer
|
||
carries all the punch** — used with intent for the *live/active/selected* layer and for a
|
||
small number of categorical distinctions only. Everything else stays quiet grey. Waveforms
|
||
and the keyboard strip's active zone glow in the **primary** accent; inactive/categorical
|
||
elements pick up secondary/tertiary. This is the "Serum / Vital / modern Arturia" idiom —
|
||
accent-on-dark — but **pastel, not neon,** and on **REAPER-grey rather than near-black**.
|
||
|
||
**Accent layer (DS-2 revised — three pastel accents, replacing the single electric cyan):**
|
||
Daniel's call — "it's too CYAN, I prefer pastel hues" — swaps the one vivid cyan for a
|
||
**three-hue pastel family**. Concrete *starting* values (the implementer locks final hex
|
||
against the WCAG tests — §2.1):
|
||
|
||
| Role | Pastel | Starting RGB (hex) | Job |
|
||
|---|---|---|---|
|
||
| `accent/primary` | pastel lime green | `~176,224,152` (`#B0E098`) | the live/active/selected signal — the eye-magnet |
|
||
| `accent/secondary` | pastel teal | `~132,214,208` (`#84D6D0`) *(superseded, see below)* | categorical role A (non-active zones, a second cluster) |
|
||
| `accent/tertiary` | pastel purple | `~194,170,232` (`#C2AAE8`) | categorical role B (tertiary zones, a distinct affordance class) |
|
||
| `accent/hot` | brighter primary tint | `~200,236,178` (`#C8ECB2`) | hover / live / drag feedback (a lighter pastel-lime) |
|
||
|
||
**Locked values, where they diverge from the starting table (2026-07-31).** `accent/secondary`
|
||
darkened to `#38A8A0` (same hue ~176°, same saturation, lightness 0.68 → 0.44). Its **binding
|
||
limiter** — the pair that stops it going darker — is the velocity-curve trace on a
|
||
*hover-lightened* `bg/cell` at **3.03:1** against a 3:1 floor, tighter than either the AA 4.5:1
|
||
text-on-fill pair (4.91:1 on `bg/base`) or the indicator floor on rest `bg/cell` (3.94:1). ONE
|
||
new Role was added past the original three, since "exactly three accents" is no longer a
|
||
constraint: `overlay/trace` `#816AA6`, a muted violet for marks drawn *over* an accent fill (see
|
||
§the two-neighbour problem below). The spectral ramp's mid stop also became its own value —
|
||
the original pastel teal `#84D6D0` — but as a private constant, not a `Role`.
|
||
|
||
**The two-neighbour problem** (rule + numbers owned by `src/core/ui/CLAUDE.md`; the design
|
||
consequence is recorded here). A mark drawn *over* an accent fill has to read against the fill
|
||
*and* the surface behind it, which caps *any* single value at ≈3.07:1 against both. Two
|
||
consequences shape the design rather than the palette: such a role is confined to the band it
|
||
was picked for, and a **state** of that mark cannot be expressed as a hotter color — every
|
||
value that clears the ceiling sits within 1.05:1 of every other, so the grabbed envelope handle
|
||
signals by **size and a punched-out core** instead. That deliberately inverts the kit's
|
||
"brighter = hotter" convention in this one band, because over a light fill a brighter tint is a
|
||
*lower*-contrast tint (`accent/hot` measures 1.15:1 against the lime).
|
||
|
||
These are light-ish, low-saturation tints. On the **REAPER-grey surfaces** (`bg/cell` ≈
|
||
`#3a3a3a`) they still clear **AA-large (3:1)** and the **state-indicator** floor — but the
|
||
margin is **much smaller than it was on near-black** (~7.6:1 primary / ~5.5:1 tertiary /
|
||
~3.9:1 the darkened secondary on grey, vs. ~15:1 on `#12121x`). This is a real DS-2-revision tension: **the greyer background pulls the
|
||
pastels toward the floor** at the same time the "keep it soft" rule pulls them away from
|
||
saturation. Approach from the **soft side** (§2.1) — keep them as pastel as possible while
|
||
still clearing the floor on **grey, not near-black** — but **if any pastel used as a state
|
||
indicator / active fill drops below 3:1 on `bg/cell`, nudge that hue slightly deeper /
|
||
more saturated until it clears** (hue stays lime/teal/purple; only value/chroma moves a
|
||
little). Re-verify against the grey ladder, not the old near-black values.
|
||
|
||
- **Feel:** a 2026 instrument, softer and calmer than a neon single-accent — the pastel
|
||
family guides the eye to what's live (primary) and quietly categorizes the rest.
|
||
- **Watch — the tight pairs (flag for the implementer):**
|
||
- **Body text *on* a pastel fill.** When a pastel accent is a *fill* and `text/primary`
|
||
(light-grey `~220,220,220` / `#dcdcdc`) or a dark label sits on top, the pair can drop
|
||
below **AA 4.5:1** — pastels are mid-to-light, so light-grey-on-pastel is the risky
|
||
combination.
|
||
Where an accent is used as a filled surface behind body text, either (a) use a *dark*
|
||
near-black label on the light pastel (which clears easily) or (b) darken that specific
|
||
pastel fill until near-white clears 4.5:1. The theme test must cover every actual
|
||
text-on-accent-fill pair the kit draws.
|
||
- **Secondary vs. tertiary distinguishability.** Pastel teal and pastel purple are both
|
||
cool-ish and desaturated; verify they read as *distinct* categories, not two greys,
|
||
when adjacent (e.g. neighboring zone bands). Nudge tertiary warmer/more-violet if they
|
||
muddy.
|
||
- **Three pastels can drift "candy" if over-applied** — same discipline as before: accent
|
||
= live + a few categorical marks, never chrome. Coherence rule: keep all three clearly
|
||
in the pastel family (no one of them creeping back to full saturation).
|
||
- **Reversibility:** unchanged — all three accents live in the one constants block, so a
|
||
future re-pick (or a saturation nudge) is still a single-file change.
|
||
|
||
### Direction C — "Spectral" (bold; SETTLED for the keyboard strip; REVISED to a pastel sweep)
|
||
|
||
Lean into depth via a **hue-mapped gradient** across the signature surface: zones and the
|
||
waveform aren't a flat accent — the keyboard strip is a **spectrum you play across** (low
|
||
notes cool → high notes hot), the waveform filled with a vertical accent gradient. Selection
|
||
glows; the active zone has a soft outer bloom (`LICE_GradRect` halo, a *static* drawn state
|
||
— §3.5). Multi-zone keymaps become genuinely *beautiful* — each zone a band of the
|
||
spectrum — which is exactly the "multi-zone-in-one-instance" capability that RS5K
|
||
structurally lacks (the sharpest "better than RS5K" claim, made *visual*).
|
||
|
||
**How the spectral ramp relates to the three pastel accents (DS-2 revised).** The strip
|
||
keeps a **full low→high sweep**, but it is now a **pastel spectral ramp anchored on the
|
||
three accent hues** rather than a neon cool-blue→hot-magenta gradient — so the signature
|
||
surface stays coherent with "pastel, not neon" and visibly belongs to the same palette as
|
||
the rest of the UI. Concretely: the ramp runs **pastel-lime (primary, low) → pastel-teal
|
||
(secondary, mid) → pastel-purple (tertiary, high)** as a three-stop gradient through the
|
||
accent constants (or a slightly wider pastel arc that passes *through* those three anchor
|
||
points), keeping every stop in the pastel band. This ties the spectrum to the palette:
|
||
the same hues that mean "live / category A / category B" elsewhere anchor the spectrum here,
|
||
so the strip reads as an extension of the accent system, not a separate neon flourish.
|
||
**The mid stop is its own constant, not an alias of `accent/secondary`** (decoupled
|
||
2026-07-31): the ramp is a luminance progression while the accents are categorical roles, and
|
||
darkening secondary for a categorical reason inverted lo→mid→hi. A monotonicity test now guards
|
||
the ordering. The **active** zone still lifts to `accent/primary` +
|
||
its bloom, so "which zone is live" stays unambiguous over the categorical spectral bands.
|
||
|
||
- **Feel:** premium, almost visualizer-grade — but soft and cohesive, a pastel spectrum
|
||
rather than a rave. The keyboard strip is the signature; it is unmistakably the same
|
||
palette as the panel.
|
||
- **Settled scope:** **C's spectral treatment lands on the keyboard strip** (the signature
|
||
surface), over Direction B's Neon-Console chrome (now pastel-accented) everywhere else.
|
||
This is the "modern-sleek center with one bold, identity-defining surface" Daniel chose —
|
||
dial the spectrum wider (a broader pastel arc) or narrower (tight to the three anchors)
|
||
after seeing it on the DAW.
|
||
- **Watch:** gradients/bloom are easy to overdo into "toy"; the glow must stay a static
|
||
drawn state, never a pulse (§3.5); a pastel ramp has *less* inter-note contrast than a
|
||
neon one, so verify adjacent zone bands stay distinguishable (the same secondary-vs-
|
||
tertiary caution as Direction B); risks reading as *flashy* over *fast* if the speed
|
||
discipline slips.
|
||
|
||
**Settled recommendation, now spec:** build the **kit palette abstractly** (roles, not
|
||
hardcoded colors — §2.1) so the direction is *one file of constants*; **default to
|
||
Direction B (Neon Console) with the three-accent pastel system** (primary lime / secondary
|
||
teal / tertiary purple) and **Direction C's pastel spectral keyboard strip** as the
|
||
signature surface. Because the palette is abstract, a later re-pick — or a saturation nudge
|
||
within the pastel intent — is one file.
|
||
|
||
---
|
||
|
||
## 5. The build shape (landed history in `docs/ARCHIVE.md` §Phase L)
|
||
|
||
Three points and their sequencing.
|
||
|
||
### 5.1 `L1` — shared LICE drawing kit (the foundation)
|
||
|
||
A small shared module — the palette + type + component-draw layer that the VST editor,
|
||
the embed strip, and the bank panel all draw through. Honoring the house pure/shell split:
|
||
|
||
- **Pure layer (CTest-covered):** the *geometry* of components — a `theme`/palette module
|
||
(role → color, direction-selectable via one constants block; pure, trivially testable
|
||
that contrast pairs clear their floor with the B+spectral constants) and per-component
|
||
**layout/hit-test** helpers where they don't already exist (button rect, slider
|
||
track/handle geometry, list-row rect + hover hit-test). These mirror
|
||
`mode_switch`/`embed_strip`/`editor_geometry` — no LICE, no host types, unit-tested.
|
||
- **Shell layer (DAW-verified):** the **draw kit** — LICE functions that render each
|
||
component (`drawButton`, `drawSlider`, `drawListRow`, `drawWaveform`, `fillSurface` with
|
||
the micro-gradient + inner highlight, `text()` via a shared `LICE_CachedFont` set). Owns
|
||
the cached-font lifecycle. **WDL/vwnd reuse is assessed here, at build time, per DS-1** —
|
||
reuse a vwnd piece where it's genuinely cheaper (the scroll listbox is the archetype),
|
||
else draw on LICE. This is the layer that *replaces* the flat `LICE_FillRect` +
|
||
`DrawTextA` calls in every surface.
|
||
- **Cross-artifact reuse (load-bearing):** the kit is the **one source** all surfaces
|
||
draw through — the same instinct as the one-source-multiple-views rule. A button looks
|
||
identical in the panel, the embed strip, and the VST because it's the same `drawButton`.
|
||
This is what makes "scope it for the whole system" real rather than divergent restyles.
|
||
- **Coordination with Phase S:** L1 is not S10's dependency — S10–S13 build their
|
||
interaction UX with the *current* drawing and **adopt the kit when L1 lands** (whichever
|
||
order happens). The kit is the one source of drawing; when both exist, all surfaces
|
||
consume it.
|
||
|
||
### 5.2 `L2` — dock-panel layout redesign (extension-side; DS-3 SETTLED: thorough, not a re-skin)
|
||
|
||
**DS-3 settled (2026-07-26): this is a real, thorough layout design, not a light reskin.**
|
||
The panel already has good bones (thumbnails, selection, focus ring, a LICE grid, the
|
||
multi-bank vertical split, the Design View mode switch). But **M11 (about to merge to
|
||
dev)** adds a substantial button inventory — the panel must lay out the *full* inventory
|
||
intuitively, uncluttered, and useful. L2 designs that layout, then applies the L1 kit to
|
||
draw it.
|
||
|
||
**The full button/affordance inventory L2 must place (dev, M11-aware):**
|
||
|
||
*Existing (landed / specced):*
|
||
- Bank **grid** — thumbnails, multi-select, keyboard nav, audition, focus ring.
|
||
- Design View **segmented mode switch** (`[ Arrange | Design ]`) + per-mode membership count.
|
||
- Multi-bank: **named-banks tab strip** (LICE-drawn, overflow/scroll), **active-bank
|
||
indicator**, **pool/banks full-height toggles**, create/rename/delete/activate-bank
|
||
affordances, per-selection **move / copy / remove** sample menu.
|
||
- **Prune** button (R-E) — the byte-deleting action, `warn`-colored.
|
||
|
||
*M11 adds (landed — `docs/ARCHIVE.md` Milestone 11):*
|
||
- **Action trigger buttons** — clickable buttons that fire the capture + provenance
|
||
action family directly (capture item / capture track scopes, re-capture from source,
|
||
resample-and-mute-source, batch capture, conform-on-insert, insert-at-cursor, drag-out,
|
||
null-test verify). A real cluster of buttons.
|
||
- **Keybinding help labels** — each capture/provenance action surfaces its current key
|
||
binding (e.g. "Capture Item → F5") or an "unbound" marker.
|
||
|
||
**L2 design mandate (the thorough part):**
|
||
- **Group by function, not by phase.** The user thinks in *tasks* — capture, organize,
|
||
reclaim, view — not in M/B/R/D phases. Cluster the capture-action buttons together
|
||
(with their keybinding labels), the bank-organization affordances together (tabs +
|
||
move/copy/remove + create/rename/delete), the view/mode switch in the header, and the
|
||
destructive prune apart and `warn`-marked. First-principles: what does a capture-heavy
|
||
workflow reach for most, and how few clicks/pixels away is it?
|
||
- **A place for every button that doesn't crowd the grid.** The grid is the centerpiece;
|
||
the button inventory must not squeeze it. Consider a **compact action bar/toolbar** for
|
||
the frequent capture actions (icon+label buttons drawn through the kit, keybinding as a
|
||
`micro` sub-label), an **overflow/menu** for the rare ones, and reserved header space
|
||
for view/mode. The keybinding labels are `micro`/`text-dim` so they inform without
|
||
shouting.
|
||
- **Density is a design decision, not an accident.** Use the 8px grid (§3.2), elevation
|
||
layers (§2.1) to separate the action bar from the grid from the bank strip without a
|
||
forest of hairlines, and the kit's `warn` role to make prune (and any destructive verb)
|
||
unmistakable. Hover on every interactive element (§3.3).
|
||
- **Then apply the L1 kit** to draw it all: cached-font text (retire the panel's GDI
|
||
text), kit `drawButton`/segment/tab draws, palette roles, micro-gradient surfaces.
|
||
- **Hit-test geometry stays pure.** The layout redesign may add new pure geometry modules
|
||
(an action-bar layout module, mirror of `mode_switch`/`bank_grid`) but keeps all
|
||
hit-test math in the pure, CTest-covered layer. `bank_grid`/`tab_strip`/`mode_switch`
|
||
stay the source of truth for what they own; new layout gets its own pure module.
|
||
|
||
**Sequence L2 AFTER M11 merges to dev** — L2 must inventory M11's *actual* landed buttons,
|
||
not a forecast of them. If L2 starts before M11 lands, it designs against a moving target.
|
||
|
||
### 5.3 `L3` — VST editor + embed-strip restyle [LANDED]
|
||
|
||
**L3 has landed (merged `c53683e`, 2026-07-27). Phase L is now complete (L1–L7 all
|
||
landed).** The ReaSampler 9000 VST editor (`IPlugView` LICE surface,
|
||
`reasampler_editor.cpp`) and the S6 embed strip (`reasampler_embed.cpp`) received the full
|
||
kit treatment: kit cached-font text (§3.1 — the kit's current face, no bundled font), kit
|
||
component draws, the **B palette with the three-accent pastel system** (primary lime /
|
||
secondary teal / tertiary purple), the **pastel spectral keyboard strip + zone bars** (active
|
||
zone lifts to `accent/primary` + a static glow) as the signature surface, hover/pressed/drag
|
||
states throughout. The full editor component set was restyled: the **capture-first browser**
|
||
(search box + tabs + sample thumbnails, S10), **channel mono/stereo toggles** (S7), **waveform
|
||
pane with start + loop markers** (S11), **keyboard strip + zone bars** (S10/S10-Z), and
|
||
**ADSR + pitch sliders + Trigger/Gate & Varispeed/Preserve mode toggles** (S12/S15/S16).
|
||
|
||
**What was confirmed at build:**
|
||
|
||
- **All Phase S UI draw was in exactly two shells** — `reasampler_editor.cpp` (the
|
||
`IPlugView` editor) and `reasampler_embed.cpp` (the S6 embed strip). Every other
|
||
`src/vst/` UI file (`editor_geometry`, `keyboard_strip`, `waveform_view`,
|
||
`capture_browser`, `param_slider`, `browser_scroll`, `embed_strip`) is a **pure
|
||
geometry/hit-test module with zero LICE and zero draw** — the house pure/shell discipline
|
||
held. L3 was therefore a **shell-side restyle of two files**; no geometry rework required.
|
||
- **Full restyle, not a born-in-kit no-op.** The Phase S surfaces arrived on dev drawing flat
|
||
`LICE_FillRect` blocks + raw GDI `DrawTextA`, off a local pre-L1 forest-green palette
|
||
(`kColBackground` etc.) — the coordination contract's "born in the kit" branch did not
|
||
occur. L3 performed the full restyle.
|
||
- **Two palettes collapsed to one.** The editor shell's local `kColBackground`/`kColCardBg`/
|
||
`kColThumb`/… forest-green-on-near-black constants block (the **pre-L1 palette** this
|
||
doc's §2 set out to replace) was retired; all surfaces now draw through the L1 `theme`
|
||
roles. One kit, one look.
|
||
- **Beta title band — textual-only distinction (Daniel, 2026-07-27).** The S18 beta channel
|
||
title band gets no distinct visual accent; L3 restyles it in the standard B pastel palette
|
||
and the beta-vs-stable distinction stays purely textual (the channel-derived plugin name
|
||
via `app_version`, as before). No channel-specific accent color.
|
||
- **VST3 class UID unchanged.** A visual refresh is not a compat event; RT/`process` path
|
||
untouched; pure geometry modules remained pure throughout.
|
||
|
||
**Coordination contract (resolved).** Phase S's S10–S13 built their interaction UX with the
|
||
current drawing and adopted the L1 kit via L3 (the "L3 restyles them" branch). There is now
|
||
one kit and one look across both artifacts.
|
||
|
||
### 5.4 Sequencing summary
|
||
|
||
```
|
||
L1 (shared kit) ──► L2 (dock-panel layout redesign, extension-side) [LANDED]
|
||
└─────────► L3 (VST editor + embed-strip restyle) [LANDED]
|
||
|
||
Phase L is complete — L1, L2, L3, L4, L5, L6, and L7 have all landed.
|
||
```
|
||
|
||
L1 was the foundation both L2 and L3 consumed. L2 was extension-side and landed after M11.
|
||
L3 was gated on Phase S reaching dev — Phase S merged 2026-07-27 and L3 followed
|
||
immediately. **Phase L is complete.**
|
||
|
||
---
|
||
|
||
## 6. Forks — all SETTLED (Daniel, 2026-07-26)
|
||
|
||
**Fork DS-1 — the toolkit. SETTLED: LICE + WDL free game, no external frameworks.**
|
||
|
||
- **Settled:** draw the modern look with **LICE** directly (gradients, AA, rounded, cached
|
||
fonts). **Anything useful in WDL/vwnd is free game** — "don't reinvent the wheel": reuse
|
||
a vwnd piece (skin/image helper, draw idiom, a specific control like the scroll listbox)
|
||
where it's genuinely usable and cheaper than re-deriving; otherwise build on LICE. The
|
||
one standing caution (not a ban): keep hit-test **geometry** in pure CTest-covered
|
||
modules rather than importing vwnd's retained-mode object model wholesale, so the
|
||
pure/shell split and its test coverage hold.
|
||
- **Rejected:** any **external framework** — iPlug2, JUCE, VSTGUI. They re-open settled
|
||
D-A to solve a look problem that is *not* a toolkit-ceiling problem (REAPER's own themed
|
||
UI is LICE). WDL's own VST2-era `IPlug` is dated and irrelevant.
|
||
- **Reasoning preserved:** LICE is a sufficient modern-dark-UI toolkit; the current
|
||
"temple os" look is drawing at LICE's floor, not hitting its ceiling. No new dependency,
|
||
fully consistent with D-A.
|
||
|
||
**Fork DS-2 — visual direction. SETTLED 2026-07-26: Direction B (Neon Console) + Direction
|
||
C's spectral keyboard strip. REVISED 2026-07-26 (Daniel): three-accent pastel system.**
|
||
|
||
- **Settled (original):** **B** (near-black + one vivid accent, modern soft-synth idiom) as
|
||
the system-wide target, with **C's spectral, hue-mapped keyboard strip** as the signature
|
||
surface — the multi-zone-in-one-instance advantage made visual. The single accent was
|
||
**electric cyan**.
|
||
- **Revised (2026-07-26, Daniel) — accent layer:** the accent layer changes from **one
|
||
electric cyan** to a **three-accent pastel system** — **primary = pastel lime green,
|
||
secondary = pastel teal, tertiary = pastel purple**. The spectral keyboard strip becomes
|
||
a **pastel** sweep anchored on the three accents (§4 Direction C). This revision is
|
||
**palette-only.** *Rationale (Daniel, verbatim intent):* "the layout is a huge
|
||
improvement… the only thing I want to change is the cyan — it's too CYAN, I prefer pastel
|
||
hues… a three-color accent system, primary / secondary / tertiary as pastel lime green /
|
||
pastel teal / pastel purple" — too cyan; prefers pastel; wants three-accent
|
||
primary/secondary/tertiary.
|
||
- **Revised (2026-07-26, Daniel) — neutral surfaces:** the neutral role ladder moves
|
||
**from near-black up into REAPER's mid-grey theme family**, so the dock reads as *part of
|
||
REAPER, not a black slab bolted on beside it*. Concrete starting ladder (§2.1): `bg/base`
|
||
≈ `#2b2b2b` (REAPER chrome), `bg/panel` ≈ `#333333`, `bg/cell` ≈ `#3a3a3a` (REAPER track
|
||
bg), `line/hairline` ≈ `#4a4a4a`, `text/primary` ≈ `#dcdcdc`, `text/dim` ≈ `~#a0a0a0`+.
|
||
The **elevation-ladder discipline is unchanged** (base < panel < cell by a few %, micro-
|
||
gradient + inner highlight/shadow carry elevation, not hard borders). This shifts the WCAG
|
||
burden (§2.1 re-read): **`text/dim` on grey** and **the three pastels as state indicators
|
||
on grey** are now the tight pairs — mid-grey-on-mid-grey for dim text, shrunken pastel
|
||
cushion for the accents — the implementer re-verifies both against the grey ladder, not
|
||
near-black. *Rationale (Daniel, verbatim):* "we should adjust the neutral colors to be
|
||
more grey to match my REAPER theme… Text and surfaces should match" — the dock should
|
||
read as part of REAPER, not a black box.
|
||
- **Font upgrade — considered and DECLINED (Daniel, 2026-07-26).** A stylish/bundled-font
|
||
upgrade was on the table in an earlier draft of this revision and Daniel **declined it**:
|
||
he does not want to bundle and redistribute a font. The kit keeps its **current**
|
||
cached-font face; no new typeface is specified (§3.1). Recorded here so the question is
|
||
not re-raised — the §3.1 win is the AA cached-glyph mechanism, not the face.
|
||
- **Not selected:** **A Studio Rack** (evolutionary green — retained only as a fallback if
|
||
B/C read too loud for pro-audio).
|
||
- **Reasoning preserved:** modern-sleek center + one bold identity surface; the revision
|
||
keeps that shape but trades neon punch for pastel calm (Daniel's taste, stated on seeing
|
||
the landed layout) and adds categorical meaning via the accent trio. The kit palette is
|
||
abstract (roles, one constants block — now three accent roles instead of one), so this is
|
||
still **reversible for ~one file** — a starting direction, not a permanent lock. The final
|
||
hex is the implementer's to lock against the theme module's WCAG tests, staying within the
|
||
pastel intent (the "soft-side" floor discipline, §2.1).
|
||
|
||
**Fork DS-3 — panel refresh scope. SETTLED: thorough layout redesign (not a light
|
||
re-skin).**
|
||
|
||
- **Settled:** L2 is a **real, thorough layout design** that lays out the full button
|
||
inventory (including M11's action buttons + keybinding labels) intuitively, uncluttered,
|
||
and useful — then applies the L1 kit to draw it. Sequenced **after M11 merges** so it
|
||
designs against the actual button set.
|
||
- **Superseded:** the earlier "(a) light — apply the kit, keep the structure" lean. M11's
|
||
button additions make a *layout* pass necessary, not just a skin pass — a forest of new
|
||
buttons crammed into the current structure would be exactly the clutter the redesign
|
||
exists to prevent.
|
||
- **Reasoning preserved:** the panel's grid/vertical-split bones are sound and stay; the
|
||
*layout of the button inventory around them* is the design work. Structural rework
|
||
destabilizing landed Phase-B panel work is still avoided — L2 groups and places, it does
|
||
not tear down the vertical-split / grid / tab structure.
|
||
|
||
---
|
||
|
||
## 7. What this does NOT change (guardrails)
|
||
|
||
- **D-A stays settled** — bare Steinberg SDK + LICE, no framework. This doc *reinforces*
|
||
it (LICE is sufficient), it does not re-open it. (D-A lives on the phase-s worktree; on
|
||
dev it is the settled Phase S build-shape decision.)
|
||
- **The pure/shell split holds** — all layout/hit-test stays in pure, CTest-covered
|
||
geometry modules; the kit's *draw* half is shell, its *geometry* half is pure. No
|
||
hit-test math moves into untestable code, even where a WDL piece is reused (DS-1).
|
||
- **RT discipline untouched** — the kit is draw-thread only; nothing here touches
|
||
`process` or the off-thread reload handoff (Phase S concern).
|
||
- **Read-only-over-bank untouched** — this is look-and-feel; no data-ownership change.
|
||
- **The instrument keeps the same VST3 class UID / component-state contract** — a visual
|
||
refresh is not a compat event (Phase S concern; noted for when L3 runs).
|
||
- **Windows-only (D5)** — the extension's GDI/HFONT path assumes Windows; no cross-platform
|
||
font-fallback concern. The kit keeps its current cached-font face (no bundled font — DS-2
|
||
declined the font upgrade), so §3.1 introduces no new redistribution or cross-platform
|
||
font obligation.
|
||
- **Phase S is not gated on Phase L** — S7–S13 proceeded in parallel; they adopted the
|
||
kit via L3 when it landed. Phase L is complete (L1–L7 all landed).
|