Problem
When scrolling with the mouse wheel, the viewport snaps back to the cursor position instead of scrolling freely. Users can only navigate with arrow keys. This affects both edit and view mode:
- Edit mode: Every keystroke calls
updateViewport() → SetContent(), which resets scroll position via GotoBottom() when yOffset > maxYOffset(). The auto-scroll-to-cursor logic then forces the viewport back to the cursor.
- View mode: Toggling a checklist checkbox calls
updateViewport(), which triggers the same SetContent() reset path.
Context
The root cause is in the forked viewport at fork/bubbles/viewport/viewport.go:259-261:
if m.YOffset() > m.maxYOffset() {
m.GotoBottom()
}
This runs inside SetContentLines() every time content is set. In edit mode, editor.go:1272-1273 calls SetContent() on every update, and the auto-scroll logic at lines 1285-1358 then overrides any manual scroll position.
View mode correctly skips auto-scroll (editor.go:1277-1280) but still hits the SetContent reset when checklist items are toggled.
Possible approaches
Approach A: Track manual scroll state
- How: Add a
userScrolled flag to the viewport. Set it on mouse wheel events, clear it on cursor movement / keystrokes. Skip auto-scroll-to-cursor when the flag is set. Preserve yOffset in SetContent() when flag is set.
- Pros: Clean separation of manual vs automatic scrolling
- Cons: Need to decide exactly when to clear the flag
- Files:
fork/bubbles/viewport/viewport.go, internal/editor/editor.go
Approach B: Save/restore yOffset around SetContent
- How: Before calling
SetContent(), save current yOffset. After setting content, restore it (clamped to new maxYOffset). Remove the GotoBottom() call from SetContentLines().
- Pros: Simpler, no new state to manage
- Cons: May cause viewport to show blank space if content shrinks
- Files:
fork/bubbles/viewport/viewport.go, internal/editor/editor.go
Approach C: Hybrid — preserve offset + debounce auto-scroll
- How: Remove
GotoBottom() from SetContentLines(), preserve yOffset. In edit mode, only auto-scroll to cursor after a keystroke/cursor-move, not on every updateViewport() call.
- Pros: Best UX — scroll stays put until user types
- Cons: Slightly more complex, needs careful event routing
- Files:
fork/bubbles/viewport/viewport.go, internal/editor/editor.go
Tasks
Test plan
Scope
Type: bug
Size: medium
Problem
When scrolling with the mouse wheel, the viewport snaps back to the cursor position instead of scrolling freely. Users can only navigate with arrow keys. This affects both edit and view mode:
updateViewport()→SetContent(), which resets scroll position viaGotoBottom()whenyOffset > maxYOffset(). The auto-scroll-to-cursor logic then forces the viewport back to the cursor.updateViewport(), which triggers the sameSetContent()reset path.Context
The root cause is in the forked viewport at
fork/bubbles/viewport/viewport.go:259-261:This runs inside
SetContentLines()every time content is set. In edit mode,editor.go:1272-1273callsSetContent()on every update, and the auto-scroll logic at lines 1285-1358 then overrides any manual scroll position.View mode correctly skips auto-scroll (
editor.go:1277-1280) but still hits theSetContentreset when checklist items are toggled.Possible approaches
Approach A: Track manual scroll state
userScrolledflag to the viewport. Set it on mouse wheel events, clear it on cursor movement / keystrokes. Skip auto-scroll-to-cursor when the flag is set. Preserve yOffset inSetContent()when flag is set.fork/bubbles/viewport/viewport.go,internal/editor/editor.goApproach B: Save/restore yOffset around SetContent
SetContent(), save current yOffset. After setting content, restore it (clamped to new maxYOffset). Remove theGotoBottom()call fromSetContentLines().fork/bubbles/viewport/viewport.go,internal/editor/editor.goApproach C: Hybrid — preserve offset + debounce auto-scroll
GotoBottom()fromSetContentLines(), preserve yOffset. In edit mode, only auto-scroll to cursor after a keystroke/cursor-move, not on everyupdateViewport()call.fork/bubbles/viewport/viewport.go,internal/editor/editor.goTasks
GotoBottom()call inSetContentLines()(viewport.go:259-261)SetContent()calls (clamp to new max)userScrolledstate to viewport, set on wheel eventsupdateViewport()Test plan
Scope
Type: bug
Size: medium