3.6 KiB
The editing grid is the symbol's own frames
What was wrong
Every number a person authors — a span, a :time :at, a key, a cut — is in the
frame space of the symbol it lives in (docs/time.md, and that part is right).
The timeline, though, drew its ruler in OUTPUT frames: clip/output-frames, the
transport's length. In a 12fps project holding 30fps symbols those two spaces sit
at a ratio of 2.5, so:
- a clip at symbol frame 31 was drawn at ruler frame 12.4 — no clip edge landed on a frame mark, because almost none of them can;
- a gesture measured in ruler frames had to be multiplied into the symbol's
frames and rounded (
nest/dragged), so dragging one ruler frame moved the clip 2 or 3 symbol frames — 0.8 or 1.2 ruler frames, never the 1 the pointer said. That is the jumpiness; - the preview drew the gesture's own number of ruler frames while the commit wrote the rounded one, so the ghost sat somewhere the settled clip did not;
- before the rounding was added, the fraction went into the document and every
later edge edit on that clip was refused for ever (
span/*refuses a fractional edge, as it should).
One cause, four symptoms. None of them is an edge case to patch.
The model
- A symbol's own frames are the only coordinate anything authored lives in. Unchanged.
- The editor edits in the open symbol's frames. The ruler, the marks, the playhead's position on it, every pointer→frame answer, every drop frame and every drag delta are the open symbol's frames. No multiplication anywhere in the gesture path, so no rounding and nothing fractional to refuse.
- The output grid is playback's alone — the clock, the audio mix, export,
and the frame the stage draws. Exactly two pure functions cross between them
and nothing else does:
clip/shown-frame clip sid f— which ofsid's frames output framefshows (cadence/frame: the latest at or before it).clip/first-output-frame clip sid n— the output frame that first shows symbol framen; the inverse, for seeking from the ruler.
nest/inside,nest/placement,nest/spansand the gestures take the subject symbol's OWN frame. They used to take an output frame and multiply it secretly, which is what made every caller's units a guess. A caller holding the playhead converts withclip/shown-frame, at its own edge, visibly.
The gestures
- One pointer→frame function for the whole timeline,
frame-under. A drag's delta is the difference of two of its answers, never a pixel ratio rounded separately — so the preview and the commit are the same number by construction. - The junction between two clips is one handle with one meaning: roll. It
moves the end of the left clip and the start of the right one together, which
is
span/roll, which is already nothing butresize-outthenresize-in. The three 4px-wide zones it used to pick between — trim-left, roll, trim-right, inside twelve pixels — were the "it just picks one" the handle was accused of. An edge that is not shared still has its own in/out handles.
Audio goes with the picture it belongs to
A take's sound lived inside the take symbol, so placing the take brought it and
placing the FACE the take is made of brought nothing. A symbol now says what it
sounds like — :audio, a sound source — and placing one places a linked audio
node beside the clip. Detection sets it on the face it extracts, which is the
automatic link; ::ui/link-audio sets or clears it by hand, which is the manual
one. :linked-to on the audio node already existed and already follows a moved
picture.