3.8 KiB
Implementation notes — "A lane is a view"
Running log for docs/lane-is-a-view-plan.md. [ ] not started, [~] in
progress, [x] done with npm test green.
Baseline at fb38990: 475 tests, 9621 assertions, 0 failures.
Order of work
The plan's seven steps, re-grouped — see Deviation from the plan's order below.
- A. Domain:
symbol/children,symbol/lane?,symbol/overlaps,lane.cljs→span.cljs, the overlap check inspan/finish(plan steps 3, 4, and the domain half of 5) - B. Events: re-base callers and remove lane-node-specific commands; keep
explicit
::new-laneand cross-lane adoption (plan step 5) - C. UI: row per symbol, explicit lane creation, and drag handling (plan steps 1, 2, and the UI half of 5)
- D. Audio: delete
holds-other?, the mixed-lane refusal, thein-lanefilter insound-rows(plan step 6) - E. Tests:
domain/sequence_test,events/lane_test,browser/lane.mjs - F. Shift-to-reparent still works, untouched (plan step 7)
Not in this pass — see Left for a second pass: tearing out the held cel, the instance-playback control, drawing a loop's repeats, the audio period guard.
Deviation from the plan's order
The plan's steps 1 and 2 are display work that keys off "a symbol's children",
and step 3 is what MAKES the cels a symbol's children. Until then a cel's
:parent is the lane node, so there is nothing for the display to read: step 2
cannot draw "a symbol's children as blocks" while the children belong to a
group. So the data model moves first (A) and the display follows (C). The
content of each step is unchanged; only the order is.
The one thing this gives up is the plan's promise that every step leaves the
editor usable — between A and C the timeline draws the new shape with the old
code. npm test is green at each step either way.
Decisions taken
- Lane mode is
:display :laneon the SYMBOL — plan's recommendation 2, and open question 1 answered "the symbol, not the instance". A symbol placed twice is drawn as a lane in both places. Added tosymbol/symbol-keysand toleaf/leaves'select-keysso it saves like:frames. - A symbol's children are its parent-less nodes that have a placed span. The plan's step 6 settles it: "an audio node is already a parent-less child of a symbol, which is exactly the new shape". Span-less nodes — a shape on screen for the whole shot — are not in the sequence and are skipped, which is also what stops the commands destructuring a nil span.
- A symbol holds at most one sequence. It follows from 1 and 2: the container is the symbol. Two lanes of picture is now two symbols placed in a third, which is what compositing already was.
- The open symbol gets a row of its own in lane mode, and only then. The blocks have to sit on a row and the open symbol had none; expanding it turns its children into ordinary rows. Not a row always, which would shift every row in the pane for no gain.
- Open question 2 — a lane row's edge drag trims the PLACING INSTANCE's
span, via
span/resize-out, like the handle on every other row. Rippling the children is what the cel blocks' own edges already do, and giving one handle two meanings is what the plan refuses elsewhere. - Open question 3 — the lane work first, the held cel after. The plan says
they are independent, and the held cel is joined to a loop control that does
not exist yet; doing it second costs one more pass over
lane_test's fixtures and risks nothing. - Lane creation stays explicit. A blank document and
new symbolcreate ordinary symbols. The separatenew → lanecommand creates and places a symbol with:display :lanein the effective creation target derived from selection and playhead; the new lane then becomes the primary selection.