diff --git a/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift b/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift index c7f0f63..75c1d5d 100644 --- a/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift +++ b/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift @@ -683,9 +683,15 @@ struct TranscriptList: View { } // Coalescing means item count lags event count; key the autoscroll on the raw stream // so every streamed delta keeps the view pinned to the bottom — but only while the - // user is already parked there. Scrolling up to read history is never yanked down. + // user is already parked there. Scrolling up to read history is never yanked down, + // and a finger actively on the transcript is never fought mid-drag (the drag that + // takes them past the unfollow threshold flips the gate; until then the native + // bottom anchor alone keeps content pinned, without an animated scroll grabbing the + // viewport back out of their hand). .onChange(of: events.count) { - if isScrolledToBottom { scrollToEnd(proxy, animated: !transcriptSettling) } + if isScrolledToBottom, !isUserScrolling { + scrollToEnd(proxy, animated: !transcriptSettling) + } } // An explicit jump — the chevron or sending a message — always wins. The chevron // itself lives in the parent's chat bar (above the composer), not as an overlay here,