feat(settings): add streaming auto-follow toggle

New Streaming section on the Chat settings page with a checkbox that
controls whether the viewport follows new content while a response
streams. Default stays on. With it off, the anchored user message still
parks at the top on send, but no glide or end-follow correction runs and
the list's maintain-scroll-at-end stays disabled; the scroll-to-bottom
pill and session open keep scrolling explicitly.

Persisted through desktop settings like the other chat toggles (auto-save
diff, authoritative apply, sanitize), registered in settings search, and
localized in every locale.
This commit is contained in:
Bohdan Triapitsyn
2026-08-25 14:53:43 +03:00
parent 87c1ae809d
commit dac31ee026
19 changed files with 109 additions and 1 deletions
@@ -3,6 +3,7 @@ import React from 'react';
import { MessageFreshnessDetector } from '@/lib/messageFreshness';
import { createScrollSpy } from '@/components/chat/lib/scroll/scrollSpy';
import { useViewportStore } from '@/sync/viewport-store';
import { useUIStore } from '@/stores/useUIStore';
import {
CHAT_LIST_ANCHOR_OFFSET,
getAnchoredTurnMetrics,
@@ -520,7 +521,15 @@ export const useChatTimelineScroll = ({
first: null,
second: null,
});
// User preference: with auto-follow off, streaming growth never moves the
// viewport — the anchored user message still parks at the top on send, but
// no glide or end-follow correction runs afterwards.
const streamingAutoFollowEnabled = useUIStore((state) => state.streamingAutoFollowEnabled);
const streamingAutoFollowEnabledRef = React.useRef(streamingAutoFollowEnabled);
streamingAutoFollowEnabledRef.current = streamingAutoFollowEnabled;
const onTimelineDataChange = React.useCallback(() => {
if (!streamingAutoFollowEnabledRef.current) return;
if (!isLiveFollowActive()) return;
// Following the end needs no animation frames: the totalSize listener