arthur/docs/one-grid-plan.md
2026-10-01 23:35:22 -04:00

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

  1. A symbol's own frames are the only coordinate anything authored lives in. Unchanged.
  2. 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.
  3. 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 of sid's frames output frame f shows (cadence/frame: the latest at or before it).
    • clip/first-output-frame clip sid n — the output frame that first shows symbol frame n; the inverse, for seeking from the ruler.
  4. nest/inside, nest/placement, nest/spans and 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 with clip/shown-frame, at its own edge, visibly.

The gestures

  1. 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.
  2. 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 but resize-out then resize-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.