Skip to content

fix: clamp preview scroll so over-scrolling doesn't dead-zone scroll-up#11

Merged
brtkwr merged 1 commit into
mainfrom
fix/preview-scroll-clamp
Jun 22, 2026
Merged

fix: clamp preview scroll so over-scrolling doesn't dead-zone scroll-up#11
brtkwr merged 1 commit into
mainfrom
fix/preview-scroll-clamp

Conversation

@brtkwr

@brtkwr brtkwr commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

What

The last finding from the codebase review (Yoda lens).

renderPreview clamped previewScroll with m.previewScroll = max(0, len(msgLines)-1) - but renderPreview has a value receiver, so that write hits a copy and is discarded. The model's previewScroll therefore grew unbounded on pgdown / wheel-down past the content. The render itself never panicked (it re-clamps a local copy each frame before slicing), but after over-scrolling, scrolling back up was dead for many keypresses while previewScroll unwound from a huge value.

Fix

  • Extract the preview line builder into buildPreviewLines(conv, query), shared by renderPreview and a new maxPreviewScroll().
  • Bound the scroll in Update (pgdown/ctrl+j and wheel-down) via min(previewScroll+n, maxPreviewScroll()) - where the mutation actually persists.
  • renderPreview now clamps a local var only, making the value-receiver constraint explicit instead of silently discarding the write.

Tests

  • TestPreviewScrollClampedToContent - hammers pgdown 100× and asserts previewScroll never exceeds maxPreviewScroll, settles at it, and that a single pgup from the bottom visibly moves (no dead zone). Fails under the old discarded-clamp code.
  • TestUpdateMouseScroll fixtures given real messages (an empty conversation now correctly can't scroll).
  • TestRenderPreview unchanged and passing - confirms the extraction preserved rendering. Coverage up to 70.9%.

renderPreview clamped previewScroll on a value receiver, so the write
was discarded and the model's previewScroll grew unbounded on pgdown /
wheel-down past the content. The render survived (it re-clamped a local
copy each frame), but after over-scrolling, scrolling back up was dead
for many keypresses while previewScroll unwound from a huge value.

Extract the preview line builder (buildPreviewLines) so Update can bound
the scroll against the real line count via maxPreviewScroll, where the
mutation actually persists. renderPreview now clamps a local var only,
making the value-receiver constraint explicit.
@brtkwr brtkwr merged commit ae9ab0b into main Jun 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant