# src/core/view — pure Design View mode model ## Scope Pure, REAPER-free Design View model: mode/track membership, folder-derived visibility, snapshot-based park/restore planning, the per-mode solo cache and its replay plan, new-content (GUID) detection, and the managed/manual lane-identity convention that underlies per-item mode separation (fixed lanes). Does **not** include: the actual DAW-side flag application (hide, CPU-park, per-FX offline, restore via `B_SHOWINTCP` / `B_SHOWINMIXER` / `B_MAINSEND` / `I_FXEN`, solo cache/clear/replay via `I_SOLO`) or the never-touch-master/mute enforcement — those live in `shell/view`. ## Invariants Design View — additive phase spec (settled decisions; Two-canvas sub-phase, settled 2026-07-23): - **Membership.** Default = Arrange; every untagged leaf belongs to it. Leaves opt in to Design (or any mode). No track appears in two modes at once except via an explicit show-both toggle or parent/folder derivation. - **Parents are derived, never tagged.** A parent/folder track is visible in mode M if either any descendant leaf is visible in M, or the parent belongs to M by its own membership; an untagged parent is an Arrange member by default. A parent is never parked in any mode it is visible in. Master track is always visible and never touched. - **N-mode model, two-mode UI.** The data model carries arbitrarily many modes; the UI ships Arrange + Design. A mode is (stable id, display name, ordinal). - **Persistence.** Membership index + last-active mode + per-track flag snapshots ride in the existing `"reasampler"` project ext-state namespace and travel with the `.rpp`. - **Non-destructive restore (enforced in the pure layer).** For every flag the tool drives, snapshot the prior value before parking; on toggle-back, restore from the snapshot, never to a hardcoded default. Round-trip (snapshot → park → restore) returns every driven flag to its captured value — this is the phase's trust anchor, the analog of the capture null test. - **Disjoint solo surfaces, cached not destroyed.** Solo is per mode: a real switch banks the outgoing mode's raw `I_SOLO` values, clears them, and replays the incoming mode's verbatim. Same snapshot sense of non-destructive as the bullet above — the tool never *loses* the user's solo, it parks it with the mode it belongs to. `B_MUTE` and the master track stay untouched absolutely. A reapply touches solo not at all. - **GUID-keyed, reorder-safe.** Membership keys on track GUID (`GetTrackGUID`), never track index; tolerates unknown/stale GUIDs (pruned on reconcile via `ViewModeModel::reconcile(liveGuids)`). The same rule binds one level down: a parked track's per-FX offline state keys on the FX's own identity (`TrackFX_GetFXGUID`), never its slot — see `fx_offline`. - **Relative/portable state only** in the persisted view section (GUID strings, mode ids — no absolute paths, no index positions). - **Show-both semantics.** A per-track "pin visible across modes" flag that re-enables processing whenever shown. A show-both leaf appears in every mode's visible set and is never parked — its driven flags stay at snapshot/restored values. Stored on the membership record; persists; togglable per selection. - **Non-goals / guardrails:** No literal second canvas — a second window or duplicated project stays rejected; subproject/second-project-file approaches and overloading item `D_POSITION` with mode semantics (timebase-offset regions) are rejected paths (the latter collides with the capture null test). Every leaf is managed: an untagged leaf is an Arrange member and, when the active mode is not Arrange, is fully parked and snapshot-restored exactly like a tagged leaf; show-both is the only way to opt a leaf out of parking. Restore from snapshot, never to a default: no hardcoded "on" restores. **Two-canvas sub-phase (Phase D2/E) — settled and landed parts only:** - **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. - **Membership: adoption rule for new items; active mode for new tracks absent an explicit tag.** New tracks are tagged to the active mode at creation **only when the GUID carries no membership record** — an explicit tag wins over the detector, because the detector classifies content the *user* made, not content the tool made and already classified. New items follow an adoption rule: if the item's track has pre-existing managed-eligible content spanning exactly one mode, the item adopts that mode; the active-mode fallback applies only when the track is empty or already spans multiple modes. Items in manual lanes are excluded from the prior-mode computation and are not auto-tagged at all. Membership is exclusive per item except via show-both. - **Managed vs. manual lanes — indexed and distinct.** 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. The ownership index rides in `"reasampler"` `view_state` alongside the membership index, GUID-keyed and portable. **New invariant — mode operations touch only managed lanes:** "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." - **REAPER floor: v7** for this sub-phase (fixed lanes shipped in v7); no version-gate branch — below v7 the sub-phase is simply unavailable. - 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; lane assignment and `C_LANEPLAYS` are reversible flags, never a destructive operation. ## Modules - `view_mode_model` — Design View mode system: mode registry, GUID-keyed membership, folder-tree-aware visibility derivation, snapshot-based park/restore planner, the per-mode `SoloCache` it owns, JSON round-trip. - `fx_offline` — the per-FX offline snapshot's KEY and its restore resolution: `FxKeying` (Identity / Slot), `FxOfflineState`, the planned `FxOfflineOp`, and `resolveFxRestore`, which matches each captured state to the FX it came from against the chain as it stands at restore time. An identity that is no longer live is DROPPED and counted (`FxRestoreDrops`, reported through `describeFxRestoreDrops`), never re-pointed at a slot — see the `FxKeying` and `resolveFxRestore` comments in `fx_offline.h` for why. Slot keying survives only for snapshots lifted from a pre-identity `view_state` and for park plans, where every live slot is the target by construction. - `solo_cache` — the per-mode solo surface: `SoloCache` (mode id → GUID → raw `I_SOLO`), the soloed-subset filter, and `planSoloRestore`, whose two drop rules (dead GUID, not visible in the incoming mode) and their reasoning live in its header. - `view_tree` — pure `I_FOLDERDEPTH`→FolderTree helper for the Design View shell. - `mode_switch` — REAPER-free segment layout + hit-test for the bank_panel's Design View mode switch. - `guid_diff` — the pure, REAPER-free core of the D2 Wave-2 new-content detection: `newGuids(previous, current)` computes the GUIDs present in `current` but absent from `previous` (empty GUIDs ignored); `GuidBaseline` tracks the live GUID set across polls for one project, implementing the first-poll-after-open guard (the first `observe()` after construction/`reset()` records a baseline and reports nothing new, so pre-existing content is never mass-tagged) and re-arms via `reset()` on a detected project switch so detection never diffs across two unrelated projects. - `lane_keys` — the pure, REAPER-free convention mapping a fixed lane's durable REAPER name (`P_LANENAME:n`) to the opaque lane-key `view_mode_model` keys by, plus the managed/manual heuristic both the toggle-apply path and the new-content/auto-tag exemption path share: `kManagedLanePrefix` ("reasampler:") stamps every lane the tool mints; `isManagedLaneName`/`managedLaneKey`/`laneNameForMode`/`modeIdFromLaneName` round-trip a lane name ↔ its owning mode id; `isOnManualLane` is the single predicate governing which lanes a toggle may drive and which items are exempt from auto-tag. ## Gotchas - REAPER exposes no durable per-lane GUID — the only lane identity is the ordinal `I_FIXEDLANE`, which REAPER renumbers on reorder/delete. Lane identity must ride on the durable `P_LANENAME` (`lane_keys`), never the raw ordinal, or a reorder will silently corrupt managed/manual ownership. - `kManagedLanePrefix` ("reasampler:") is stable-forever like an action-id string — changing it strands the ownership of every already-minted lane in every already-saved project. - The `view_state` blob's version ladder lives beside `kViewStateVersion` in `view_mode_model.cpp`. v2 writes the v1 slot array BESIDE the identity array so a downgrade keeps the behavior it had; the version field is written but deliberately not validated on read, because an unreadable `view_state` falls back to a default model and loses every membership tag. - `guid_diff::GuidBaseline` must have `reset()` called on every detected project switch, or the next `observe()` will diff across two unrelated projects and mass-tag (or miss) content.