fix: prevent scroll snapback when using Space/PgUp/PgDown#105
fix: prevent scroll snapback when using Space/PgUp/PgDown#105benvinegar merged 1 commit intomainfrom
Conversation
Greptile SummaryThis PR addresses two distinct concerns: (1) a scroll snapback bug where Space/PgUp/PgDown and new keyboard shortcuts would snap the view back to the selected hunk during free scrolling, and (2) exposing TTY device path and tmux pane metadata in session registration so agents can better identify which terminal a live Hunk session is running in. Key changes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant Keyboard as Keyboard / Space / PgDn
participant DiffPane
participant ScrollBox
participant PollingTimer as 50ms Polling Timer
Note over DiffPane: prevSelectedAnchorIdRef = null
User->>Keyboard: Navigate to hunk B
Keyboard->>DiffPane: selectedAnchorId changes → "hunk-B"
DiffPane->>DiffPane: isSelectionChange = true<br/>prevSelectedAnchorIdRef = "hunk-B"
DiffPane->>ScrollBox: scrollSelectionIntoView() (immediate)
DiffPane->>DiffPane: schedule retries [0ms, 16ms, 48ms]
User->>Keyboard: Press Space / PgDn (free scroll)
Keyboard->>ScrollBox: Scroll viewport down
Note over ScrollBox: selectedAnchorId unchanged = "hunk-B"
PollingTimer->>DiffPane: scrollViewport.top updated
DiffPane->>DiffPane: selectedEstimatedScrollTop recalculates
DiffPane->>DiffPane: useLayoutEffect fires<br/>isSelectionChange = false → early return
Note over DiffPane: ✅ No snapback — view stays where user scrolled
|
src/mcp/sessionRegistration.ts
Outdated
| try { | ||
| const result = spawnSync("tty", [], { stdio: ["inherit", "pipe", "pipe"] }); | ||
| const name = result.stdout?.toString().trim(); | ||
| return name && !name.startsWith("not a tty") ? name : undefined; | ||
| } catch { | ||
| return undefined; |
There was a problem hiding this comment.
spawnSync result error not checked before reading stdout
spawnSync does not throw on spawn failure (e.g. tty binary not found on PATH); instead it sets result.error. The code already handles a null/undefined result.stdout via optional chaining, so there is no crash risk — but on some stripped-down environments result.stdout may be an empty Buffer (not null) even when result.error is set, which means name would be "" and the function would return undefined correctly anyway. Still, explicitly checking result.error makes the intent clearer and more robust:
| try { | |
| const result = spawnSync("tty", [], { stdio: ["inherit", "pipe", "pipe"] }); | |
| const name = result.stdout?.toString().trim(); | |
| return name && !name.startsWith("not a tty") ? name : undefined; | |
| } catch { | |
| return undefined; | |
| try { | |
| const result = spawnSync("tty", [], { stdio: ["inherit", "pipe", "pipe"] }); | |
| if (result.error) { | |
| return undefined; | |
| } | |
| const name = result.stdout?.toString().trim(); | |
| return name && !name.startsWith("not a tty") ? name : undefined; | |
| } catch { | |
| return undefined; | |
| } |
3ff90b2 to
09e9f38
Compare
The useLayoutEffect that scrolls the selected hunk into view was firing whenever selectedEstimatedScrollTop changed, which happens during normal scrolling when agent notes are visible (metrics recalculate as viewport changes). Fix: Track previous selectedAnchorId and only auto-scroll when the selection actually changes, not when layout metrics update during free scrolling. Closes scroll snapback issue with Space, PageUp, PageDown keys.
09e9f38 to
ab92fbf
Compare
Summary
Fixes scroll snapback issue where manually scrolling with Space, PageUp, PageDown, or the new navigation shortcuts (d/u/f/Shift+Space) would cause the view to snap back to the selected hunk.
Root Cause
The
useLayoutEffectthat scrolls the selected hunk into view was firing wheneverselectedEstimatedScrollTopchanged. During scrolling, this metric constantly recalculates due to a dependency chain:scrollViewport.topvisibleViewportFileIdsto recalculatevisibleAgentNotesByFilesectionMetricsto recalculateselectedEstimatedScrollTop[0, 16, 48]The Fix
Added a ref to track
prevSelectedAnchorIdand only auto-scroll when the selection actually changes, not when layout metrics update during free scrolling.Testing
Typecheck & Tests