From 95451798d26f0dd1fee28d7ca7153d71c7ff8db2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 1 Oct 2026 15:13:18 -0400 Subject: [PATCH] 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 --- docs/lane-nesting-notes.md | 116 +++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 docs/lane-nesting-notes.md 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. +