diff --git a/docs/lane-nesting-notes.md b/docs/lane-nesting-notes.md new file mode 100644 index 0000000..7adee9a --- /dev/null +++ b/docs/lane-nesting-notes.md @@ -0,0 +1,116 @@ +# 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. +