Skip to content

Steadier scrolling on ProMotion; let the reader scroll up during live text; 0.16.6 - #21

Merged
belugaming merged 3 commits into
mainfrom
claude/admiring-lovelace-9rinej
Oct 9, 2026
Merged

belugaming merged 3 commits into
mainfrom
claude/admiring-lovelace-9rinej

Conversation

@belugaming

Copy link
Copy Markdown
Owner

Every scrolling page judders on a macOS 27 MacBook Pro (M5 Max), including the plain-text live translation window. Two changes address this.

Ask for a steady refresh rate while scrolling

On macOS 27, a ProMotion panel does not settle on one refresh rate while a SwiftUI view scrolls. It flips between 120 Hz and 60 Hz, so each frame stays on screen for an unpredictable time. Apple has the report (FB24091347, Developer Forums thread 840214) and has not shipped a fix. The only confirmed workaround is setting the whole display to 60 Hz, which is a system setting and not something to ask of users.

An app's way to say which rate it wants is a display link with a preferred frame-rate range. ScrollFrameRate (in App/ScrollFrameRate.swift) runs one from the first scroll event until 0.4 s after the last, momentum included. It asks for 120 Hz (range 80–120, preferred 120), and the link pauses when the page stops, so an idle window costs nothing.

defaults write com.beluga.classnote scrollFrameRate -int 60 asks for a steady 60 Hz instead, and 0 turns the request off. Either takes effect from the next launch, so the options can be compared on the affected Mac without a new build.

Not yet verified on the affected hardware. CI builds and tests on macos-15 without a ProMotion panel. Whether macOS 27 honours the request for scrolling has to be checked on the M5 Max.

Let the reader scroll up while text is still coming in

The live window scrolled to its end on every new word, and Ask AI did the same on every token of an answer. That happened several times a second, so scrolling up was undone at once.

Both views now follow the end only while the reader is at it:

  • Scrolling up stops following.
  • Scrolling back to the end resumes it.
  • Asking a question follows the answer.

This uses onScrollGeometryChange, which needs macOS 15; on macOS 14 the views follow as before.

The live window's sentences are now Equatable on what they show, so a word added to the draft line no longer redraws every sentence on screen.

Tests

  • BottomFollowTests: when following stops and resumes, including new text arriving under a reader who has not moved.
  • ScrollFrameRateTests: the range asked for at each setting. A display link raises an exception on a malformed range, so every setting is checked for that.

Version 0.16.6 (build 24).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UP49VoRGBw2wv1CEAroky7


Generated by Claude Code

claude added 3 commits October 9, 2026 05:05
…oming in

The live window scrolled to its end on every new word, and the Q&A on every
token of an answer, several times a second, so scrolling up was undone at
once and the page looked like it could not be scrolled. Both now follow the
end only while the reader is at it: scrolling up stops following, getting
back to the end resumes it, and asking a question follows the answer. macOS
14 cannot read a scroll view's position, so it follows as before.

The live window's sentences are Equatable on what they show, so a word
added to the line still being spoken no longer redraws every sentence on
screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UP49VoRGBw2wv1CEAroky7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UP49VoRGBw2wv1CEAroky7
On macOS 27 a ProMotion panel does not settle on one refresh rate while a
SwiftUI view scrolls: it flips between 120 Hz and 60 Hz, each frame stays
on screen for an unpredictable time, and every page judders, plain text
included (FB24091347, Developer Forums thread 840214). The only confirmed
workaround is setting the whole display to 60 Hz.

An app can say which rate it wants: a display link with a preferred
frame-rate range is that request. One now runs from the first scroll event
until shortly after the last, momentum included, asking for 120 Hz, and
pauses when the page stops. `defaults write com.beluga.classnote
scrollFrameRate -int 60` asks for a steady 60 Hz instead and `0` turns it
off, from the next launch, so the two can be compared without a new build.

Version 0.16.6 (build 24).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UP49VoRGBw2wv1CEAroky7
@belugaming
belugaming merged commit b1fb0bc into main Oct 9, 2026
4 checks passed
belugaming pushed a commit that referenced this pull request Oct 9, 2026
The branch still carried #21's commits, which main has as a squash; this
keeps the branch's files exactly as they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UP49VoRGBw2wv1CEAroky7
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.

2 participants