Repository navigation
Description
Summary
While a selection drag is active, the renderer clamps the drag endpoint to the owning renderable's visible grid and delivers drag events to whatever renderable is under the pointer. It never scrolls the renderable that owns currentSelection. So a drag that leaves the renderable's bounds — the normal way to select more than one screen — cannot extend the selection beyond the visible area.
Confirmed with a terminal pane in opencode; the same applies to any scrollable, selectable renderable such as ScrollBox.
Evidence
Raw mouse capture of the real gesture (WezTerm, opencode 2.2.5): press at the bottom row, drag upward.
43 Cb=0 x=230 y=52 M press
44 Cb=32 x=230 y=51 M drag ┐
… │ y descends 52 → 1
66 Cb=32 x=212 y=1 M drag ┘ ← clamped at row 1, outside the pane
90 Cb=0 x=215 y=9 m release
No scroll occurs at any point. Control case: wheel with the button held and the pointer inside the pane scrolls normally, so local scroll in the embedded terminal and the renderer's scroll dispatch both work during a drag. The only missing behaviour is the out-of-bounds case.
Where it is
-
packages/core/src/renderables/EmbeddedTerminal.ts,onSelectionChangedclamps every endpoint:const point = (x: number, y: number) => { if (y < 0) return { x: 0, y: 0 } if (y >= this.height) return { x: this.width - 1, y: this.height - 1 } return { x: Math.max(0, Math.min(this.width - 1, x)), y } }
-
packages/core/src/renderer.ts,processSingleMouseEvent: a drag callsupdateSelection(maybeRenderable, x, y)and thensendMouseEvent(maybeRenderable, …)— the event goes to the hit target, not to the selection owner, so the owner is never told that the pointer left it. -
_latestPointeris private, so a host cannot read the pointer position and implement autoscroll itself.
Proposal
Either of these makes the gesture work; the first also fixes scrollboxes:
- Renderer autoscroll. While
currentSelection.isDraggingand the pointer is outside the owner's bounds, scroll the owner on a timer (roughly every 50–100 ms, proportional to distance past the edge) through a minimal contract on the renderable — for examplescrollBy(delta).EmbeddedTerminalRenderablealready has the operation internally (contextEmbeddedTerminalScroll), and scrollable containers have equivalents. Keep the selection endpoint pinned to the edge row while the content moves under it. - Expose the gesture. Dispatch drag events to the selection owner as well as the hit target, with unclamped coordinates (and/or expose the pointer publicly), so a host can implement autoscroll itself.
Notes
- Not opencode-specific: any host with a scrollable selectable region has this.
- Related: input: wheel events are decoded as drag while a mouse button is held, so nothing scrolls during a drag #1911 — retracted; the wheel is decoded correctly, the problem is that during an edge drag the pointer is outside the pane, so the wheel is hit-tested elsewhere. The terminal-pane symptom is tracked in tui: terminal pane viewport is frozen during a selection drag, so a selection can never extend past one screen opencode#61053.
- No new protocol is required; the scroll primitive already exists.
Activity
mleivaDesarrollos commentedon Nov 9, 2026
Checked whether a host could implement this itself today, so the scope here is not over-stated: it cannot, and #1716 does not change that.
processSingleMouseEventhandles an active selection drag in its own branch, before the pressed-renderable path:maybeRenderableishitTest(x, y), so once the pointer leaves the owner the owner receives nothing, and_latestPointeris private — there is no way for a host to notice the pointer and autoscroll on its own.One implementation detail worth pinning down while this is open: an autoscroll tick has to scroll and re-apply the selection. Scrolling alone moves the content under a fixed screen-space selection without re-running
onSelectionChanged, so the native selection keeps addressing the old cells and the range does not grow. Scroll, then re-apply with the current pointer, and the selected range extends by the scrolled amount — which is the behaviour users expect.