docs(s-view): spec the r11 Sample-face recomposition (knob deck, curve popup, full-width hero) for Wave B

Adds S-VIEW-11/12/13 to PLAN, r11 addendum, and forks R11-F1 (window default height) + R11-F2 (Zone-panel knob parity) for Daniel.
This commit is contained in:
2026-07-27 18:25:32 -04:00
parent e2bd4f4351
commit a4e33b1c04
3 changed files with 274 additions and 9 deletions
+157 -3
View File
@@ -2462,6 +2462,137 @@ payload bump), gated on T-KEYTRK; plus a **Wave 2 shell-integration item** (the
Zone-panel curve-editor UI through the L1 kit), gated on the foundation track and composing with the
S-VIEW-2 Sample face + S-VIEW-3 envelope overlay.
### The Sample-face recomposition — full-width hero, knob deck, curve popup (r11; Daniel, 2026-07-27)
> **Additive S-VIEW revision (r11) — the layout spec for the Wave B editor rebuild.** Recomposes
> the landed S-VIEW-1..10 Sample face after Daniel's DAW pass: **every linear slider becomes a
> small radial knob** grouped into a fenced **knob deck**, the two **mode toggles shrink** from
> full-width control rows to compact group-header segments, and the **inline velocity-curve box
> is replaced by a miniature curve preview button** that summons a **full-size popup editor**
> freeing the **hero waveform to run full width**. Engine, params, storage, and persistence are
> all **unchanged** — this is a pure view recomposition of existing controls (zero component-state
> change, no new persisted fields, VST3 class UID unchanged). The **knob primitive** itself
> (minimal arc ~6→4 o'clock, needle indicator, vertical drag) is a **separate in-flight track**;
> this section specs the layout that consumes it. Directives (Daniel, 2026-07-27): radial knobs
> replacing all sliders, grouped "in a reasonable way" with **the envelope controls grouped
> together intuitively**; pitch-engine and Gate/Trigger toggles **not full-width**; the velocity
> curve as a **small square preview button right of the preview-velocity control** opening a
> popup with the full-size editor, **right-click removing a control point** in the popup; the
> waveform preview **full-width**. Product framing: `docs/product/midi-playback.md` §Addendum r11.
**Inventory contract — every landed Sample-face element has a named home (nothing silently
dropped):**
| Landed element (S-VIEW-1..10) | r11 home |
|---|---|
| Title band: name + live readout, Browse + Zone nav buttons | Unchanged |
| Hero waveform + S11 markers (start/loop/loop-fill) + "(decoding…)" placeholder | Unchanged, now **full width** (the velCurve carve-out is gone) |
| Envelope overlay polyline + draggable nodes (S-VIEW-3/F2) | Unchanged, over the full-width hero |
| Inline velocity-curve box (trace, node drag, click-add, Alt-click delete, drag-off delete) | **Popup editor** — all interactions preserved, + new right-click delete; summoned from the mini preview button |
| Fenced root spectral strip + root marker drag | Unchanged (cluster left, now remainder-width) |
| Preview-trigger button | Unchanged (cluster) |
| Preview-velocity horizontal slider | **Radial knob** in the cluster ("Vel"), same persisted `previewVelocity` seam (envelope-v6, untouched) |
| Mono \| Stereo toggle | Unchanged (cluster, right-anchored) |
| Mode row (Gate\|Trigger, full-width toggle) | **Compact toggle in the AMP ENVELOPE group caption row** |
| Pitch eng row (Varisp\|Preserve, full-width toggle) | **Compact toggle in the PITCH group caption row** |
| Attack / Hold / Decay / Sustain / Release sliders (Gate) | **Knobs in the AMP ENVELOPE group** |
| Length % / Fade in / Fade out sliders (Trigger) | **Knobs in the AMP ENVELOPE group** (time-ordered: Fade In · Length % · Fade Out) |
| Pitch env row (Off\|On, full-width toggle) | **Compact toggle in the PITCH ENV group caption row** |
| P.Attack / P.Decay / P.Depth sliders | **Knobs in the PITCH ENV group** |
| Key track slider | **Knob in the PITCH group** |
| Empty state ("pick a capture") + S13 drop-hint banner | Unchanged |
**Band order (top → bottom) — hero becomes the elastic band.** Title (26, unchanged) → **hero
(full width between kPad margins, ELASTIC: absorbs all height left after the fixed bands, floor
150px)** → cluster (52, unchanged height) → **knob deck (fixed-height, bottom-anchored; ~92px
per deck row)**. The old fixed-150 hero + rest-of-window slider stack inverts: the control
surface is now the fixed band and the waveform grows with the window. At the unchanged 840×620
default this yields a ~430px hero (see fork R11-F1 for the height call).
**The knob deck — three fenced groups, left → right.** Each group is a hairline-bordered
`bg/panel` box with a **caption row** (~20px: micro-caps caption left; the group's **compact
mode toggle right-anchored in the caption row** — this is where the not-full-width toggles
live) over a **knob row** of fixed cells. Knob cell: **48w × 58h** — 28px knob centered, 12px
`Font::Micro` label beneath in `text/dim`; **the label swaps to the live value during
hover/drag** (no third line, no permanent value clutter). Toggle segments reuse the Mono/Stereo
grammar (~4452px per segment, 18px tall, Active segment accent-primary). Groups:
- **AMP ENVELOPE** — caption toggle: **Gate | Trigger** (`kPlayMode`). Knobs, Gate: **Attack ·
Hold · Decay · Sustain · Release**; Trigger: **Fade In · Length % · Fade Out** (a deliberate
time-order reorder of today's row order — left-to-right matches the drawn envelope). The group
**reserves the 5-cell Gate width** so a mode flip repopulates in place and never reflows the
neighboring groups (Trigger simply leaves two cells blank). This is the "envelope controls
grouped together as a unit" directive: the group IS the envelope, its toggle picks the shape,
and the hero's envelope overlay is the same params drawn large (two surfaces, one model —
unchanged from S-VIEW-F2).
- **PITCH** — caption toggle: **Varisp | Preserve** (`kPitchEngine`). Knob: **Key Track**
(0200%, the S-VIEW-6 scalar). Key tracking is repitch math, so it lives with the engine that
applies it.
- **PITCH ENV** — caption toggle: **Off | On** (`kPitchEnvEnable`). Knobs: **P.Attack · P.Decay
· P.Depth**. Its own fenced envelope unit, mirroring AMP ENVELOPE's grammar at smaller scale.
When Off, the three knobs draw `Disabled` (kit state) rather than vanish — stable geometry.
Group gaps 12px; deck side margins kPad. Sum at these metrics ≈ 610px wide; **below that width
the pure module wraps whole trailing groups to a second deck row deterministically** (the deck
grows, the elastic hero shrinks toward its floor — the 560px `checkSizeConstraint` minimum
forces PITCH ENV onto row two, which is acceptable at the floor).
**The cluster band (root + preview + curve button).** Root strip keeps the left side but becomes
**remainder-width** (min ~200px) instead of a fixed 55%; the right side is a fixed-width
right-anchored run: **Preview button (64w) · Vel knob cell (48w, the radial preview-velocity
knob) · curve preview button (28×28, immediately right of the Vel knob — per the directive) ·
Mono | Stereo (right-anchored, unchanged)**. The **curve preview button** is a hairline-bordered
`bg/cell` square with the zone's live velocity curve traced in miniature (1px secondary-accent
trace, no node markers at this scale); Hover lifts it, and it draws **Active (accent-primary
border)** while its popup is open. It re-renders live as the popup edits the curve.
**The popup curve editor.** Summoned by left-click on the curve preview button. A **centered
sheet over the Sample face**: a 0.50-alpha `bg/base` wash over the whole window (lighter than
Browse's 0.82 — this is a focused sub-editor, the Sample face stays legible behind it), then a
`bg/panel` + hairline sheet, **width clamp(60% of window, 360..520), height clamp(55%, 260..380)**.
Inside: a ~22px title row ("VELOCITY → AMP" micro-caps left, a **Close (×) button** 18×18 right),
and the **full-size curve box** filling the remainder using the existing `curveBoxFromRect`
inset/mapping-box grammar (one coordinate formula, as landed). Interactions are **identical to
the landed inline editor** — node drag with mouse-up commit, empty-space click adds + grabs a
point, Alt-click delete, drag-off delete (the box+24px drag-off margin stays inside the sheet,
so it cannot collide with dismissal) — **plus the NEW right-click delete (issue 3c): right-click
on a node removes it**, committing immediately through the same path as Alt-click, with the
existing `deletePoint` endpoint guard making endpoint right-clicks a safe no-op. Right-click
becomes the *primary* delete affordance; Alt-click and drag-off remain as landed alternates
(nothing dropped). **Dismiss:** Close click, click on the wash outside the sheet (only when no
drag is in flight), or Esc. Popup state (open flag + target zone) is editor-local, never
persisted. The `dragStartMap_` rollback contract is unchanged.
**Module architecture (r11 delta — house pattern preserved).**
- **Pure NEW `knob_deck`** — group boxes, caption rows, compact-toggle rects, knob-cell rects,
deterministic group wrap, and hit-test (point → control id + element kind). Mirror of
`action_bar` / `param_slider`; consumes the same shell-owned control-id descriptors;
engine-free, LICE-free, unit-tested (layout at reference widths, wrap at the 560 floor,
hit-test at cell/toggle boundaries).
- **Pure NEW `curve_popup`** — sheet/close/box geometry from the window size + the
outside-sheet dismissal test. Mirror of `overflow_menu`; unit-tested at the size clamps.
- **The knob primitive** (value↔needle-angle map, arc geometry, vertical-drag delta→value) is
the **separate in-flight track**; `knob_deck` treats a knob cell as a rect and defers
value↔angle to the primitive. Layout-level contract on it: vertical drag with resolution at
least matching the retired sliders; a Shift fine-drag is optional polish (mirroring the
envelope-node note), not required.
- **`param_slider` retires from the Sample face** (and from the Zone panel under fork R11-F2);
its toggle-segment helpers may be reused for the compact toggles or subsumed into `knob_deck`
— implementer's call at build. The preview-velocity control becomes a knob cell bound to the
same persisted `previewVelocity` (seam untouched).
- **Shell (`reasampler_editor.cpp`):** re-lays `computeSampleBands` (elastic hero, fixed deck),
draws groups/knobs/mini-button/popup through the **L1 kit** by palette role, routes
right-click (verify `WM_RBUTTONDOWN` reaches the child wndproc — see must-verify) and Esc.
Knob drags commit on mouse-up exactly as slider drags did (live invalidate, `commitAndReload`
on release); the knobs and the hero's envelope nodes stay two surfaces on one param model.
**Spec'd aesthetic defaults (decided here; Daniel can veto at smoke test):** label↔value
swap-in on knob cells; Trigger knob time-order (Fade In · Length % · Fade Out); popup wash
0.50; group captions AMP ENVELOPE / PITCH / PITCH ENV; disabled-not-hidden PITCH ENV knobs.
The two genuinely open aesthetic calls are forks **R11-F1** and **R11-F2** in the fork ledger
below.
### Engineering prerequisite 1 — drop-to-FX bug (routed to implementation, NOT a design call)
**Symptom (Daniel):** dropping a capture onto a track's FX chain does not instantiate + init
@@ -2569,15 +2700,27 @@ face without scrolling, and cannot be resized below the constraint floor.
preview velocity **persists** via envelope-v6 `ComponentState`; the envelope overlay's nodes are
**draggable** via the pure `envelope_edit` module. They are no longer open questions.)*
- **S-VIEW-F3 — Browse modal presentation.** Full-window overlay (lean — the modal-picker feel) vs.
a large centered sheet with a dimmed Sample behind. Presentation detail; either satisfies "easy
to summon and dismiss." **Still Daniel's call.**
- **S-VIEW-F3 — Browse modal presentation. SETTLED (2026-07-27): full-window overlay** — landed
in S-VIEW-1/S-VIEW-5 (Browse renders as a full-window modal over Sample). Recorded here so the
ledger matches PLAN.md; no longer open.
- **R10-F1 — velocity-curve default (Daniel, 2026-07-27).** **Option A: flat y=1** (Daniel's
verbatim default — every velocity plays at full level; NOT back-compat with today's linear
`velocity/127`, so existing zones' dynamics change) vs. **Option B: linear y = x/127** (bit-
identical to the shipped engine; contradicts the verbatim "full level" default). **Lean A**,
flagged as a deliberate shipped-behavior change, not a silent regression. This is the only
non-back-compat surface the velocity-curve feature introduces. **Daniel's call.**
- **R11-F1 — hero height vs. default window (r11; Daniel, 2026-07-27).** With the knob deck
collapsing ~312px of slider rows into ~92px, the elastic hero at the unchanged 840×620
default runs ~430px tall — waveform-dominant (Simpler-like; envelope-node drags gain vertical
precision). Alternative: shrink the default to ~840×520 (hero ~330px, a tighter face).
**Lean: keep 840×620 + elastic hero** — the freed space going to the waveform is the point of
the recomposition, and no default-size churn. Purely aesthetic. **Daniel's call.**
- **R11-F2 — Zone-panel parity (r11; Daniel, 2026-07-27).** Convert the Zone param panel to the
same knob deck + curve-preview-button/popup (one control grammar everywhere; retires
`param_slider`'s slider rows outright) vs. leave Zone on the landed slider rows (smaller Wave
B, but the same params render as knobs on Sample and sliders on Zone — a grammar fork).
**Lean: parity** — one grammar, and the two surfaces already share one storage site (S15-F2).
Cost: Wave B scope grows by the Zone panel re-lay. **Daniel's call.**
### Must-verify before build (S-VIEW)
@@ -2609,6 +2752,17 @@ preview velocity **persists** via envelope-v6 `ComponentState`; the envelope ove
- **Velocity-curve eval monotonicity (r10)** — confirm the pure `velocity_curve` bezier is monotonic
in x over the 0127×01 box (one output per velocity) and that control-point edits stay clamped +
x-ordered at the boundaries.
- **Right-click routing (r11)** — confirm `WM_RBUTTONDOWN`/`WM_RBUTTONUP` reach the editor's child
wndproc (today only left-button + move are handled) before committing to right-click node delete;
same sweep for Esc/`VK_ESCAPE` key routing while the curve popup is open (the Browse search box
already takes keyboard input, so the focus path exists — confirm the popup sees it).
- **Deck wrap + hero floor (r11)** — confirm the pure `knob_deck` wrap is deterministic at the
560×460 `checkSizeConstraint` floor (PITCH ENV onto row two) and the elastic hero's 150px floor
holds with a two-row deck at minimum height.
- **Knob primitive contract (r11)** — confirm the separately-built knob primitive and `knob_deck`
agree on the knob-cell rect and the value↔angle map (one formula each side of the seam), so the
drawn needle and the drag hit-test can never drift — the same one-formula discipline as
`curveBoxFromRect`.
---