Files
reasampler/src/core/view/CLAUDE.md
T
daniel d5ed4f6e53 Split unidentified from missing FX in Design View drop report, fix message + console pop
Distinguish no-GUID-at-capture from identity-no-longer-live; rewrite the drop
message to state the real recovery step; mark FX-GUID stability [verify — DAW];
guard mismatched fx/fxOffline lengths; the report never force-opens now.
2026-08-02 18:51:41 -04:00

9.4 KiB

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.