# src/shell/view — Design View mode application shell

## Scope

The REAPER-facing half of Design View: applying a mode's visibility/processing
state to live tracks (park/restore), snapshotting flag values before parking, and
restoring from snapshot on toggle-back. The mode registry, membership derivation,
and the pure park/restore planner are owned by `core/view` (`view_mode_model`) —
this directory is the shell that reads/writes REAPER track flags, it does not
decide membership or mode rules.

## Invariants

- **Never touches master or `B_MUTE`; never LOSES solo.** The tool owns visibility,
  `B_MAINSEND`, `I_FXEN`, per-FX offline, and — on a real mode switch only —
  `I_SOLO`, on every managed leaf, tagged or untagged. `B_MUTE` is untouched
  absolutely. The master track is untouched absolutely: it is outside `GetTrack`'s
  index space, so it never enters the enumeration any of these writes iterate, and
  its visibility flags are never driven (the SDK forbids
  `B_SHOWINTCP`/`B_SHOWINMIXER` on master).
- **Solo surfaces are disjoint per mode, cached not destroyed.** A real switch
  (target != active) reads every live track's raw `I_SOLO`, banks the non-zero
  values against the OUTGOING mode, clears them, and replays the incoming mode's
  banked values verbatim — solo-in-place and safe-solo variants included, never
  collapsed to a boolean. This is the same snapshot sense of non-destructive that
  park/restore already gives visibility and FX state: the user's solo is never
  lost, only parked with the mode it belongs to. A REAPPLY (target == active —
  tag/untag/show-both, project load) touches solo not at all.
- **Parking a track** (inactive-mode leaf) drives `B_SHOWINTCP=0`, `B_SHOWINMIXER=0`
  (hide both panels), `B_MAINSEND=0` (out of mix), `I_FXEN=0` (FX bypassed), and
  `TrackFX_SetOffline(track, fx, true)` for each FX (reclaim CPU) — full CPU-park,
  not mix-removal-only.
- **Non-destructive restore.** For every flag the tool drives, snapshot the prior
  value BEFORE parking; on toggle-back restore FROM the snapshot, never to a
  hardcoded "on." Round-trip (snapshot → park → restore) returns every driven flag
  to its captured value — the phase's trust anchor, the analog of the capture null
  test. Per-FX offline is snapshotted WITH each FX's identity (`TrackFX_GetFXGUID`)
  and restored through `core/view/fx_offline`, so a chain reordered while the track
  was parked cannot land one plugin's state on another; an FX gone at restore time
  is dropped and reported to the console, never restored onto its old slot.
- **GUID-keyed, reorder-safe.** Membership/snapshot keys on track GUID
  (`GetTrackGUID`), never track index; tolerates unknown/stale GUIDs (pruned on
  reconcile).
- **Relative/portable state only** in the persisted view section (GUID strings,
  mode ids — no absolute paths, no index positions).
- **Documented caveat:** offlined FX re-instantiate when a track returns to the
  active mode — stateful plugins (convolution, loaded samplers, tail-holding
  effects) re-initialize on return (possible load hitch, un-persisted internal
  state lost). Accepted cost of the CPU reclaim; surfaced at the toggle affordance
  (tooltip).
- **Show-both semantics:** a per-track "pin visible across modes" flag 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, FX
  online, in the mix. ("Show but keep parked" is not offered.) Stored on the
  membership record; persists; togglable per selection.
- **No literal second canvas.** A literal second arrange surface, a second window,
  or a duplicated project stays rejected — reject any such path in review.

**Two-canvas sub-phase (Phase D2/E) — settled and landed parts, DAW-application half:**

- **Fixed-lane item-level separation mechanics.** 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.
- **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.
- **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** for this sub-phase (fixed lanes shipped in v7); no
  version-gate branch — below v7 the sub-phase is simply unavailable.

Item→lane membership rules (the adoption rule, exclusive-per-item membership, the
managed/manual lane distinction, and the lane-ownership index) are model concepts
owned by `core/view` — see that directory's Invariants; this directory only
applies the resulting lane state to live tracks.

## Modules

- `view` — Design View shell: snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (`B_SHOWINTCP`/`B_SHOWINMIXER`/`B_MAINSEND`/`I_FXEN` + per-FX offline), restores from snapshot. Owns the one discriminator (`target != active`) that separates a real switch from a reapply, and with it both the playback gate (`transportBlocksModeSwitch`) and the solo cache/clear/restore seams. **Never touches master or `B_MUTE`.**
- `view_solo` — the `I_SOLO` read/write pair behind the per-mode solo surface, plus `clearTrackSolos`/`restoreTrackSolos`, the outgoing-clear and incoming-replay entry points `view` drives them through. Holds no policy: what to cache, clear, or replay is `core/view/solo_cache`.

## Gotchas

- The pure mode model (`ViewModeModel`, membership, `reconcile(liveGuids)`, the
  snapshot-based park/restore planner) lives in `core/view` — reference it, do not
  duplicate its spec here.
- The Two-canvas sub-phase (Phase D2/E)'s settled DAW-application rules
  (fixed-lane mechanics, mode-aware capture placement, hidden-AND-silenced) are
  reflected in Invariants above; the membership/lane-ownership model concepts
  it also covers live in `core/view`'s Invariants.
- REAPER's own undo restores live `I_SOLO` but not the model's solo cache or
  `activeModeId` — neither rolls back with a Ctrl-Z. An undo after a mode switch
  leaves the two out of step, and the next switch banks the undo-restored solos
  under whatever mode id is active at that point, not the one the user undid back
  to. Pre-existing: `snapshots_` already carries this same model-vs-undo split;
  the solo cache inherits it rather than introducing it. Not fixed here.
- `fx_offline`'s identity keying (`TrackFX_GetFXGUID`) assumes the GUID stays
  attached to its plugin across a chain mutation while parked. That is
  `[verify — DAW]` (see `fxGuidString` in `view.cpp`) and SWS issue #802 is a
  known reason it might not hold: `SNM_MoveOrRemoveTrackFX` reportedly leaves
  the FXID lines behind on reorder rather than moving them with the plugin. If
  confirmed, an SWS-driven reorder of a parked track's chain — not a native
  drag-reorder — can produce wrong-plugin restores or mass drops through
  `resolveFxRestore`. Do not design around this pre-emptively; if native
  reorder is clean (the likely case), only the SWS path degrades.
