fix(media): drag selection follows the pointer beyond the pane and auto-scrolls at edges - #342
fix(media): drag selection follows the pointer beyond the pane and auto-scrolls at edges#342jlocala1 wants to merge 4 commits into
Conversation
…to-scrolls at edges Two defects made drag-highlight unreliable in the transcript editor: 1. onMouseLeave on the listbox called handleMouseUp, silently ending the drag the moment the pointer left the pane. 2. Selection only extended via per-word mouseenter, so padding, end-of-line whitespace, and anything outside the pane never extended it - and nothing scrolled while holding at an edge. The drag now attaches document-level mousemove/mouseup listeners for its lifetime: the selection extends to the word under the clamped pointer (elementFromPoint with fallback probes), the pane auto-scrolls while the pointer is held at or past an edge, releasing outside the window ends the drag (buttons === 0 guard), and leaving the pane only cancels the pending long-press, not the drag. Regression tests fail on the previous implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Improves transcript drag-selection behavior in MediaEditor so selections keep tracking the pointer outside the transcript pane and the pane auto-scrolls when dragging at its edges.
Changes:
- Switches drag-selection tracking to document-level
mousemove/mouseuplisteners for the lifetime of a drag. - Adds an animation-frame auto-scroll loop that scrolls the transcript pane when the pointer is held near/over the top/bottom edges.
- Adds regression tests covering selection extension outside the pane and ensuring
mouseleaveno longer cancels an in-progress drag.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/components/MediaEditor/MediaEditor.tsx | Implements document-level drag tracking, selection extension via elementFromPoint, and edge auto-scroll; removes onMouseLeave ending the drag. |
| src/components/MediaEditor/MediaEditor.test.tsx | Adds regression tests for dragging selection beyond the pane and preventing mouseleave from canceling a drag. |
elementFromPoint only resolves inside the viewport, but the transcript pane can extend beyond it (found on the deployed app at laptop viewport sizes: auto-scroll ran but the highlight stalled at the anchor). Probe points and the edge zones now use the intersection of the pane rect and the viewport, and a zero-area pane extends nothing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… sits in empty pane space Dragging below the last line of a short or fully scrolled transcript put the clamped probe point in the pane's empty bottom area where no word span exists, so the selection stalled. When every probe misses, extend to the last word above the point (or the first word when above all), matching standard text-editor drag behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
src/components/MediaEditor/MediaEditor.tsx:810
stepDragAutoScrollschedules a newrequestAnimationFrameunconditionally, even when the pointer is nowhere near an edge and no scrolling happens (step === 0). This creates a continuous 60fps loop for the entire drag, which is unnecessary work for long drags.
if (step !== 0) {
container.scrollTop += step;
extendSelectionToPointer();
}
dragScrollFrame.current = requestAnimationFrame(stepDragAutoScroll);
src/components/MediaEditor/MediaEditor.tsx:853
- If the auto-scroll loop is made conditional (only running while
step !== 0), it needs a way to restart when the pointer later moves back into an edge zone. A lightweight approach is to request a frame on mousemove only when no frame is currently queued.
if (e.buttons === 0) {
up();
return;
}
dragPointer.current = { x: e.clientX, y: e.clientY };
|
Two follow-up commits from testing on the deployed pulseclip dev instance:
Verified on the deployed instance: drag held below the viewport scrolls the pane to its limit and extends the selection to the final word (298/303 words, last word selected). All gates green, 399/399 tests. 🤖 Generated with Claude Code |
- Clear a pending long-press timer on unmount alongside the listeners and scroll frame - Clamp every probe X into the pane so narrow panes cannot resolve unrelated DOM - Guard the auto-scroll step against a fully offscreen pane (keep the loop alive, never scroll) - Extend to the first word when the pointer sits in the top padding above all spans, mirroring the past-the-end fallback Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem
Drag-selecting words in the MediaEditor transcript is unreliable at the pane boundaries:
Cause
onMouseLeaveon the listbox calledhandleMouseUp, silently ending the drag the moment the pointer left the pane.mouseenter, so padding, end-of-line whitespace, and anything outside the pane never extended it — and there was no auto-scroll loop at all.Fix
For the lifetime of one drag, document-level
mousemove/mouseuplisteners take over:elementFromPoint, with fallback probe points for whitespace misses), so it keeps tracking wherever the mouse goes.buttons === 0guard on mousemove).Tests
Two regression tests added — both fail against the previous implementation (verified by stashing the fix):
mouseleaveno longer cancels an in-progress dragGates: lint ✅ typecheck ✅ format ✅ 399/399 tests ✅
🤖 Generated with Claude Code