Repository navigation
feat(studio): alt marquee removes the covered items from the selection - #5680
Conversation
jrusso1020
left a comment
There was a problem hiding this comment.
Code verdict: approve-ready. I found no blocking issues. The one open question is whether Alt is the modifier we want, which is a product call, so I'm leaving the merge decision to the maintainers. Two small suggestions are below. Neither one blocks.
What I checked
utils/marqueeGeometry.ts:28:resolveMarqueeModeis now the only place that reads the modifiers, and Alt wins over Shift as the PR says. On the timeline, the mode set at pointerdown (Cmd/Ctrl givesadd) is still the base, and Shift or Alt held mid-drag overrides it (useTimelineRangeSelection.ts:218,:350). That matches how Shift already behaved. The edge auto-scroll path now carries Alt too, throughMarqueePointer.- Canvas subtract (
hooks/useDomSelection.ts:506): it filters the seeded group, clears the selection when nothing is left, and re-anchors the primary on the first survivor. With no hits, theselections.length === 0early return keeps the selection unchanged. That is correct, and it now also coverssubtractbecause it testsreplacerather than!additive. - Timeline subtract (
timelineMarquee.ts:129,useTimelineRangeSelection.ts:111): hits are deleted from the pre-drag base,survivingPrimarykeeps the old primary when it is still selected, and Escape still restores the snapshot. - Every in-repo caller of
applyMarqueeSelectionandonMarqueeSelectwas moved fromfalseto"replace"(useStudioUrlState.ts,useTimelineSelectionPreviewSync.ts,marqueeCommit.ts). I grepped and found none left on a boolean. - I checked Alt's existing meanings in Studio. Alt+click jumps to source and Alt+drag turns off snapping, but both apply only when the press lands on an element. A marquee only starts on empty canvas (
DomEditOverlay.tsx, the fresh hit-test beforemarquee.begin), so the two don't collide.
What I ran
viteston the 9 touched suites: 142/142 pass.tsc --noEmitforpackages/studiois clean.- Mutation check: I disabled the subtract branch in
computeMarqueeSelectionand the filter inapplyMarqueeSelection. That made 9 tests fail acrosstimelineMarquee,useTimelineRangeSelectionanduseDomSelection, so the new tests do catch the behavior.
Suggestions (non-blocking)
- Canvas candidate outlines while subtracting.
MarqueeOverlay.tsx:16-29turns every element the band touches red, including elements that aren't selected and that the release will leave alone. On the timeline the live selection itself shows what will happen, but on the canvas the red outlines suggest that more will be removed than really is. You could filtercandidateRectsdown to members of the current group in subtract mode, or keep unselected candidates in the neutral style. - Hosts that consume
useMarqueeGesturesand ignore the second argument. The API note covers the type change. One thing it doesn't cover: a host that reusesMarqueeOverlaywith this hook but decides add/replace on its own will now draw the red "remove" band under Alt, yet still commit a replace or add. That's worth noting in the release notes, so those hosts can either handlemodeor keep Alt out of the band.
Thanks for the careful scoping and the thorough tests.
— Rames
miguel-heygen
left a comment
There was a problem hiding this comment.
Thanks for the careful work here. resolveMarqueeMode as the single place that reads the modifiers is a nice shape, and the timeline tests drive the real pointer handlers, so they cover what people will actually do. I agree with the earlier review's two suggestions and won't repeat them. A few additions:
- Alt pressed at the start of a drag behaves differently on the two surfaces. On the timeline, the mode resolved at pointerdown becomes the base (
useTimelineRangeSelection.ts:350, then:218falls back to it), so if you press Alt, start dragging and then let go of Alt, the marquee keeps subtracting. On the canvas the mode is re-read from each move and from the release event (marqueeCommit.ts:283,:305), so the same gesture ends as a replace, and the selection becomes exactly the items you meant to remove. Either rule is reasonable, but it would be good for both surfaces to follow the same one, with a test that releases the modifier mid-drag to pin it. - Rebase needed.
useDomSelectionTypes.tsnow conflicts with a change onmainthat added aniframeparameter torefreshDomEditSelectionFromPreviewand removedrefreshDomEditGroupSelectionsFromPreview. It's an adjacent-line conflict: keep both ofmain's changes plus your newmodesignature. Everything else merges cleanly, and I found no caller added onmainsince your base that still passes a boolean. - CI hasn't run on this head yet, so a maintainer will need to start the workflows after the rebase.
None of this blocks on the code itself. I'm leaving a comment rather than an approval because of the conflict and the missing CI run.
Verdict: COMMENT
Reasoning: The code looks correct and well tested, but the branch conflicts with main and no CI has run at this head; one small cross-surface inconsistency is worth settling first.
|
Yes on Alt+marquee to subtract. Please make canvas and timeline behave the same way, then rebase. |
7925be6 to
0f7959e
Compare
|
Thanks both, and apologies for the slow turnaround. Rebased onto main (v0.9.10) with the
Typecheck, lint, format, both comment checks and the studio suite pass locally (8305 tests; the one failure is the pre-existing |
|
@miguel-heygen @jrusso1020 this is ready for another look: rebased onto main and both review rounds addressed (summary in the comment above). Could one of you start the CI workflows on the new head ( |
miguel-heygen
left a comment
There was a problem hiding this comment.
Thanks for the rebase and for addressing both earlier rounds. I ran it for real at this head and the behavior is right, so I'm approving and turning on auto-merge.
What I verified
- Alt released mid-drag gives a replace on the timeline, same as the canvas (the earlier point is fixed).
- Alt held through the drag: 4 selected became 3 live and stayed 3 on release; a band that covers nothing leaves the selection alone.
- The touched suites pass (149 tests); with the subtract path disabled, 11 of them fail across
timelineMarquee,useDomSelectionanduseTimelineRangeSelection.
One thing left before it can land: the required "Studio and player captures" check wants ## Before and ## After sections in the PR description (for changes under packages/studio). Add both headings on GitHub and drag a screenshot under each, for example the timeline with Alt held mid-drag and the selection after release. Once that check is green, auto-merge takes it in. "Studio: timeline viewport gate" also fails the same way on main, so that one isn't from your change.
|
Thanks @vincent65, this is approved. The one required check left is Studio and player captures: the body needs |
|
Thanks @miguel-heygen and @jrusso1020! Added |
|
Thanks @vincent65, the captures check passes now. GitHub is still holding the merge on stale check results from the Nov 4 run, so could you rebase on main and push once more? That gives it a clean set of checks; we'll approve the CI run and re-approve right after, and auto-merge is already on. |
Shift already adds marquee hits to the selection on the canvas and the timeline; there was no way to take a few items back out of a large selection. Both marquee paths now carry a MarqueeSelectionMode (replace | add | subtract) resolved from the pointer modifiers in one helper, with Alt winning over Shift. Subtracting the primary re-anchors on a surviving member; subtracting everything clears the selection. The band and candidate outlines turn the danger colour while subtracting. Closes heygen-com#5586
Both surfaces now let the modifiers held at release decide: the timeline re-applies the marquee from the pointerup event before it finishes, so letting go of Alt mid-drag turns a subtract back into a replace, as the canvas already did. Alt at pointerdown no longer bakes a timeline base mode; Cmd/Ctrl still sets add. While subtracting, the canvas outlines only candidates that are in the current selection, so the band never suggests removing something that is not selected. The timeline commit helpers move to timelineMarqueeCommit.ts to keep the hook under the file-size cap.
Head branch was pushed to by a user without write access
0f7959e to
f2554cf
Compare
|
Done, @jrusso1020: rebased onto current main ( One note: the push switched auto-merge off on GitHub's side, so it will need turning back on after the re-approval. All eight workflows on the new head are waiting for approval. |
jrusso1020
left a comment
There was a problem hiding this comment.
Re-approving after the rebase onto main: the patch is byte-identical to the one reviewed.
Closes #5586
What
Holding Alt while dragging a marquee now removes the covered items from the current selection, on both the canvas and the timeline. Shift still adds, no modifier still replaces, and Alt wins when both are held so you can flip from adding to removing without releasing Shift.
utils/marqueeGeometry.tsgainsMarqueeSelectionMode(replace | add | subtract) andresolveMarqueeMode(modifiers, base), the one place the modifiers are read. Both marquee paths thread this mode instead of the oldadditive: boolean.useMarqueeGesturescommits with the mode its modifiers select and tracks it live for the band;applyMarqueeSelectioninuseDomSelectionfilters the covered selections out of the group in subtract mode, re-anchors the primary on a survivor if the primary was removed, and clears the selection when nothing is left.computeMarqueeSelectiontakes amodeand removes hits frombaseSelectionwhen subtracting;useTimelineRangeSelectionpicks Alt up at pointerdown and mid-drag, the way Shift already is, and resolves the primary throughsurvivingPrimary. Escape still restores the pre-drag selection.border-dangeron the canvas, a new--timeline-danger-filltoken next to--timeline-dangeron the timeline). Both bands exposedata-marquee-modefor tests.Release note for hosts
onMarqueeSelect/applyMarqueeSelection/onMarqueeSelectRefreceivemode: MarqueeSelectionMode("replace" | "add" | "subtract") instead ofadditive: boolean;marqueeRectis aMarqueeBand(aRectplusmode).MarqueeOverlaywithuseMarqueeGesturesbut decides add or replace on its own will now see the red "remove" band when the user holds Alt. Either handlemodein the host's commit callback, or keep Alt out of the band by passing amarqueeRectwhosemodeyou override.addas the base, as before.API note
onMarqueeSelectonDomEditOverlay,onMarqueeSelectRefonuseMarqueeGestures, andapplyMarqueeSelectionnow receivemode: MarqueeSelectionModeinstead ofadditive: boolean, andmarqueeRectis aMarqueeBand(aRectplusmode).MarqueeBand,MarqueeSelectionModeandresolveMarqueeModeare exported from the package root for hosts such as the Desktop app.Before
Same project and gesture on
main: all four clips selected, then an Alt+drag across Logo on the timeline. Alt is ignored, so the marquee replaces the selection and only Logo is left selected.Starting selection (same on both branches):
After the Alt+drag on
main:After
With this PR, the same Alt+drag shows the red subtract band, and Logo leaves the selection live while the drag is still going (3 elements selected):
After release with Alt still held, Intro, Tagline and Outro stay selected:
Walkthrough video
A 50-second explainer of this change, made with HyperFrames itself (the
pr-to-videoworkflow, built from this PR's diff): watch pr-5680-alt-marquee.mp4 (6 MB, 1080p).How I tested
marqueeGeometry.test.ts(mode resolution),marqueeCommit.test.tsx(mode committed on release, live band mode),useDomSelection.test.ts(subtract members, re-anchor primary, clear on empty, no-op on outsiders),timelineMarquee.test.ts(subtract andsurvivingPrimary),useTimelineRangeSelection.test.tsx(Shift add, Alt subtract at pointerdown and mid-drag, cancel restores).bun run --cwd packages/studio typecheck,bun run lint,bun run format:check,node scripts/check-comment-citations.mjsandnode scripts/comment-ratchet.mjspass.bun run --cwd packages/studio test: the touched suites pass; two unrelated tests fail on my machine onmainas well (useTimelineGroupEditing.test.tsxwith abase64urlencoding error from the runtime, and the timing-sensitiveversions.test.mjs).swiss-gridexample (hyperframes preview): a plain marquee on the timeline selected Intro and Graphics, then an Alt marquee over Intro left only Graphics selected with the band drawn in red. On the canvas, a plain marquee selected the stat card and the caption, then an Alt marquee over the card left only the caption selected and re-anchored the primary on it.🤖 Generated with Claude Code