Skip to content

fix(scroll): keep mouse wheel / trackball scrollable past a stale-small clamp#71

Merged
luca-chen198 merged 1 commit into
mainfrom
fix/discrete-device-scroll-clamp
Jun 16, 2026
Merged

fix(scroll): keep mouse wheel / trackball scrollable past a stale-small clamp#71
luca-chen198 merged 1 commit into
mainfrom
fix/discrete-device-scroll-clamp

Conversation

@luca-chen198

Copy link
Copy Markdown
Member

Problem

Discrete-delta input devices (classic mouse wheel, Ploopy trackball) intermittently can't scroll the editor; a brief trackpad scroll "revives" it.

Root cause

ClampedScrollView.scrollWheel runs clampToInsets() after every event, clamping the max scroll against scrollableContentHeight, which derives from a cached TextKit-2 measurement (baseContentHeight) that can under-measure / settle on a too-small value. A continuous trackpad gesture keeps firing relayout/recalcOverscroll mid-gesture and refreshes that cache, so it escapes the clamp. A discrete device delivers a single event with no relayout in between, so a stale-small height clamps each tick straight back to the same spot → "scroll doesn't work".

Fix

In clampToInsets(), when a clamp-back is imminent (b.origin.y > realHeight - b.height), force one fresh full-layout re-measure and clamp against the corrected height — i.e. make the trackpad's free re-measure deterministic for discrete devices.

  • Self-limiting: only runs at the bottom edge, never during normal mid-document scrolling (zero steady-state cost).
  • Still clamps against the real content height, so short docs can't scroll into the click-below inflation area.

Invariants preserved

No scroll-into-empty for short docs, bottom overscroll + scroll-away header, reading-column centering, file-switch no-drift, live-resize save/restore — all unchanged (the fix only changes when the height is re-validated).

Tests

swift build clean; swift test 90/90 green.

🤖 Generated with Claude Code

…ll clamp

clampToInsets() clamps against scrollableContentHeight, derived from a cached TextKit-2 measurement that can under-measure. A continuous trackpad refreshes that cache mid-gesture, but a discrete device (classic mouse wheel, Ploopy trackball) sends one event with no relayout, so a stale-small height clamps every tick straight back = "scroll doesn't work". When a clamp-back is imminent, force one fresh full-layout re-measure and clamp against the corrected height. Self-limiting (only fires at the bottom) and still clamps to the real content height, so short docs can't scroll into the click-below inflation area.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@luca-chen198 luca-chen198 merged commit faa7b04 into main Jun 16, 2026
1 check passed
@luca-chen198 luca-chen198 deleted the fix/discrete-device-scroll-clamp branch June 16, 2026 20:37
luca-chen198 added a commit that referenced this pull request Jun 20, 2026
fitsContent height behavior (#75), BlockquoteStyle.extraLineHeight (#76), and fixes (#69/#70/#71/#73).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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