docs: archive Phase L L7 to COMPLETED; reconcile CLAUDE.md + CONTEXT.md + PLAN.md
This commit is contained in:
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Repo identity and current state
|
## Repo identity and current state
|
||||||
|
|
||||||
The CMake project and binary are now named `reaper_reasampler`. This is **ReaSampler** — a per-project audio sample-bank capture tool. The MPE modules (`mpe_model`, `mpe_view`) have been removed. M0–M8 are complete (bank_model, peaks, capture offline+realtime, persist, bank_panel, insert, capture action family, RealtimeRecordBackend, tail T1+T2+T1-followons). Phase B multi-bank (B1–B5, B-cap), Phase D1/D2 Design View (D1–D5, D2-W1–W3-B), Phase V versioning/beta-channel (V1/V3, V4), M10 provenance + re-capture from source, Phase R Reclaim (R1–R3: prune-reconcile core, dry-run shell, guarded deletion + action + panel button), M11 in full (batch capture, action-button strip + keybinding labels, conform-on-insert verified-extant, native OS drag-out), Phase L L1 (shared LICE drawing kit: `theme`/palette module, `component_geometry` geometry helpers, `draw_kit` shell, GDI DrawText retired in `bank_panel`), Phase L L2 (dock-panel layout redesign: `action_bar` pure task-grouped layout module, full M11-aware button inventory placed by task cluster, `bank_panel` redesigned through the L1 kit), Phase L L4 (dock-panel button layout enhancement: three-zone layout — top capture/placement/maintenance toolbar, bottom Design-View tagging/switching toolbar, footer narrow-toggle + Tail-button + Prune; `footer_bar` pure module + `footer_bar_tests`; `ActionCluster::Tagging`/`Switching` in `action_bar`), Phase L L5 (dock-panel button refinements: top-bar overflow menu for Batch Items/Batch Razor/Capture RT via `overflow_menu` pure module; custom LICE-kit hover-delay tooltips via `tooltip` pure module; four opposite-mode Item/Track tag buttons + Show Both on the bottom toolbar; Toggle + Activate-Arrange/Design buttons removed; grouping spacing widened — `mode_enable` pure opposite-mode enablement predicate), and Phase L L6 (toolbar polish: single-row short-label button faces — keybinding micro sub-row removed from `ActionBarSlot`, keybinding now shown in hover tooltip as "`phrase — binding`"; Cancel RT moved from visible bar into the overflow menu — More (⋯) now lists Batch Items / Batch Razor / Capture RT / Cancel RT; visible top-bar cluster order Capture → Maintenance → Placement; toolbar height 40→28 px) have all landed. M9 slots deferred indefinitely. The *discipline* — pure REAPER-free testable core split from REAPER-facing shells — is **preserved** throughout.
|
The CMake project and binary are now named `reaper_reasampler`. This is **ReaSampler** — a per-project audio sample-bank capture tool. The MPE modules (`mpe_model`, `mpe_view`) have been removed. M0–M8 are complete (bank_model, peaks, capture offline+realtime, persist, bank_panel, insert, capture action family, RealtimeRecordBackend, tail T1+T2+T1-followons). Phase B multi-bank (B1–B5, B-cap), Phase D1/D2 Design View (D1–D5, D2-W1–W3-B), Phase V versioning/beta-channel (V1/V3, V4), M10 provenance + re-capture from source, Phase R Reclaim (R1–R3: prune-reconcile core, dry-run shell, guarded deletion + action + panel button), M11 in full (batch capture, action-button strip + keybinding labels, conform-on-insert verified-extant, native OS drag-out), Phase L L1 (shared LICE drawing kit: `theme`/palette module, `component_geometry` geometry helpers, `draw_kit` shell, GDI DrawText retired in `bank_panel`), Phase L L2 (dock-panel layout redesign: `action_bar` pure task-grouped layout module, full M11-aware button inventory placed by task cluster, `bank_panel` redesigned through the L1 kit), Phase L L4 (dock-panel button layout enhancement: three-zone layout — top capture/placement/maintenance toolbar, bottom Design-View tagging/switching toolbar, footer narrow-toggle + Tail-button + Prune; `footer_bar` pure module + `footer_bar_tests`; `ActionCluster::Tagging`/`Switching` in `action_bar`), Phase L L5 (dock-panel button refinements: top-bar overflow menu for Batch Items/Batch Razor/Capture RT via `overflow_menu` pure module; custom LICE-kit hover-delay tooltips via `tooltip` pure module; four opposite-mode Item/Track tag buttons + Show Both on the bottom toolbar; Toggle + Activate-Arrange/Design buttons removed; grouping spacing widened — `mode_enable` pure opposite-mode enablement predicate), Phase L L6 (toolbar polish: single-row short-label button faces — keybinding micro sub-row removed from `ActionBarSlot`, keybinding now shown in hover tooltip as "`phrase — binding`"; Cancel RT moved from visible bar into the overflow menu — More (⋯) now lists Batch Items / Batch Razor / Capture RT / Cancel RT; visible top-bar cluster order Capture → Maintenance → Placement; toolbar height 40→28 px), and Phase L L7 (capture ordering, card metadata, and selection styling: per-bank `SlotMap` (id→slot) in `bank_book` — gap-preserving persisted display order, `reorderSample`/`replaceSample`/`orderedSampleIds`/`reconcileSlots` mutators; `captureTimeSigNum`/`captureTimeSigDenom` stamped on `Sample` at capture via `TimeMap_GetTimeSigAtTime`; `card_drag` pure module — gesture precedence + SWELL stock cursor cues; `card_meta` pure module — bars.beats.subdivisions + s.ms formatters; `bank_panel` sparse-grid rendering with gap cells, drop dispatch + cursor cues, metadata overlay, tertiary-border selection) have all landed. M9 slots deferred indefinitely. The *discipline* — pure REAPER-free testable core split from REAPER-facing shells — is **preserved** throughout.
|
||||||
|
|
||||||
CONTEXT.md is the authoritative spec and build roadmap. Read it first for any non-trivial task. Every REAPER API name cited there is correct-by-intent; verify argument order, types, and flag values against `vendor/reaper-sdk/sdk/reaper_plugin_functions.h` before use.
|
CONTEXT.md is the authoritative spec and build roadmap. Read it first for any non-trivial task. Every REAPER API name cited there is correct-by-intent; verify argument order, types, and flag values against `vendor/reaper-sdk/sdk/reaper_plugin_functions.h` before use.
|
||||||
|
|
||||||
@@ -49,6 +49,8 @@ Key targets (see CMakeLists.txt for the full list):
|
|||||||
| `overflow_menu_tests` | executable | Pure unit tests for `overflow_menu` — no REAPER, no DAW. |
|
| `overflow_menu_tests` | executable | Pure unit tests for `overflow_menu` — no REAPER, no DAW. |
|
||||||
| `mode_enable_tests` | executable | Pure unit tests for `mode_enable` — no REAPER, no DAW. |
|
| `mode_enable_tests` | executable | Pure unit tests for `mode_enable` — no REAPER, no DAW. |
|
||||||
| `tooltip_tests` | executable | Pure unit tests for `tooltip` — no REAPER, no DAW. |
|
| `tooltip_tests` | executable | Pure unit tests for `tooltip` — no REAPER, no DAW. |
|
||||||
|
| `card_drag_tests` | executable | Pure unit tests for `card_drag` — no REAPER, no DAW. |
|
||||||
|
| `card_meta_tests` | executable | Pure unit tests for `card_meta` — no REAPER, no DAW. |
|
||||||
| `reaper_reasampler` | loadable module | The actual extension binary (`.dll` / `.dylib` / `.so`). |
|
| `reaper_reasampler` | loadable module | The actual extension binary (`.dll` / `.dylib` / `.so`). |
|
||||||
|
|
||||||
### Beta channel build (Phase V, V4)
|
### Beta channel build (Phase V, V4)
|
||||||
@@ -75,12 +77,12 @@ There is no hot-reload. Copy the built binary into REAPER's `UserPlugins/` folde
|
|||||||
## Architecture: the load-bearing split
|
## Architecture: the load-bearing split
|
||||||
|
|
||||||
**Pure core (no REAPER types, unit-testable outside the DAW):**
|
**Pure core (no REAPER types, unit-testable outside the DAW):**
|
||||||
- `bank_model` — `Sample` metadata struct + `BankIndex` (add/remove/query/tier/dedup-by-hash + JSON round-trip). Test it hard — it is the heart.
|
- `bank_model` — `Sample` metadata struct + `BankIndex` (add/remove/query/tier/dedup-by-hash + JSON round-trip). Test it hard — it is the heart. `Sample` carries `captureTimeSigNum`/`captureTimeSigDenom` (L7: stamped at capture via `TimeMap_GetTimeSigAtTime`, refreshed on re-capture).
|
||||||
- `peaks` — waveform min/max bin computation from raw PCM. Fed a known signal, asserts envelope. Does not depend on REAPER's peak API.
|
- `peaks` — waveform min/max bin computation from raw PCM. Fed a known signal, asserts envelope. Does not depend on REAPER's peak API.
|
||||||
- `view_mode_model` — Design View mode system: mode registry, GUID-keyed membership, folder-tree-aware visibility derivation, snapshot-based park/restore planner, JSON round-trip. Mirror of `bank_model` for the Design View phase.
|
- `view_mode_model` — Design View mode system: mode registry, GUID-keyed membership, folder-tree-aware visibility derivation, snapshot-based park/restore planner, JSON round-trip. Mirror of `bank_model` for the Design View phase.
|
||||||
- `view_tree` — pure `I_FOLDERDEPTH`→FolderTree helper for the Design View shell; no REAPER types at the boundary.
|
- `view_tree` — pure `I_FOLDERDEPTH`→FolderTree helper for the Design View shell; no REAPER types at the boundary.
|
||||||
- `mode_switch` — REAPER-free segment layout + hit-test math for the bank_panel's Design View mode switch; divides a header rectangle into N equal segments and hit-tests a point to a segment. Mirror of `bank_grid`.
|
- `mode_switch` — REAPER-free segment layout + hit-test math for the bank_panel's Design View mode switch; divides a header rectangle into N equal segments and hit-tests a point to a segment. Mirror of `bank_grid`.
|
||||||
- `bank_book` — multi-bank registry (Phase B): an ordered set of banks (pool seeded as bank-zero + named banks), each wrapping a `BankIndex`. Owns create/rename/reorder/delete of named banks, pool privileges (un-deletable/un-renamable/un-evacuable, never zero banks) enforced in-model, active-bank id, index-only move/copy/remove of a sample between or from banks, `hashReferencedElsewhere` cross-bank reference query, JSON round-trip + legacy-`bank_index`→pool migration. Wraps `BankIndex` (bank_model untouched; no `bankId` on `Sample`).
|
- `bank_book` — multi-bank registry (Phase B): an ordered set of banks (pool seeded as bank-zero + named banks), each wrapping a `BankIndex`. Owns create/rename/reorder/delete of named banks, pool privileges (un-deletable/un-renamable/un-evacuable, never zero banks) enforced in-model, active-bank id, index-only move/copy/remove of a sample between or from banks, `hashReferencedElsewhere` cross-bank reference query, JSON round-trip + legacy-`bank_index`→pool migration. Wraps `BankIndex` (bank_model untouched; no `bankId` on `Sample`). L7 adds: per-`Bank` gap-preserving **`SlotMap`** (id→slot) for deterministic display order — persisted inside the existing `"banks"` blob; `reorderSample` (insert-before-shift; same-slot = no-op), `replaceSample` (occupant index-removal via standard remove path + pool-guard inheritance; no-op on reject), `orderedSampleIds`, `reconcileSlots` (pre-L7 migration seeds dense insertion order on load).
|
||||||
- `owned_manifest` — owned-file manifest seam (Phase B B-cap): the set of project-relative files the capture path itself created, persisted under the `"owned_files"` ext-state key, so Phase R prune can distinguish the bank system's own orphans from hand-dropped files. Deliberately decoupled from `bank_book` — tracks files created, not index membership. Phase R (R1/R2) consumes it; no prune logic here.
|
- `owned_manifest` — owned-file manifest seam (Phase B B-cap): the set of project-relative files the capture path itself created, persisted under the `"owned_files"` ext-state key, so Phase R prune can distinguish the bank system's own orphans from hand-dropped files. Deliberately decoupled from `bank_book` — tracks files created, not index membership. Phase R (R1/R2) consumes it; no prune logic here.
|
||||||
- `app_version` — REAPER-free version/channel identity (Phase V, V1+V4): CMake-sourced semver constant (`appVersion()`), ext-state stamp value (`stampVersion()` — numeric triple only, no channel suffix), `parseVersion`/`versionLess`/`classifyWritingVersion`, and the full set of channel-derived identity accessors (`extStateNamespace()`, `commandIdPrefix()`, `actionDisplayPrefix()`, `binaryName()`, `dockTitle()`, `dockIdent()`, `channelCommandId()`, `channelActionName()`). All channel strings derive from the one `REASAMPLER_CHANNEL_IS_BETA` bit threaded in via `configure_file` → `version_generated.h`; no scattered `#ifdef`s in the shells.
|
- `app_version` — REAPER-free version/channel identity (Phase V, V1+V4): CMake-sourced semver constant (`appVersion()`), ext-state stamp value (`stampVersion()` — numeric triple only, no channel suffix), `parseVersion`/`versionLess`/`classifyWritingVersion`, and the full set of channel-derived identity accessors (`extStateNamespace()`, `commandIdPrefix()`, `actionDisplayPrefix()`, `binaryName()`, `dockTitle()`, `dockIdent()`, `channelCommandId()`, `channelActionName()`). All channel strings derive from the one `REASAMPLER_CHANNEL_IS_BETA` bit threaded in via `configure_file` → `version_generated.h`; no scattered `#ifdef`s in the shells.
|
||||||
- `wav_trim` — 32-bit-float WAV parse + header-aware truncate plan (RIFF/data size rewrite) for the realtime tail's PCM decay-scan trim (T2). Rejects WAVE_FORMAT_EXTENSIBLE with non-float SubFormat GUID. Depends on `peaks` for the `AudioSample` float alias.
|
- `wav_trim` — 32-bit-float WAV parse + header-aware truncate plan (RIFF/data size rewrite) for the realtime tail's PCM decay-scan trim (T2). Rejects WAVE_FORMAT_EXTENSIBLE with non-float SubFormat GUID. Depends on `peaks` for the `AudioSample` float alias.
|
||||||
@@ -97,12 +99,14 @@ There is no hot-reload. Copy the built binary into REAPER's `UserPlugins/` folde
|
|||||||
- `overflow_menu` — Phase L pure overflow-menu-button geometry/reserve/hit-test (L5): right-anchored More (⋯) button rect computation and hit-test for the top-toolbar overflow. No LICE or REAPER types. Mirror of `prune_button`.
|
- `overflow_menu` — Phase L pure overflow-menu-button geometry/reserve/hit-test (L5): right-anchored More (⋯) button rect computation and hit-test for the top-toolbar overflow. No LICE or REAPER types. Mirror of `prune_button`.
|
||||||
- `mode_enable` — Phase L pure opposite-mode enablement predicate (L5): given the active mode, computes per-button live/disabled state for the four Item/Track × Arrange/Design tag buttons on the bottom toolbar. No LICE or REAPER types.
|
- `mode_enable` — Phase L pure opposite-mode enablement predicate (L5): given the active mode, computes per-button live/disabled state for the four Item/Track × Arrange/Design tag buttons on the bottom toolbar. No LICE or REAPER types.
|
||||||
- `tooltip` — Phase L pure tooltip placement + prefix-strip (L5): custom LICE-kit hover-delay tooltip geometry and text derivation — strips the `ReaSampler:` display prefix from the registered action phrase; tooltip box width clamped to the client rect. No LICE or REAPER types.
|
- `tooltip` — Phase L pure tooltip placement + prefix-strip (L5): custom LICE-kit hover-delay tooltip geometry and text derivation — strips the `ReaSampler:` display prefix from the registered action phrase; tooltip box width clamped to the client rect. No LICE or REAPER types.
|
||||||
|
- `card_drag` — Phase L pure drag-gesture precedence + slot hit-test (L7): leave-client → OS drag-out; other-bank → move/copy; same-bank → reorder / Alt-over-occupied → replace. Returns the resolved gesture so the shell can map it to a SWELL stock cursor cue. Sparse `computeSlotRects` / `hitTestSlot`. No LICE or REAPER types. Mirror of `drag_out` / `prune_button`.
|
||||||
|
- `card_meta` — Phase L pure card-metadata formatters (L7): bars.beats.subdivisions from `lengthSeconds` + `captureTempo` + capture-time-signature stamp; seconds.milliseconds rounded. Both blank when the sample is unstamped. No LICE or REAPER types.
|
||||||
|
|
||||||
**REAPER-facing shells:**
|
**REAPER-facing shells:**
|
||||||
- `capture` — `ICaptureBackend` interface; `OfflineRenderBackend` (deterministic default) and `RealtimeRecordBackend`. Input: `CaptureRequest`. Output: finished file + populated `Sample` handed to `bank_model`.
|
- `capture` — `ICaptureBackend` interface; `OfflineRenderBackend` (deterministic default) and `RealtimeRecordBackend`. Input: `CaptureRequest`. Output: finished file + populated `Sample` handed to `bank_model`.
|
||||||
- `insert` — placement via `InsertMedia`; conform-to-project-tempo is an explicit opt-in flag, never silent stretching.
|
- `insert` — placement via `InsertMedia`; conform-to-project-tempo is an explicit opt-in flag, never silent stretching.
|
||||||
- `bank_panel` — docked LICE-drawn grid: thumbnails, audition, multi-select, keyboard navigation; three-zone layout (L4/L5/L6): **top toolbar** = visible bar in cluster order Capture (Capture Item, Capture Track) → Maintenance (Re-capture) → Placement (Insert, Insert Conform) via `action_bar`, each button showing a single-row short label, firing via `NamedCommandLookup` + `Main_OnCommand`; a right-anchored **More (⋯) overflow menu button** (`overflow_menu`) opens a `TrackPopupMenu` listing Batch Items / Batch Razor / Capture RT / Cancel RT; **hover tooltips** (`tooltip`) show the full action name with the `ReaSampler:` prefix stripped plus the live keybinding ("`phrase — binding`" when bound, bare phrase when unbound), sourced from the registered action phrase + `kbd_getTextFromCmd`; toolbar height 28 px; **bottom toolbar** = four opposite-mode tag buttons (Item: Arrange / Item: Design / Track: Arrange / Track: Design — live only for the opposite of the active mode, `Disabled` otherwise via `mode_enable`) + set-apart Show Both (`VIEW_SHOW_BOTH`); Toggle and Activate-Arrange/Activate-Design buttons removed (actions stay registered); grouping `clusterGap` 24 / `buttonGap` 4; **footer** = narrow `[Arrange|Design]` toggle · per-mode count · Tail button (cycles tail on click, proper kit button states) · set-apart `warn` Prune (rightmost) via `footer_bar`; entire panel draws through the L1 kit by palette role with hover on interactive elements, single `KitColor→LICE_pixel` boundary via `draw_kit`; OS drag-out hook (M11) initiating an OS-level copy drag when the pointer leaves the panel client rect, via `drag_out` + `drag_out_win`.
|
- `bank_panel` — docked LICE-drawn grid: thumbnails, audition, multi-select, keyboard navigation; three-zone layout (L4/L5/L6): **top toolbar** = visible bar in cluster order Capture (Capture Item, Capture Track) → Maintenance (Re-capture) → Placement (Insert, Insert Conform) via `action_bar`, each button showing a single-row short label, firing via `NamedCommandLookup` + `Main_OnCommand`; a right-anchored **More (⋯) overflow menu button** (`overflow_menu`) opens a `TrackPopupMenu` listing Batch Items / Batch Razor / Capture RT / Cancel RT; **hover tooltips** (`tooltip`) show the full action name with the `ReaSampler:` prefix stripped plus the live keybinding ("`phrase — binding`" when bound, bare phrase when unbound), sourced from the registered action phrase + `kbd_getTextFromCmd`; toolbar height 28 px; **bottom toolbar** = four opposite-mode tag buttons (Item: Arrange / Item: Design / Track: Arrange / Track: Design — live only for the opposite of the active mode, `Disabled` otherwise via `mode_enable`) + set-apart Show Both (`VIEW_SHOW_BOTH`); Toggle and Activate-Arrange/Activate-Design buttons removed (actions stay registered); grouping `clusterGap` 24 / `buttonGap` 4; **footer** = narrow `[Arrange|Design]` toggle · per-mode count · Tail button (cycles tail on click, proper kit button states) · set-apart `warn` Prune (rightmost) via `footer_bar`; entire panel draws through the L1 kit by palette role with hover on interactive elements, single `KitColor→LICE_pixel` boundary via `draw_kit`; OS drag-out hook (M11) initiating an OS-level copy drag when the pointer leaves the panel client rect, via `drag_out` + `drag_out_win`. L7 adds: **grid renders in sparse slot order** (decorative empty-gap cells for unoccupied slots; every cell↔sample consumer remapped to id-based occupied-ordinal space — selection, keyboard nav with gap-skip arrows, audition, multi-select, delete/re-capture resolution, drags); **drop dispatch** (reorder / Alt-replace / move-copy) with one-Ctrl-Z undo, per-slot drop highlight (`accent/hot`, doubled outline for replace), SWELL stock cursor cues via `SetCursor` per the `card_drag` resolved gesture; **metadata overlay** (bars.beats bottom-left Micro, s.ms bottom-right ValueMono, `TextDim`, decorative); **selection = normal cell + `accent/tertiary` purple border**, inversion removed, focus ring distinct.
|
||||||
- `persist` — project ext state (`SetProjExtState` / `GetProjExtState`, namespace `"reasampler"`) ↔ `BankBook` JSON (`"banks"` key) + `ViewModeModel` JSON (`"view_state"` key) + `TailSetting` JSON (`"tail_setting"` key) + `OwnedManifest` JSON (`"owned_files"` key) + writing-version stamp (`"version"` key, written via `stampVersion()` on every `saveToActiveProject()`); project-relative path resolution. A `projectconfig` hook (`BeginLoadProjectState(isUndo)`) triggers a deferred session reload on undo/redo so Ctrl-Z/redo visibly restores book/view/tail/manifest in-session. Hosts `ReaSamplerSession::pruneDryRun()` (read-only orphan enumeration via M4 project-relative resolution) and `pruneOrphanSet()` (full-set query for the R3 delete path); supplies `referencedPaths()` + `owned().paths()` to the `prune_reconcile` pure core.
|
- `persist` — project ext state (`SetProjExtState` / `GetProjExtState`, namespace `"reasampler"`) ↔ `BankBook` JSON (`"banks"` key — L7: per-`Bank` `SlotMap` rides inside this blob; a pre-L7 blob with no slot data migrates via `reconcileSlots()` to dense insertion order on load) + `ViewModeModel` JSON (`"view_state"` key) + `TailSetting` JSON (`"tail_setting"` key) + `OwnedManifest` JSON (`"owned_files"` key) + writing-version stamp (`"version"` key, written via `stampVersion()` on every `saveToActiveProject()`); project-relative path resolution. A `projectconfig` hook (`BeginLoadProjectState(isUndo)`) triggers a deferred session reload on undo/redo so Ctrl-Z/redo visibly restores book/view/tail/manifest in-session. Hosts `ReaSamplerSession::pruneDryRun()` (read-only orphan enumeration via M4 project-relative resolution) and `pruneOrphanSet()` (full-set query for the R3 delete path); supplies `referencedPaths()` + `owned().paths()` to the `prune_reconcile` pure core.
|
||||||
- `view` — Design View shell: reads the folder tree via `view_tree`, snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (`B_SHOWINTCP`/`B_SHOWINMIXER`/`B_MAINSEND`/`I_FXEN` + per-FX offline) and derived visibility on parents; restores from snapshot. Never touches master or `B_MUTE`/`I_SOLO`.
|
- `view` — Design View shell: reads the folder tree via `view_tree`, snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (`B_SHOWINTCP`/`B_SHOWINMIXER`/`B_MAINSEND`/`I_FXEN` + per-FX offline) and derived visibility on parents; restores from snapshot. Never touches master or `B_MUTE`/`I_SOLO`.
|
||||||
- `track_guid` — shared `MediaTrack*` → canonical GUID-string formatter; single source of truth for membership keys used by both the view shell and the actions layer.
|
- `track_guid` — shared `MediaTrack*` → canonical GUID-string formatter; single source of truth for membership keys used by both the view shell and the actions layer.
|
||||||
- `provenance_shell` — FX-chain identity queries via `TrackFX_*` / `TakeFX_*` APIs; collects source-item paths and parent-detection inputs to feed the pure `provenance` fingerprint builder. Stamps `Sample.provenance` on capture when every resolving source item maps by exact normalized path (case-folded on Windows) to exactly one bank sample; ambiguous/mixed cases record nothing conservatively.
|
- `provenance_shell` — FX-chain identity queries via `TrackFX_*` / `TakeFX_*` APIs; collects source-item paths and parent-detection inputs to feed the pure `provenance` fingerprint builder. Stamps `Sample.provenance` on capture when every resolving source item maps by exact normalized path (case-folded on Windows) to exactly one bank sample; ambiguous/mixed cases record nothing conservatively.
|
||||||
|
|||||||
@@ -1423,3 +1423,61 @@ targets, no capture/placement behavior change.
|
|||||||
|
|
||||||
**Notes/decisions:**
|
**Notes/decisions:**
|
||||||
- Icons were considered and deferred (not implemented in this pass).
|
- Icons were considered and deferred (not implemented in this pass).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## L7 — capture ordering, card metadata, and selection styling
|
||||||
|
**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 via a
|
||||||
|
per-`Bank` id→slot `SlotMap` in `bank_book`; a card may sit in a slot that leaves earlier slots
|
||||||
|
empty (gaps preserved; trailing empty tail trimmed for scroll extent). (2) **Decorative metadata
|
||||||
|
over the peaks:** each card overlays capture length as **bars.beats.subdivisions (bottom-left)**
|
||||||
|
and **seconds.ms (bottom-right)** in the kit's micro / value-mono type class, `text/dim`. (3)
|
||||||
|
**Selection restyle:** a selected card drops the inverted accent-fill and instead draws the
|
||||||
|
*normal* cell + an **`accent/tertiary` (pastel purple `#C2AAE8`) border**. No new capture /
|
||||||
|
placement behavior; the "capture ≠ placement" principle is untouched.
|
||||||
|
|
||||||
|
- [x] **`SlotMap` in `bank_book`:** gap-preserving per-`Bank` id→slot map — persisted
|
||||||
|
deterministic display order; interior gaps preserved / trailing tail trimmed; JSON rides
|
||||||
|
inside the existing `"banks"` blob; pre-L7 migration seeds dense insertion order via
|
||||||
|
`reconcileSlots()` on the load path. `bank_model` / `Sample` untouched by position (position
|
||||||
|
is a per-bank display concern).
|
||||||
|
- [x] **`BankBook` mutators:** `reorderSample` (insert-before-shift; same-slot = no-op),
|
||||||
|
`replaceSample` (occupant index-removal via the standard remove path + pool-guard inheritance;
|
||||||
|
no-op on reject), `orderedSampleIds`, `reconcileSlots`. All gap-preserving; deterministic;
|
||||||
|
CTest-covered.
|
||||||
|
- [x] **`Sample` meter stamp (ONE sanctioned `Sample` change):** `captureTimeSigNum` /
|
||||||
|
`captureTimeSigDenom` added to `Sample` + JSON round-trip; stamped on both capture paths and
|
||||||
|
refreshed on re-capture via `TimeMap_GetTimeSigAtTime` (API confirmed at build). Old samples
|
||||||
|
with no stamp fall back gracefully (blank musical read-out).
|
||||||
|
- [x] **`card_drag` pure module:** gesture precedence — leave-client → OS drag-out;
|
||||||
|
other-bank → move/copy; same-bank → reorder / Alt-over-occupied → replace. Cursor-cue map
|
||||||
|
returned as the pure resolved gesture. Sparse `computeSlotRects` / `hitTestSlot`. SWELL stock
|
||||||
|
cursors chosen at build: Reorder→`IDC_SIZEALL`, Move→`IDC_HAND`, Copy→`IDC_UPARROW`,
|
||||||
|
Replace→`IDC_SIZEWE`. New CTest target `card_drag_tests`.
|
||||||
|
- [x] **`card_meta` pure module:** bars.beats.subdivisions from the stamped tempo + meter;
|
||||||
|
seconds.milliseconds rounded. Both blank when the sample is unstamped. New CTest target
|
||||||
|
`card_meta_tests`.
|
||||||
|
- [x] **`bank_panel` sparse-grid render:** grid renders in sparse slot order; decorative
|
||||||
|
empty-gap cells drawn for unoccupied slots; every cell↔sample consumer remapped to
|
||||||
|
id-based occupied-ordinal space (selection, keyboard nav — arrows skip gaps, audition,
|
||||||
|
multi-select, delete / re-capture resolution, drags).
|
||||||
|
- [x] **`bank_panel` drop dispatch:** reorder / Alt-replace / move-copy drop with one-Ctrl-Z
|
||||||
|
undo via the existing batched undo pattern; per-slot drop highlight (`accent/hot`, doubled
|
||||||
|
outline for replace); SWELL stock cursor cues via `SetCursor` per the pure resolved gesture.
|
||||||
|
- [x] **`bank_panel` metadata overlay:** bars.beats bottom-left (Micro), s.ms bottom-right
|
||||||
|
(ValueMono), `TextDim`, decorative / non-interactive.
|
||||||
|
- [x] **Selection restyle:** normal cell + `accent/tertiary` purple border; inversion removed;
|
||||||
|
focus ring distinct from the selection border.
|
||||||
|
|
||||||
|
**Notes/decisions:**
|
||||||
|
- **M9 overlap (awareness note — unchanged intent).** M9 (capture-to-slot-N / insert-slot-N,
|
||||||
|
MIDI-bindable, MPC-style) remains **explicitly deferred (Daniel, 2026-07-26).** The
|
||||||
|
interchangeable-slot substrate L7 builds still eases a future M9 revival but L7 adds **no**
|
||||||
|
slot-numbered capture/insert actions and **no** MIDI bindings. The "plain vs. M9-shaped"
|
||||||
|
sub-fork is closed: plain gap-preserving substrate (F2).
|
||||||
|
- **Build-time choices confirmed:** `TimeMap_GetTimeSigAtTime` confirmed at build for the
|
||||||
|
meter stamp; SWELL stock cursors chosen (no custom cursor load/synthesis required);
|
||||||
|
gap navigation = skip gaps (arrow keys skip empty slots); same-slot reorder = no-op.
|
||||||
|
- New CTest targets `card_drag_tests`, `card_meta_tests`.
|
||||||
|
|||||||
+15
-15
@@ -1688,9 +1688,8 @@ id→slot map) is settled at build; the *contract* below holds either way.
|
|||||||
slot** (append). It never fills an earlier gap automatically — a gap is a user's deliberate
|
slot** (append). It never fills an earlier gap automatically — a gap is a user's deliberate
|
||||||
layout, not a hole to be plugged.
|
layout, not a hole to be plugged.
|
||||||
- **Delete / remove / prune:** removing a sample **leaves its slot empty** (does not re-pack) so
|
- **Delete / remove / prune:** removing a sample **leaves its slot empty** (does not re-pack) so
|
||||||
every other sample keeps its position. Define at build whether a purely-empty trailing tail is
|
every other sample keeps its position. Trailing empty tail is trimmed for scroll-extent
|
||||||
trimmed for content-height purposes (the recommendation: trim trailing empties for scroll
|
purposes; interior gaps are preserved (confirmed at build).
|
||||||
extent, keep interior gaps).
|
|
||||||
- **Reorder:** the user drags a card to a target slot within its bank; the pure reorder mutator
|
- **Reorder:** the user drags a card to a target slot within its bank; the pure reorder mutator
|
||||||
moves that sample's position to the target slot, gap-preserving. **Drop-into-empty-slot places
|
moves that sample's position to the target slot, gap-preserving. **Drop-into-empty-slot places
|
||||||
there; drop-onto-occupied inserts-before and shifts the tail** (matching common file-manager
|
there; drop-onto-occupied inserts-before and shifts the tail** (matching common file-manager
|
||||||
@@ -1739,8 +1738,8 @@ kit draw is shell.
|
|||||||
requires a **tempo + time-signature** reference. `Sample` already carries `captureTempo` (BPM at
|
requires a **tempo + time-signature** reference. `Sample` already carries `captureTempo` (BPM at
|
||||||
capture) and `lengthBeats`, but **no time-signature.** **Decision: capture-time stamp** — add
|
capture) and `lengthBeats`, but **no time-signature.** **Decision: capture-time stamp** — add
|
||||||
`captureTimeSigNum` / `captureTimeSigDenom` to `Sample` + its JSON round-trip, stamped on the
|
`captureTimeSigNum` / `captureTimeSigDenom` to `Sample` + its JSON round-trip, stamped on the
|
||||||
capture path (read the project meter at capture via the REAPER meter API — verify
|
capture path (read the project meter at capture via `TimeMap_GetTimeSigAtTime` — confirmed
|
||||||
`TimeMap_GetTimeSigAtTime` / equivalent against the SDK at build). bars.beats.subdivisions renders
|
at build against the SDK). bars.beats.subdivisions renders
|
||||||
from the stamped tempo + meter, **stable under later project tempo/meter changes** — a bank sample
|
from the stamped tempo + meter, **stable under later project tempo/meter changes** — a bank sample
|
||||||
outlives the project state it was captured under, matching the existing `captureTempo` stamp
|
outlives the project state it was captured under, matching the existing `captureTempo` stamp
|
||||||
philosophy. This is a **capture-path write beyond draw work** (its own checkbox in the PLAN, now
|
philosophy. This is a **capture-path write beyond draw work** (its own checkbox in the PLAN, now
|
||||||
@@ -1812,8 +1811,8 @@ precisely:
|
|||||||
pool's rules forbid. The consistent rule the model enforces: **the replace's index-removal step
|
pool's rules forbid. The consistent rule the model enforces: **the replace's index-removal step
|
||||||
is the same operation as remove-from-bank, and it must pass the same pool-privilege guard that
|
is the same operation as remove-from-bank, and it must pass the same pool-privilege guard that
|
||||||
remove already applies — if remove-from-pool would be rejected for that sample, Alt+Replace over
|
remove already applies — if remove-from-pool would be rejected for that sample, Alt+Replace over
|
||||||
it is rejected too** (the drop falls back to a no-op or the default insert-shift; settle the
|
it is rejected too** (the drop falls back to a no-op; the pool-privilege guard is reused
|
||||||
exact rejection UX at build). No special pool-only replace path; one rule, guarded by the
|
as-is — confirmed at build). No special pool-only replace path; one rule, guarded by the
|
||||||
existing pool invariants.
|
existing pool invariants.
|
||||||
|
|
||||||
**Drop-result cursor cues (SETTLED 2026-07-27 — REAPER-idiomatic special cursors).** During a
|
**Drop-result cursor cues (SETTLED 2026-07-27 — REAPER-idiomatic special cursors).** During a
|
||||||
@@ -1828,12 +1827,13 @@ distinct action cursors. The cue set:
|
|||||||
occupied slot** (i.e. only when precedence resolves to the Alt+replace case). It must not appear
|
occupied slot** (i.e. only when precedence resolves to the Alt+replace case). It must not appear
|
||||||
over an empty slot or when Alt is not held.
|
over an empty slot or when Alt is not held.
|
||||||
|
|
||||||
**Shell mechanism:** the shell sets the cursor via Win32/SWELL `SetCursor` (REAPER ships its own
|
**Shell mechanism:** the shell sets the cursor via Win32/SWELL `SetCursor`. SWELL stock cursors
|
||||||
action-cursor idiom as the visual reference — match its feel; load or synthesize kit-consistent
|
were chosen at build (Reorder→`IDC_SIZEALL`, Move→`IDC_HAND`, Copy→`IDC_UPARROW`,
|
||||||
cursors at build). **The *decision* of which cue applies stays in the pure gesture/disambiguation
|
Replace→`IDC_SIZEWE`; no custom cursor load/synthesis required). **The *decision* of which cue
|
||||||
helper** — the same helper that resolves precedence returns the resolved gesture (reorder / move /
|
applies stays in the pure gesture/disambiguation helper** — the same helper that resolves
|
||||||
copy / os-drag-out / replace), and the shell maps that pure result to a cursor. No cue logic in
|
precedence returns the resolved gesture (reorder / move / copy / os-drag-out / replace), and the
|
||||||
the shell; the shell only owns the `SetCursor` call and the cursor resources.
|
shell maps that pure result to a cursor. No cue logic in the shell; the shell only owns the
|
||||||
|
`SetCursor` call and the cursor resources.
|
||||||
|
|
||||||
### Pure/shell discipline (L7)
|
### Pure/shell discipline (L7)
|
||||||
|
|
||||||
@@ -1856,8 +1856,8 @@ editor (`IPlugView` LICE surface), the S6 embed strip, and the keyboard strip li
|
|||||||
Phase S, which is **not on dev yet** (it exists on the phase-s worktree). L3 cannot be
|
Phase S, which is **not on dev yet** (it exists on the phase-s worktree). L3 cannot be
|
||||||
built on dev until Phase S's editor/embed surfaces (≈ S1 / S6 / S10) merge to dev — **the
|
built on dev until Phase S's editor/embed surfaces (≈ S1 / S6 / S10) merge to dev — **the
|
||||||
Phase L team must not chase these files on dev; they are not there.** Until then L3 is a
|
Phase L team must not chase these files on dev; they are not there.** Until then L3 is a
|
||||||
planned, blocked point; L1, L2, L4, and **L5 have all landed** — see `COMPLETED.md`. L4 and L5
|
planned, blocked point; L1, L2, L4, L5, L6, and **L7 have all landed** — see `COMPLETED.md`.
|
||||||
are independent of the L3 gate.
|
L4, L5, L6, and L7 are independent of the L3 gate.
|
||||||
|
|
||||||
**Coordination contract (load-bearing):** Phase S's S10–S13 build their interaction UX with
|
**Coordination contract (load-bearing):** Phase S's S10–S13 build their interaction UX with
|
||||||
the **current** drawing and **adopt the L1 kit when it is available — they are NOT gated on
|
the **current** drawing and **adopt the L1 kit when it is available — they are NOT gated on
|
||||||
|
|||||||
@@ -285,7 +285,8 @@ build:** the `IReaperUIEmbedInterface` contract + embed message/lifecycle agains
|
|||||||
> moves it to `COMPLETED.md`.
|
> moves it to `COMPLETED.md`.
|
||||||
>
|
>
|
||||||
> **L1 (shared LICE drawing kit — the foundation), L2 (dock-panel layout redesign), L4
|
> **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
|
> `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
|
> `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
|
> 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),
|
> `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
|
> 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
|
> 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
|
> widened (`mode_enable` pure module) (L5); single-row button faces, keybinding in tooltip,
|
||||||
> Phase S landing on dev.
|
> 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
|
> **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
|
> 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
|
state — never a pulse, per the speed constraint) as the signature surface; VST3 class UID
|
||||||
unchanged. (Gated.)
|
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
|
## Phase L — sequencing
|
||||||
```
|
```
|
||||||
L1 (shared kit) ──► L2 (dock-panel layout redesign) [LANDED]
|
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]
|
└─────────► L4 (dock-panel button layout enhancement) [LANDED]
|
||||||
└────► L5 (dock-panel button refinements; ungated, after L4) [LANDED]
|
└────► L5 (dock-panel button refinements; ungated, after L4) [LANDED]
|
||||||
└────► L6 (toolbar polish; ungated, after L5) [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
|
L1, L2, L4, L5, L6, and L7 have all landed. L3 waits for Phase S to reach dev. Phase S
|
||||||
Phase S to reach dev. Phase S feature work runs in parallel, ungated by Phase L.
|
feature work runs in parallel, ungated by Phase L.
|
||||||
|
|
||||||
## Phase L — must-verify-before-build
|
## Phase L — must-verify-before-build
|
||||||
- **LICE design-kit surfaces (L1)** — `LICE_GradRect`, `LICE_RoundRect`, AA
|
- **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
|
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
|
and track-tag action ids confirmed; active-mode read confirmed via the same
|
||||||
`view().activeModeId()` the footer toggle uses.
|
`view().activeModeId()` the footer toggle uses.
|
||||||
- **L7 model + tempo surface (L7)** — forks F1/F2/F3 **all settled 2026-07-27** (see the §L7
|
- **L7 model + tempo surface (L7)** — **RESOLVED at build (L7 landed).** Forks F1/F2/F3 all
|
||||||
forks block above and CONTEXT.md §L7): position carrier = per-`Bank` id→slot map in `bank_book`
|
confirmed: `TimeMap_GetTimeSigAtTime` confirmed at build for the meter stamp; SWELL stock
|
||||||
(plain interchangeable substrate, `bank_model`/`Sample` untouched); F1 = capture-time signature
|
cursors chosen for drop-result cues (Reorder→IDC_SIZEALL, Move→IDC_HAND, Copy→IDC_UPARROW,
|
||||||
stamp; F3 = drag precedence + REAPER-idiomatic cursor cues + Alt-replace-over-occupied with the
|
Replace→IDC_SIZEWE); existing pool-privilege guard reused as-is for Alt-replace. Gap navigation
|
||||||
stated pool guard. **Remaining build-time verification (SDK/API, not product decisions):**
|
in the grid = skip gaps (arrow keys skip empty slots). See `COMPLETED.md` §L7.
|
||||||
(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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user