docs: archive Phase L L7 to COMPLETED; reconcile CLAUDE.md + CONTEXT.md + PLAN.md

This commit is contained in:
2026-07-27 03:00:42 -04:00
parent 776be32dff
commit 9a1bcbcf7a
4 changed files with 97 additions and 169 deletions
+15 -149
View File
@@ -285,7 +285,8 @@ build:** the `IReaperUIEmbedInterface` contract + embed message/lifecycle agains
> moves it to `COMPLETED.md`.
>
> **L1 (shared LICE drawing kit — the foundation), L2 (dock-panel layout redesign), L4
> (dock-panel button layout enhancement), and L5 (dock-panel button refinements) have landed** —
> (dock-panel button layout enhancement), L5 (dock-panel button refinements), L6 (toolbar
> polish), and L7 (capture ordering, card metadata, and selection styling) have landed** —
> `theme`/palette module, `component_geometry` geometry/hit-test helpers, `draw_kit` shell, GDI
> `DrawText` retirement in `bank_panel` (L1); `action_bar` pure task-grouped layout module, full
> M11-aware button inventory placed by task cluster, `bank_panel` redesigned through the L1 kit
@@ -294,8 +295,11 @@ build:** the `IReaperUIEmbedInterface` contract + embed message/lifecycle agains
> `Switching` in `action_bar` (L4); top-bar overflow menu (`overflow_menu` pure module),
> custom LICE-kit hover-delay tooltips (`tooltip` pure module), opposite-mode Item/Track tag
> buttons + Show Both, Toggle + Activate-Arrange/Design buttons removed, grouping spacing
> widened (`mode_enable` pure module) (L5). See `COMPLETED.md`. **L3 remains** — L3 gated on
> Phase S landing on dev.
> widened (`mode_enable` pure module) (L5); single-row button faces, keybinding in tooltip,
> Cancel RT moved to overflow, top-bar cluster order tidied (L6); per-bank `SlotMap`
> (id→slot) in `bank_book`, sparse-grid rendering, `card_drag` + `card_meta` pure modules,
> `captureTimeSigNum`/`captureTimeSigDenom` on `Sample`, tertiary-border selection (L7). See
> `COMPLETED.md`. **L3 remains** — L3 gated on Phase S landing on dev.
>
> **This section is self-contained for a team without Phase S context.** Where a point
> touches a Phase S surface (the VST editor, the embed strip, the keyboard strip), the
@@ -372,139 +376,6 @@ work.
state — never a pulse, per the speed constraint) as the signature surface; VST3 class UID
unchanged. (Gated.)
## L7 — capture ordering, card metadata, and selection styling (ungated, after L6)
**Goal:** Three grid-facing improvements to the dock panel, drawn through the L1 kit in the
settled DS-2 palette. (1) **Persisted deterministic capture order + drag-drop reorder + sparse
placement:** each bank (and the pool) carries an explicit, persisted per-sample order, changed
by dragging cards within the grid; a card may sit in a grid slot that leaves gaps (an empty
first row above an occupied second row). (2) **Decorative metadata over the peaks:** each card
overlays capture length as **bars.beats.subdivisions in the bottom-LEFT** and **seconds.ms in
the bottom-RIGHT**, subtle/legible over the waveform in the kit's micro / value-mono type
classes. (3) **Selection restyle:** a selected card drops the inverted accent-fill and instead
draws the *normal* cell + an **`accent/tertiary` (pastel purple `#C2AAE8`) border**; hover,
focus, and drag-highlight stay visually distinct. **No new capture/placement behavior** — this
is model + layout + draw only; the "capture ≠ placement" principle is untouched.
> **This is a persisted-model change, not pure layout.** Feature (1) adds an explicit
> per-sample ordinal/slot to the persisted state and its JSON round-trip + a migration for
> existing projects — scoped honestly below, model work separated from draw work.
> **Forks — all SETTLED 2026-07-27 (see CONTEXT.md §L7 for full detail):**
> - **F1 — SETTLED: capture-time stamp.** Add `captureTimeSigNum` / `captureTimeSigDenom` to
> `Sample`, stamped on the capture path alongside the existing `captureTempo`;
> bars.beats.subdivisions renders from the stamped tempo + meter, stable under later project
> changes. Live-meter-at-draw-time rejected (would drift). The capture-path stamp checkbox
> below is **settled, not flagged.**
> - **F2 — SETTLED: plain interchangeable-slot substrate, NOT fixed MPC-style slots.** The
> carrier is a **per-bank id→slot map** (gap-preserving interchangeable slots): a slot is just a
> display position an occupant sits in — **no slot identities, no numbered/addressable slots, no
> slot actions, no MIDI-bindable slot numbers, no capture-to-slot-N.** The "plain vs. M9-shaped"
> sub-fork is closed (plain). M9 (capture-to-slot-N / insert-slot-N, MIDI-bindable, MPC-style)
> stays **explicitly deferred (Daniel, 2026-07-26);** the substrate still eases a future M9
> revival but L7 adds no M9 actions or bindings.
> - **F3 — SETTLED + EXTENDED.** Drag precedence: leave-client → OS drag-out; else tab/other-bank
> → move/copy (Ctrl = copy); else same-bank grid → reorder-to-slot. **Extensions:**
> (a) **drop-result cursor cues** — the cursor indicates the drop result via REAPER-idiomatic
> special cursors (reorder / move / copy / OS-drag-out / replace); the shell sets it via
> Win32/SWELL `SetCursor`, but the *decision* of which cue applies stays in the pure gesture
> helper. (b) **Alt+drop-onto-occupied = REPLACE** — default occupied-slot drop stays
> insert-and-shift; holding Alt replaces the occupant (index-only removal, existing
> remove-from-bank semantics — file stays on disk, prune governs bytes, `hashReferencedElsewhere`
> handles last-reference). **Pool rule:** Alt+Replace in the un-evacuable pool is allowed only
> when the same pool-privilege guard that `remove` applies would permit removing that sample —
> one rule, no pool-only path; the replace cursor cue shows only when Alt is actually held over
> an occupied slot.
**Verify (in DAW):** capture several samples; drag a card to a new position within its bank —
the order persists across project save/reload and Ctrl-Z restores the prior order; drag the
first card below the second so the first row is empty — the gap holds across reload; each card
shows bars.beats.subdivisions bottom-left and s.ms bottom-right, legible over the peaks; a
selected card shows the normal cell with a purple (`accent/tertiary`) border — no inversion —
and hover / focus / drag-target remain distinct; during any drag the cursor shows the correct
special cue (reorder within the bank, move/copy over a tab/other bank, OS drag-out past the client
edge); Alt+drop over an occupied card **replaces** the occupant (the replaced sample leaves that
bank's grid but its file survives on disk; if it was the last reference, prune later reclaims it),
and the replace cursor appears only while Alt is held over an occupied slot; dragging a card to
another bank's tab still moves/copies (not reorder), and dragging out of the panel still starts an
OS file drag; an existing project (saved before L7) loads cleanly with cards in their prior
insertion order.
**Depends on:** L1 (the kit) for all draw; sequences **after L6** (both rework the same
`bank_panel` grid). **Ungated by Phase S** (the dock panel is on dev); independent of the L3
gate. No `capture` / `insert` behavior change; no palette/font decision re-opened.
**(a) Persisted order + sparse-position model (pure, hard-tested) — model work, separate from draw:**
- [ ] Add an explicit per-sample **display position** to the persisted bank state — a
**per-`Bank` id→slot map in `bank_book`** (gap-preserving interchangeable slots, per F2
settled). `bank_model` / `Sample` stay untouched (position is a per-bank display concern, not a
`Sample` property — a copy of a sample into two banks may sit at different slots). **No slot
identities / numbered addressable slots** — a slot is just a position an occupant sits in.
- [ ] JSON round-trip for the position data (`serialize`/`deserialize` remain lossless:
`deserialize(serialize(x)) == x` including positions).
- [ ] **Migration for existing projects** (old blobs with no position data): load cleanly with a
**default = current insertion order, densely packed** (no gaps). Spec the default explicitly so
a pre-L7 project is visually identical on first load post-L7.
- [ ] **Gap semantics on insert / delete / prune** (pure, tested): a new capture takes the next
free slot after the last occupied one (append); a delete/remove/prune **leaves a gap** (does
not re-pack) so positions stay stable. **Confirmed defaults:** trailing empty tail trimmed for
scroll-extent purposes; interior gaps preserved.
- [ ] **Reorder mutator** (pure): move sample `id` to target slot within its bank, gap-preserving;
deterministic; unit-tested. Drop-into-empty places there; **drop-onto-occupied (default, no
modifier) inserts-before-and-shifts-tail.**
- [ ] **Alt-replace mutator** (pure): Alt+drop over an occupied slot **replaces the occupant**
index-only removal of the occupant from THAT bank (reuse the existing remove-from-bank
semantics: file stays on disk, owned-manifest/prune govern bytes, `hashReferencedElsewhere`
handles the last-reference case), then the dragged sample takes the vacated slot (position
preserved). **Pool guard:** if the sample sits in the un-evacuable pool, the replace's
index-removal step passes the **same pool-privilege guard that `remove` applies** — if
remove-from-pool would be rejected for that sample, Alt+Replace over it is rejected (falls back
to no-op / default insert-shift; settle rejection UX at build). One rule, no pool-only path.
Deterministic; unit-tested. **No new deletion authority — never touches disk.**
- [ ] Undo integration: a reorder **or a replace** is one Ctrl-Z (batched undo point in the
actions/shell layer, matching the existing bank-verb undo discipline).
**(b) Reorder-drag gesture + slot hit-test (pure geometry + shell wiring):**
- [ ] **Sparse-aware grid layout** (pure, extend `bank_grid`): map per-slot positions (with gaps)
to `CellRect`s — an empty slot is drawn empty (or skipped), not back-filled. Slot ↔ pixel-rect
math + point→slot hit-test stay pure and unit-tested (mirror `mode_switch`/`bank_grid`).
- [ ] **Drag-disambiguation** (F3): drop within the same bank's grid = reorder-to-slot (empty =
place; occupied+no-modifier = insert-shift; **occupied+Alt = replace**); drop on a tab / other
bank = move/copy (unchanged, Ctrl = copy); leave client = OS drag-out (unchanged). Precedence
encoded in a **pure decision helper** (mirror `drag_out::decideGesture`); shell reads live
pointer + region + client rect + **modifier state (Alt)** and calls it. The helper returns the
**resolved gesture** (reorder / move / copy / os-drag-out / replace) so the shell can map it to
a cursor cue.
- [ ] **Drop-result cursor cues** (F3 extension): the shell sets a REAPER-idiomatic special cursor
per the pure resolved-gesture (reorder / move / copy / OS-drag-out / replace) via Win32/SWELL
`SetCursor`; the **replace** cursor shows only when Alt is actually held over an occupied slot.
**Cue-decision logic stays in the pure helper;** the shell owns only the `SetCursor` call and
the cursor resources (load/synthesize kit-consistent cursors at build; REAPER's own action
cursors as the visual reference).
- [ ] Shell wiring: on a same-bank reorder drop, resolve the target slot via the pure hit-test and
call the reorder mutator (or, when Alt is held over an occupied slot, the **Alt-replace
mutator**); drop-target slot highlight during the drag (distinct from the existing region/tab
drop highlights, and — for a pending replace — distinct from the pending-reorder highlight).
**(c) Metadata overlay (pure formatting + kit draw):**
- [ ] **Pure formatters, unit-tested:** `bars.beats.subdivisions` from `lengthSeconds` +
`captureTempo` + capture-time signature (F1) and `seconds.milliseconds` from `lengthSeconds`.
Deterministic string output; edge cases (zero length, missing tempo → graceful blank/fallback).
- [ ] **Capture-time signature stamp (F1, settled — capture-path write beyond draw work):** add
`captureTimeSigNum` / `captureTimeSigDenom` to `Sample` + its JSON round-trip, stamped on the
capture path (read the project meter at capture — verify `TimeMap_GetTimeSigAtTime` / equivalent
against the SDK at build). Old samples with no stamp fall back gracefully in the formatter
(blank musical read-out, or a documented assumed 4/4).
- [ ] **Kit draw:** overlay the two strings on the card — bars.beats bottom-LEFT, s.ms
bottom-RIGHT — via the kit `text()` in the micro / value-mono class, `text/dim` (or a subtle
shadowed variant for legibility over the waveform); **decorative, non-interactive** (no
hit-test, no hover). Respects the speed constraint (no animation).
**(d) Selection restyle (tertiary border):**
- [ ] `drawThumbnail`: a selected card draws the **normal cell fill** (Rest/Hover surface, not
`InteractionState::Active`) + an **`accent/tertiary` border** (pastel purple `#C2AAE8`); the
waveform draws in its normal accent color (drop the inverted `bg/base` wave). Focus ring, hover
state, and drag-target highlight stay **distinct** from the selection border (keep them
coherent — spec the four states' visual separation in CONTEXT.md §L7).
## Phase L — sequencing
```
L1 (shared kit) ──► L2 (dock-panel layout redesign) [LANDED]
@@ -512,10 +383,10 @@ L1 (shared kit) ──► L2 (dock-panel layout redesign) [LANDED]
└─────────► L4 (dock-panel button layout enhancement) [LANDED]
└────► L5 (dock-panel button refinements; ungated, after L4) [LANDED]
└────► L6 (toolbar polish; ungated, after L5) [LANDED]
└────► L7 (capture ordering + card metadata + selection styling; ungated, after L6)
└────► L7 (capture ordering + card metadata + selection styling; ungated, after L6) [LANDED]
```
L1, L2, L4, L5, and L6 have landed. L7 is the next ungated dock-panel work. L3 waits for
Phase S to reach dev. Phase S feature work runs in parallel, ungated by Phase L.
L1, L2, L4, L5, L6, and L7 have all landed. L3 waits for Phase S to reach dev. Phase S
feature work runs in parallel, ungated by Phase L.
## Phase L — must-verify-before-build
- **LICE design-kit surfaces (L1)** — `LICE_GradRect`, `LICE_RoundRect`, AA
@@ -540,16 +411,11 @@ Phase S to reach dev. Phase S feature work runs in parallel, ungated by Phase L.
pure module; sourced from the registered action phrase, prefix stripped at draw time); item-move
and track-tag action ids confirmed; active-mode read confirmed via the same
`view().activeModeId()` the footer toggle uses.
- **L7 model + tempo surface (L7)** — forks F1/F2/F3 **all settled 2026-07-27** (see the §L7
forks block above and CONTEXT.md §L7): position carrier = per-`Bank` id→slot map in `bank_book`
(plain interchangeable substrate, `bank_model`/`Sample` untouched); F1 = capture-time signature
stamp; F3 = drag precedence + REAPER-idiomatic cursor cues + Alt-replace-over-occupied with the
stated pool guard. **Remaining build-time verification (SDK/API, not product decisions):**
(1) the REAPER meter-read API for the F1 stamp (`TimeMap_GetTimeSigAtTime` or equivalent) —
verify signature/argument order against the SDK; (2) the Win32/SWELL `SetCursor` path and cursor
resource loading/synthesis for the drop-result cues — confirm the cursor idiom against SWELL and
REAPER's own action cursors; (3) confirm the existing remove-from-bank pool-privilege guard is
reusable as-is for the Alt-replace index-removal step.
- **L7 model + tempo surface (L7)** — **RESOLVED at build (L7 landed).** Forks F1/F2/F3 all
confirmed: `TimeMap_GetTimeSigAtTime` confirmed at build for the meter stamp; SWELL stock
cursors chosen for drop-result cues (Reorder→IDC_SIZEALL, Move→IDC_HAND, Copy→IDC_UPARROW,
Replace→IDC_SIZEWE); existing pool-privilege guard reused as-is for Alt-replace. Gap navigation
in the grid = skip gaps (arrow keys skip empty slots). See `COMPLETED.md` §L7.
---