docs: settle Design View two-canvas sub-phase (fixed-lane per-item modes)

Item-level mode separation via REAPER fixed lanes; new content auto-tags to
the active mode; inactive lanes hidden and silenced; a managed-vs-manual lane
index keeps toggles off user comp lanes; capture placement mode-aware.
Precision invariants unaffected; REAPER 7 floor.
This commit is contained in:
2026-07-23 16:32:58 -04:00
parent 3791e6c119
commit 1688de553b
2 changed files with 504 additions and 13 deletions
+164 -5
View File
@@ -184,9 +184,27 @@ are **Arrange** mode (the default). Toggling to a mode **hides and disables** th
tracks that don't belong to it. Workflow value: mental separation + clutter
elimination — be in Design view, resample into the bank, flip to Arrange, place it.
**This is not a separate canvas.** REAPER has exactly one arrange timeline. Design
View is the *same timeline* with a curated, filtered track set and the inactive
tracks parked — not a second surface, window, or duplicated project.
Design View is a **mode projection over REAPER's single arrange timeline**
reaching both **tracks** (parked per mode) and **items** (lane-split per mode on
shared tracks). It approximates two canvases without a second surface: same project,
same timeline, but each stance sees its own tracks and its own items. It **never**
duplicates the project or opens a second window.
> **Two-canvas reach — settled (2026-07-23).** The original single-canvas framing
> here was derived from a REAPER constraint, not chosen as a product stance. Daniel
> reopened it — "as close as possible to two separate design and arrange canvases,
> same project, different items and leaves" — and has now signed off on the
> mechanism and resolved all forks. The "different leaves" half is delivered by
> track-parking (D1). The "different items" half is delivered by **per-item mode
> membership via REAPER fixed lanes** (track-side `I_FREEMODE=2`, `I_NUMFIXEDLANES`,
> `C_LANEPLAYS:N`; item-side `I_FIXEDLANE`, `C_LANEPLAYS`, `B_FIXEDLANE_HIDDEN`):
> each mode owns a lane on a shared track, and toggling shows/plays only the active
> mode's lane. This is an item-visibility projection over the one timeline — the
> exact analog of today's track-visibility projection — with still **no** literal
> second surface, window, or duplicate project. It is an **additive sub-phase (Phase
> D2 / Phase E)** on top of D1, spec'd in *§Two-canvas sub-phase* below. Full framing
> and the rejected alternatives (timebase-offset, subproject) live in
> `docs/product/design-view.md` §Two-canvas direction.
It is the visibility/processing analog of the capture pillar's load-bearing rule:
**designing and arranging are separate stances on one timeline**, and the tool
@@ -297,10 +315,21 @@ togglable per selection.
## Non-goals / guardrails
- **No second canvas.** Do not build a parallel arrange surface — reject any such
path in review.
- **No *literal* second canvas.** A literal second arrange surface, a second window,
or a duplicated project stays **rejected** — reject any such path in review. This
guardrail was **narrowed (2026-07-23), not lifted**: per-item mode separation via
REAPER fixed lanes on shared tracks (an item-visibility projection over the one
timeline) is now **allowed** and specified in *§Two-canvas sub-phase* below. Paths
that remain **rejected**: subproject / second-project-file approaches, and
overloading item `D_POSITION` with mode semantics (timebase-offset regions) — the
latter collides with the capture null test. See `docs/product/design-view.md`
§Two-canvas direction for why those were rejected.
- **Never touch mute/solo.** Any code path reading/writing `B_MUTE` / `I_SOLO` is a
bug.
- **Never drive a manual lane** (Phase D2). A mode toggle touches only **managed**
lanes (minted by the mode system, keyed in the lane-ownership index). Any code path
that shows, hides, silences, or re-lanes a **manual** lane — a user-created comp/take
lane outside the mode system — is a bug. The tool drives only lanes it created.
- **Every leaf is managed.** The mode system owns all leaf tracks: an untagged leaf
is an Arrange member, and when the active mode is not Arrange it is fully parked
and snapshot-restored exactly like a tagged leaf out of its mode. **show-both** is
@@ -325,6 +354,136 @@ togglable per selection.
- Interaction with the user having a screenset active (Design View drives the same
flags a screenset recall would; last writer wins — confirm no surprising fight).
## Two-canvas sub-phase (Phase D2 / Phase E) — additive to D1
> **Additive sub-phase, settled 2026-07-23.** Extends D1's track-level mode
> projection to **item level** so each stance owns its own items as well as its own
> tracks. Nothing in D1 changes; this wraps it. Runtime floor rises to **REAPER 7**
> for this sub-phase (fixed lanes shipped in v7). Product framing in
> `docs/product/design-view.md` §Two-canvas direction.
### What it adds
On a track present in **both** stances (a show-both track, or a folder carrying its
own media), each mode owns a **fixed lane**: the active mode's lane shows and plays;
the inactive mode's lane is hidden and silent. A Design take and an Arrange take can
then live on the *same track, same time position*, without colliding on the view.
Track-only-in-one-mode content is still handled by D1 track-parking, unchanged.
### Settled decisions
- **Mechanism: fixed item lanes.** Map mode → lane; toggle drives per-lane play/show
so only the active mode's lane is present. Items keep their real position and real
track — nothing is moved in time or deleted. SDK surface (**verified present in
`vendor/reaper-sdk`**): track-side `I_FREEMODE = 2`, `I_NUMFIXEDLANES`,
`C_LANEPLAYS:N`; item-side `I_FIXEDLANE`, `C_LANEPLAYS`, `B_FIXEDLANE_HIDDEN`.
**`I_FREEMODE` changes require `UpdateTimeline()`** to take visible effect.
- **Membership: auto-tag by active mode at creation.** New content — **both new
tracks and new items** — is tagged to whatever mode is active when it is created.
Pre-existing content defaults to **Arrange**. Membership is **exclusive**:
Design-created content never appears in Arrange and vice versa, except via the
existing **show-both** escape hatch. (This is the same tag-to-active-mode rule as
D1 track membership, now reaching items on shared tracks.)
- **Inactive-mode content is hidden AND silenced.** The off-mode lane is set
`C_LANEPLAYS = 0` — neither shown nor played — consistent with exclusive
membership and with D1's "flipping modes is a real change, not cosmetic." Show-both
is the deliberate opt-out for a lane that must stay audible across modes.
- **Managed vs. manual lanes — indexed and distinct.** Fixed lanes are also REAPER's
native comping surface: a user may keep **their own** manual lanes (comp takes,
alternate reads) on a track alongside the mode system's lanes. The tool maintains a
**lane-ownership index** — per (track GUID, lane): **managed** (which mode owns it)
vs **manual** (user-minted, outside the mode system). Mode operations touch **only
managed lanes**; manual lanes are never shown, hidden, silenced, or re-laned by a
toggle, and their `C_LANEPLAYS` stays exactly as the user set it. Items a user adds
to a **manual** lane are **not** auto-tagged (auto-tag governs normal timeline
content, not hand-managed lanes). The ownership index rides in `"reasampler"`
`view_state` alongside the membership index, GUID-keyed and portable.
- **Capture placement is mode-aware.** An **explicit** placement while in Design mode
— including capture-and-place — lands the item in the **Design lane**; the same
rule governs manual insertion. **The capture load-bearing principle is untouched:**
capture still writes a file + index entry and **never** auto-inserts; this governs
only *where an explicit placement lands*.
- **REAPER floor: v7.** No version-gate branch — below v7 this sub-phase is simply
unavailable.
### Precision invariants (unaffected — called out explicitly)
The capture precision invariants — **null test, bit-identical repeats,
non-destructive, exact bounds, relative-paths-only** — are **entirely unaffected** by
this sub-phase. No capture path changes; lanes are a placement/view concern
downstream of the written file. Lane assignment and `C_LANEPLAYS` are **reversible
flags**: the item is never relocated in time or deleted, so the sub-phase extends the
D1 non-destructive snapshot/restore contract to a new (item-lane) flag family rather
than introducing any destructive operation.
**New invariant — mode operations touch only managed lanes.** A mode toggle drives
only lanes the mode system minted (managed lanes, keyed in the lane-ownership index).
Manual lanes — user-created comp/take lanes outside the mode system — are **never**
shown, hidden, silenced, or re-laned by a toggle; their `C_LANEPLAYS` is left exactly
as the user set it. This is the fixed-lane analog of *never touch `B_MUTE`/`I_SOLO`*
and *never touch master*: **the tool drives only what it created.** Enforceable and
testable in the pure layer — the "which lanes may this toggle touch" decision is a
pure query over the ownership index; only reading REAPER's live lane state is shell.
### Module architecture (preserve the pure/shell split)
- **Pure (`view_mode_model` extension).** Lane math — which lane maps to which mode,
which `C_LANEPLAYS` value per mode, the item-lane op family alongside the existing
track-flag op family — is **REAPER-free and unit-tested**, mirroring the D1
planner. The **lane-ownership index** (per (track GUID, lane): managed-which-mode vs
manual) and the **"which lanes may this toggle touch" query** (managed only) are
pure and unit-tested — the planner emits lane ops for managed lanes only and never
for manual lanes. The **auto-tag decision** is pure too: given *a set of new
track/item GUIDs + the active mode* — and, for items, whether the item landed in a
manual lane (exempt) — produce the membership writes.
- **Shell.** Two shell responsibilities. (1) **Apply** the planner's item-lane ops
(`I_FREEMODE`/`I_FIXEDLANE`/`C_LANEPLAYS`/`B_FIXEDLANE_HIDDEN` via the media-item
info setters, `UpdateTimeline()` after `I_FREEMODE` changes) — for managed lanes
only. (2) **Detect** new content and read live lane state — see below.
- **Persistence.** The tool persists which lane maps to which mode **and the
managed/manual ownership index** (a small addition to the `"reasampler"` view
section); REAPER stores fixed lanes and lane-plays in the `.rpp` natively.
### New-content detection (implementation design point)
REAPER exposes **no clean "item added" / "track added" event callback.** Auto-tagging
therefore requires the shell to **diff project state on the panel's existing timer**
the `bank_panel` already polls and fingerprints the bank; this extends that machinery
to the timeline's tracks and items.
- Each poll, compare the live track/item GUID set against the previous poll's set;
**any GUID new since the last poll is tagged to the then-active mode.**
- Correctness the implementation must handle: the **first poll after project open must
not mass-tag** pre-existing content (pre-existing defaults to Arrange, per the
membership rule).
- **Manual-lane exemption (design point).** An item added to a **manual** lane is
**not** auto-tagged — auto-tag governs normal timeline content, not lanes the user
hand-manages. Distinguishing the two may need a heuristic at detection (e.g., an item
whose `I_FIXEDLANE` is marked manual in the ownership index is exempt; content
outside any managed lane on a mode-managed track follows the active-mode rule). The
precise rule is an **open implementation design point**; the settled boundary is that
manual-lane content is off-limits to auto-tag.
- **Lane-identity fragility (design point).** `I_FIXEDLANE` is the lane's identity and
is how the ownership index keys to a lane. Whether the index survives lane
reorder/renumber/deletion without going stale is an **implementation design point**
(same class as GUID-keyed reorder-safety for tracks) — flag, don't solve here.
- Pure/shell seam: the **detection** (diffing REAPER's live set each tick, reading live
lane ownership) is shell; the **tagging decision** and the **managed/manual lane
query** (new GUIDs + active mode + manual-lane exemption ⇒ membership + lane writes)
are pure and unit-tested.
### REAPER API surface (verify all signatures)
- Fixed lanes — track: `I_FREEMODE` (=2), `I_NUMFIXEDLANES`, `C_LANEPLAYS:N` via
`GetMediaTrackInfo_Value`/`SetMediaTrackInfo_Value`; item: `I_FIXEDLANE`,
`C_LANEPLAYS`, `B_FIXEDLANE_HIDDEN` via `GetMediaItemInfo_Value` /
`SetMediaItemInfo_Value`. Call `UpdateTimeline()` after `I_FREEMODE` changes.
- Detection reuses the `bank_panel` timer + GUID fingerprinting already in place;
item GUIDs via the item's `GUID` (`GetSetMediaItemInfo_String` `"GUID"`), track
GUIDs via `GetTrackGUID` as in D1.
- **Verify every name/signature against the SDK header before use** — the surface is
verified *present*, but confirm argument order, types, and flag values.
---
# Multi-bank — additive phase spec