Skip to content

Timeline: select several clips at once - #24

Open
dvgmdvgm wants to merge 1 commit into
HelpFreedom:mainfrom
dvgmdvgm:feat/timeline-multi-select
Open

dvgmdvgm wants to merge 1 commit into
HelpFreedom:mainfrom
dvgmdvgm:feat/timeline-multi-select

Conversation

@dvgmdvgm

Copy link
Copy Markdown

Empty space on a lane used to be a second way to drag the playhead. It is now
the selection surface.

The gestures

  • Drag on empty lane space draws a rubber band and selects every clip it
    covers, on every lane it reaches.
  • The playhead keeps its own surface — the ruler — and Shift+drag still
    marks the in/out range, on the lane and on the ruler alike.
  • Ctrl+drag adds a band to what is already selected; Ctrl+CLICK on empty
    space still closes a gap, so nothing is taken away.
  • Ctrl+click on a clip adds or removes it; Shift+click takes the whole
    rectangle between the first selected clip and the clicked one; a plain
    click on a member of a group narrows it back down to that clip.

What the selection can do. Move, delete, copy, split and link already
worked on the whole selection — the store has never been single-clip. Two
places had not caught up, and both are fixed here: deleting from a clip's
context menu first collapsed the selection to the clicked clip, and a group
dragged past t=0 piled onto zero instead of stopping there, because
setClipStarts clamps each clip on its own.

Three things worth reading in the diff

  • The band's anchor is a TIME, not a screen position. The wheel scrolls and
    zooms the timeline mid-drag, and a pixel anchor would then sit at a
    different moment than the one that was grabbed. scripts/e2e44.mjs pins
    this: it scrolls the timeline in the middle of a band drag.
  • A LOCKED track never enters a selection, not even through withLinked()
    that helper adds an A/V twin without looking at the lock, while the store
    skips locked clips, so the pair would have come apart on the first move.
    unlocked() in src/engine/timelineSelect.ts is where that is enforced.
  • windowDrag now also listens for pointercancel, for every drag in the
    file. A gesture cancelled by the system never sends pointerup, and the
    drag's overlay used to stay on screen and keep following the mouse with no
    button held.

Checks

  • node scripts/check-select.mjs — the band, range and lock maths, pure
    node, 15 assertions. Written before the code and seen red.
  • node scripts/e2e44.mjs — the gestures in a running app, 13 assertions.
    It uses TEXT clips only, so it needs no media and no ffmpeg, and it puts
    the track height back the way it found it.

Geometry lives in src/engine/timelineSelect.ts with no DOM in it, which is
what makes the first suite possible.

🤖 Generated with Claude Code

The empty space on a lane was a second way to drag the playhead. It is now
the selection surface: a drag there draws a rubber band and takes every clip
it covers, on every lane it reaches. The playhead keeps its own surface — the
ruler above — and Shift+drag still marks the in/out range.

The habits are the file manager's: Ctrl+drag adds a band to what is already
selected, Ctrl+click adds or removes one clip, Shift+click takes everything
in the rectangle between the first selected clip and the clicked one, and a
plain click on a member of a group narrows it back down to that clip.

Ctrl+CLICK on empty space still closes the gap — only a Ctrl-DRAG is a band,
so nothing is taken away.

The band's anchor is a TIME, not a screen position: the wheel scrolls and
zooms the timeline mid-drag, and a pixel anchor would then sit at a different
moment than the one that was grabbed.

A LOCKED track never enters a selection, not even through withLinked() —
that helper adds an A/V twin without looking at the lock, and the store
skips locked clips, so the pair would have come apart on the first move.

What can be done to one clip could already be done to many (move, delete,
copy, split, link): the store has worked on the whole selection all along.
Two places had not caught up — deleting from a clip's context menu first
collapsed the selection to the clicked clip, and a group dragged past t=0
piled onto zero instead of stopping there, because setClipStarts clamps each
clip on its own.

windowDrag now also listens for pointercancel, for every drag in the file:
a gesture cancelled by the system never sends pointerup, and its overlay used
to stay on screen and keep following the mouse with no button held.

Checks: node scripts/check-select.mjs (the band, range and lock maths),
node scripts/e2e44.mjs (the gestures, text clips only — no media needed).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant