The timeline opens the whole document
Making a lane one row cost the thing a row was for. A clip stopped being a row, so there was no longer any way to open a clip and see what was inside it, and the inside of a drawing — the most ordinary thing in the document — became reachable only by opening it as its own tab. This is that capability back, from the root timeline, down as far as it goes. An expanded lane opens exactly ONE clip: the selected one. Its own keys, then the lanes and nodes of the symbol it places, then theirs, each mapped into this ruler by the recursive walk that was already there. Twelve clips in a lane still cost one row, and inspection costs one branch rather than twelve. Two things that only showed up once it ran. The portal is chosen by the whole LINEAGE of the selection and not by the selected id: selecting a shape inside the clip — or the end of its span — is still working inside that clip, and matching the id alone shut the portal the instant anything under it was touched. And selecting now waits for the pointer to come UP, because selecting on the way down re-drew the timeline before the gesture had said anything: it shut the portal holding the lane being dragged INTO, out from under the pointer. A HELD clip opens too, which the old row walk never did either. `source-time` is nil for a hold, so the walk stopped there and the contents of every drawing were invisible from here. Its rows are shown across the hold — which is when the node is on screen — and marked `:unmapped?`: no keys, and no draggable edges, because a frozen clock gives no frame inside it a place on this ruler. Refusing to place the keys is the honest half; refusing to show the rows was not. Double-clicking a clip opens the symbol it places as a tab, as double-clicking the same symbol in the pool does. That was already written and had never once run: the track captures the pointer for a slide, so the click and double-click that follow are delivered to the track and never to the block. The track now resolves them itself. Fixing the delivery exposed two more: `symbol/lineage` reported a `parent cycle` for any id in a symbol with NO nodes, because a one-element chain is longer than zero nodes — and opening a symbol left the selection pointing into the symbol being left, which the breadcrumb and the inspector then tried to resolve. The editor unmounted. Both are fixed where they were wrong, and the browser test asserts the editor is still standing afterwards. Audio is a clip in a lane like everything else. A dropped sound lands in one and is trimmed and moved by the same commands; a lane holds picture or sound and not both, which is the explicit capability the model asked for rather than a guess per frame. The refusal lives in the commands and not only in validation, because placement claims time: `blank` would have deleted the sound to make room for the picture and left a perfectly valid document behind. What is in a lane of the open symbol is drawn as a lane; what is nested inside a placed symbol is still flattened by `audio-tracks`, so no sound is on two rows. Everything that enters the timeline now enters a lane: a converted take, a symbol brought in from another project, a sound. One rule answers where — `lane-destination` — and every symbol is born with a lane for it to answer with. An unaimed drop fills an EMPTY lane rather than taking an occupied one nobody pointed at, because the alternative is trimming away what was there to make room for what was dropped. Shift during a clip-body drag means the other intention: put this node INSIDE the symbol the clip under the pointer places, through `nest/move-node`, which is what keeps the world transform and the root timing. Overlap cannot say which of the two is meant — dropping on occupied time already means claiming it — so the person says, and a label by the pointer says it back. The label asks `nest/move-refusal`, the same check the command makes, so it cannot promise what the drop would refuse. Today it refuses more than it allows: both clips have to be on screen at one frame, which two clips in one lane never are, and a held destination has no clock to move through at all. `docs/lane-nesting-notes.md` argues that the second refusal is stronger than the facts require and says what would settle it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
95451798d2
commit
2dc5735ded
19 changed files with 885 additions and 166 deletions
|
|
@ -105,12 +105,57 @@ 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.
|
||||
1. ~~Render instance-level key ticks within lane clips.~~ Done: a clip's keys
|
||||
are on its block, drawn after the blocks so they land on the one they
|
||||
belong to.
|
||||
2. ~~Add selected-clip portal expansion to `timeline/rows`.~~ Done, with two
|
||||
additions the note did not anticipate:
|
||||
- The portal is chosen by the whole LINEAGE of the selection, not the
|
||||
selected id. Selecting a shape inside the clip, or the end of its span,
|
||||
is still working inside that clip, and matching the id alone closed the
|
||||
portal the moment anything under it was touched.
|
||||
- A HELD clip opens too. `clip/source-time` is nil for a hold, so the walk
|
||||
used to stop there and the inside of every drawing was unreachable from
|
||||
the root timeline. Its rows are now shown across the hold and marked
|
||||
`:unmapped?`: no keys, and no draggable edges, because no frame inside it
|
||||
has a place on this ruler.
|
||||
3. ~~Add the explicit structural affordance.~~ Done as SHIFT on a clip-body
|
||||
drag rather than a separate grab handle: shift turns a temporal move into a
|
||||
structural one, the target clip takes an inset highlight, and a label by the
|
||||
pointer says which of the two is about to happen.
|
||||
4. Route structural drops through `nest/move-node`. **Wired, and blocked in
|
||||
the domain.** The gesture asks `nest/move-refusal` on the way past, so the
|
||||
label says before the drop what the command would say after it. Two
|
||||
refusals stand in the way of ordinary use:
|
||||
- *both have to be on screen at this frame.* Inherent, and worth keeping:
|
||||
the move preserves the world transform and there is no common frame to
|
||||
preserve it at otherwise. It does mean nesting one clip into another in
|
||||
the SAME lane can never work — a lane never overlaps itself — so this is
|
||||
a between-lanes gesture with the playhead somewhere both are showing.
|
||||
- *a held or looping clip has no clock to move through.* `nest/inside`
|
||||
returns no `:time` for a hold, and a held one-frame drawing is the most
|
||||
common thing in a document, so today nesting into one is refused — which
|
||||
is most of what anybody would try.
|
||||
5. After the transplant, expand the destination portal and reveal/select the
|
||||
moved row.
|
||||
moved row. `::ui/move-node` already selects the moved node and opens the
|
||||
rows down to it; the portal follows from the lineage rule in 2.
|
||||
|
||||
## The held destination, unresolved
|
||||
|
||||
A held cel shows ONE source frame for its whole span, so there is no
|
||||
invertible map from the lane's frames to the drawing's and `move-node`
|
||||
refuses. But the refusal is stronger than the facts require. Inside a frozen
|
||||
destination only one frame is ever observed, so:
|
||||
|
||||
- the RATE of any map into it is unobservable — every rate shows frame `in`;
|
||||
- what IS observable is that the moved node should show, at that one frame,
|
||||
what it shows now at the current root frame.
|
||||
|
||||
That pins a unique sensible answer — rate 1, aligned so the current frame maps
|
||||
to the shown frame — and nothing else about the mapping can be seen. If that
|
||||
argument holds, it is a rule rather than a guess, and it is the difference
|
||||
between structural nesting working for drawings and not working at all. It
|
||||
needs its own proof: a drawing authored at the root, nested into a held cel in
|
||||
another lane, sampled before and after to show the same picture, in the style
|
||||
of `drawn` in `lane_test`.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue