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:
@@ -1021,6 +1021,10 @@ const TimelineList = React.memo(({
|
||||
rowContext,
|
||||
}: TimelineListProps) => {
|
||||
const listRef = React.useRef<LegendListRef | null>(null);
|
||||
// With streaming auto-follow off, content growth must never move the
|
||||
// viewport; explicit commands (the scroll-to-bottom pill, session open)
|
||||
// still scroll through the imperative handle.
|
||||
const streamingAutoFollowEnabled = useUIStore((state) => state.streamingAutoFollowEnabled);
|
||||
const isAtEndRef = React.useRef(true);
|
||||
|
||||
const setListRef = React.useCallback((list: LegendListRef | null) => {
|
||||
@@ -1067,7 +1071,7 @@ const TimelineList = React.memo(({
|
||||
contentInsetEndAdjustment={composerOverlayHeight}
|
||||
// While a turn is anchored, the reserved end space — not the
|
||||
// live edge — defines where the viewport rests.
|
||||
maintainScrollAtEnd={anchoredEndSpace
|
||||
maintainScrollAtEnd={anchoredEndSpace || !streamingAutoFollowEnabled
|
||||
? false
|
||||
: { animated: false, on: { dataChange: true, itemLayout: true, layout: true } }}
|
||||
// Prepending older history must not move what the user is
|
||||
|
||||
@@ -302,6 +302,8 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
const sessionGoalDefaultBudget = useUIStore(state => state.sessionGoalDefaultBudget);
|
||||
const setSessionGoalDefaultBudget = useUIStore(state => state.setSessionGoalDefaultBudget);
|
||||
const setShowReasoningTraces = useUIStore(state => state.setShowReasoningTraces);
|
||||
const streamingAutoFollowEnabled = useUIStore(state => state.streamingAutoFollowEnabled);
|
||||
const setStreamingAutoFollowEnabled = useUIStore(state => state.setStreamingAutoFollowEnabled);
|
||||
const collapsibleThinkingBlocks = useUIStore(state => state.collapsibleThinkingBlocks);
|
||||
const setCollapsibleThinkingBlocks = useUIStore(state => state.setCollapsibleThinkingBlocks);
|
||||
|
||||
@@ -1839,6 +1841,20 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
)}
|
||||
</SettingsSection>
|
||||
)}
|
||||
<SettingsSection
|
||||
title={t('settings.openchamber.visual.section.streaming')}
|
||||
settingsItem="chat.streaming"
|
||||
contentClassName={SETTINGS_OPTION_STACK_CLASS}
|
||||
>
|
||||
<SettingsCheckboxRow
|
||||
checked={streamingAutoFollowEnabled}
|
||||
onChange={setStreamingAutoFollowEnabled}
|
||||
label={t('settings.openchamber.visual.field.streamingAutoFollow')}
|
||||
ariaLabel={t('settings.openchamber.visual.field.streamingAutoFollowAria')}
|
||||
info={t('settings.openchamber.visual.field.streamingAutoFollowInfo')}
|
||||
settingsItem="chat.streaming-auto-follow"
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
{(shouldShow('collapsibleUserMessages') || shouldShow('stickyUserHeader') || (shouldShow('promptNavigatorEnabled') && !isVSCode) || shouldShow('wideChatLayout') || shouldShow('splitAssistantMessageActions') || shouldShow('codeBlockLineWrap')) && (
|
||||
<SettingsSection
|
||||
|
||||
Reference in New Issue
Block a user