eleven / ocho Public

mac: re-pin the feed on a text-size change — a shrink could blank the pane (#429)

Changing the Text Size relayouts every row at once; when the content got
shorter the scroll offset stayed clamped past the new end, showing an
empty feed until the next message arrival's wedge check rescued it. Run
the same verified pin on chatFontSize changes: immediately when the
reader was at the bottom, and after a layout beat (only if the viewport
actually came up empty) when scrolled back, so history readers aren't
yanked to the newest message.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell c4bb7f2e710549619a56b287681ad154b0860a69 parent da99651
1 file changed, +19 −0
  • ios/LChatMac/NativeFeedView.swift +19 −0
diff --git a/ios/LChatMac/NativeFeedView.swift b/ios/LChatMac/NativeFeedView.swift
index a320116..0b214cf 100644
--- a/ios/LChatMac/NativeFeedView.swift
+++ b/ios/LChatMac/NativeFeedView.swift
@@ -310,6 +310,25 @@ struct NativeFeedView: View {
// Search hits land on their message: page history until the
// target is loaded, then jump-and-wave.
.onChange(of: app.nativeJump) { _, _ in consumeJump(proxy) }
+ // A Text Size change (⌘+/⌘−, Settings slider) relayouts every row
+ // at once; a shrink can leave the scroll offset clamped past the
+ // new content end — a fully blank feed, the same wedge the
+ // message-arrival handler guards against (and which un-wedged
+ // itself only on the next arrival). Re-pin immediately when the
+ // reader was at the bottom; from deep in history, give layout a
+ // beat and rescue only if the viewport actually came up empty, so
+ // a scrolled-back reader isn't yanked to the newest message.
+ .onChange(of: chatFontSize) { _, _ in
+ let nearBottom = model.messages.last.flatMap { frames.rows[$0.id]?.minY }
+ .map { $0 < frames.viewport.height + 150 } ?? false
+ if nearBottom {
+ pinUntilSettled(proxy)
+ return
+ }
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+ if frames.rows.isEmpty && !model.messages.isEmpty { pinUntilSettled(proxy) }
+ }
+ }
.onAppear {
pinUntilSettled(proxy)
consumeJump(proxy)