arthur/docs/lane-nesting-notes.md

117 lines
5.3 KiB
Markdown
Raw Normal View History

Nesting is a gesture of its own, not an overlap Now that a lane is generic, the obvious next move is to read a clip dropped on another clip as "put it inside that symbol". It cannot mean that: dropping a clip on occupied lane time already means it claims that time and trims the incumbent. Structural nesting therefore needs an explicit affordance -- a grab handle with `grab`/`grabbing` cursors, distinct from the body's temporal move and the edges' trims -- and its drop must route through `nest/move-node`, which preserves world transform and root timing, rather than through a weaker `:parent` assignment that would make a drawing jump when it is rehoused. The second half of the note is what expansion should be. One permanently expanded row per lane clip is the vertical growth the one-row lane exists to avoid, so an expanded lane shows exactly one portal: the currently selected clip, swapped in place when the selection changes, not following the playhead. The portal header is the structural drop target, sub-expanding it walks the source symbol's lanes through the existing recursive root-time mapping, and collapsed clips carry their instance-level keys as ticks. The cost of inspection stays constant. No lane-as-symbol type and no second ownership edge: the hierarchy is still symbol, lane, clip, source symbol, its nodes. Lane membership owns time; a symbol instance owns composition. Written before the interaction is built, because the capability it protects is easy to lose by accident. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-01 15:13:18 -04:00
# Lane nesting interaction notes
Status: design note, 2026-10-01. This records the interaction before more lane
UI is implemented.
## The capability that must not be lost
A lane owns temporal placement, but a symbol instance is still a doorway into
another symbol. A drawing accidentally authored at the root must be movable into
an instance in any lane, including another lane, without changing its visible
position or timing.
That operation already exists as `nest/move-node`. It resolves the source and
destination at the current root frame, transplants the node, and re-expresses
its transform and time under the new parent. The lane UI must expose a target
path for it; it must not replace it with a weaker `:parent` assignment.
There are therefore two different drag intentions:
1. **Temporal move:** drag a clip body onto lane space. It remains a clip in a
lane, moves in time, and claims the destination interval by trimming/removing
incumbents.
2. **Structural move:** drag from the clip's grab affordance onto another symbol
instance. The dragged node is transplanted into the target instance's source
symbol with `nest/move-node`, preserving its world transform and root timing.
These cannot be inferred from overlap alone. Dropping clip A onto time occupied
by clip B already means “A claims that time and trims B.” Structural nesting
therefore needs an explicit grab affordance/mode. Its cursor is `grab` and
`grabbing`; trim edges keep their resize cursors and the ordinary body keeps its
timeline-move behavior.
Both visible clip blocks and an expanded symbol header are structural drop
targets. This permits moving a root drawing directly into `symbol-3` even when
its lane is collapsed.
## Compact expansion: one selected-clip portal
Expanding a lane must not restore row-per-clip vertical growth. Instead, an
expanded lane reveals exactly one clip portal: the currently selected clip in
that lane.
```text
▾ foreground lane [symbol-1][symbol-2][symbol-3]
▾ symbol-3 instance/source header and drop target
▸ body lane nested rows, mapped to the root ruler
▸ face lane
position nested keyframes mapped to root time
```
- Selecting another block in the same lane swaps the portal in place.
- With no selected clip in that lane, expansion shows a compact “select a clip
to inspect” row. It must not follow the playhead during playback; that would
make the timeline restructure itself while playing.
- The portal header represents the selected instance and is the structural drop
target for moving root or sibling content into its source symbol.
- Sub-expanding the portal uses the existing recursive symbol-row walk. Nested
lanes and channels are mapped through the instance clock into the open/root
ruler, as ordinary expanded instances already are.
- The lane's own transform/channel rows remain available separately. They affect
every clip in the lane and are not properties of the selected portal.
This keeps the cost of inspection constant: an expanded lane adds one selected
symbol branch, not one branch for every temporal clip it contains.
## Keyframe visibility
Two levels should be visible without changing editors:
- The selected clip's instance-level keys (transform, visibility, corrections)
appear as ticks inside that clip block on the lane row.
- Expanding the lane opens the selected clip portal, where source-symbol and
recursively nested keys appear on their own rows, mapped to root time.
Thus the collapsed lane answers “where does this clip change?” and the expanded
portal answers “which property inside this symbol changes?” The second view is
still the root timeline; entering the symbol is not required merely to see or
edit its keys.
## Drag targets and feedback
- Grab onto lane background: move/adopt the instance into that lane.
- Grab onto a symbol clip: structurally transplant into that clip's source
symbol.
- Grab onto the expanded portal header: the same structural transplant, with a
larger and less ambiguous target.
- Grab onto itself or one of its descendants: refuse before drop to prevent a
symbol cycle.
- A structural target receives an inset highlight and the preview stays in that
target. A lane-time target receives the dashed temporal clip preview.
- Successful structural drops expand the target lane and select the moved node
beneath the target portal, so the result is immediately visible.
## Data model consequence
No lane-as-symbol type is required. The hierarchy remains:
```text
symbol -> sequence lane -> instance clip -> source symbol -> its lanes/nodes
```
Lane membership owns time partitioning. Symbol instances own composition
nesting. The UI may present the selected instance below its lane, but that is a
derived portal, not another ownership edge and not a duplicated node.
## Implementation order
1. Render instance-level key ticks within lane clips.
2. Add selected-clip portal expansion to `timeline/rows` using the existing
recursive walk and root-time mapping.
3. Add the explicit structural grab affordance and clip/portal drop targets.
4. Route structural drops through `nest/move-node`; add browser coverage for a
root drawing moved into a clip in another lane without a visual jump.
5. After the transplant, expand the destination portal and reveal/select the
moved row.