fix(chat): anchor prompt navigator to last turn at chat bottom (#2213)

* fix(chat): anchor prompt navigator to last turn at chat bottom

The scroll spy activates the last turn whose top edge crossed the reading
line (100px below the container top). A final turn short enough to fit
below that line could never become active — sitting at the very bottom of
the chat kept the previous prompt highlighted on the navigator rail.
When the container is scrolled to the bottom (within 8px), force the last
turn active.

* feat(chat): enable prompt navigator by default

Users who already persisted an explicit preference keep their choice.
This commit is contained in:
Bohdan Triapitsyn
2026-07-14 10:24:31 +03:00
committed by GitHub
parent a9e99a28ea
commit 4c27f1753d
2 changed files with 10 additions and 2 deletions
@@ -16,6 +16,11 @@ type ScrollSpyInput = {
// stable while scrolling inside a long turn (no visibility-ratio flip-flop).
const READ_LINE_OFFSET_PX = 100;
// When the container is scrolled to (or almost to) the bottom, the last turn
// is what the user is reading even if it is too short for its top edge to
// ever cross the reading line — force-activate it in that case.
const BOTTOM_ANCHOR_EPSILON_PX = 8;
const pickOffsetTurnId = (list: OffsetTurn[], cutoff: number): string | undefined => {
if (list.length === 0) {
return undefined;
@@ -98,7 +103,10 @@ export const createScrollSpy = (input: ScrollSpyInput) => {
refreshOffsets();
}
const next = pickOffsetTurnId(offsets, container.scrollTop + READ_LINE_OFFSET_PX);
const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
const next = distanceFromBottom <= BOTTOM_ANCHOR_EPSILON_PX
? offsets[offsets.length - 1]?.id
: pickOffsetTurnId(offsets, container.scrollTop + READ_LINE_OFFSET_PX);
if (!next || next === active) {
return;
}
+1 -1
View File
@@ -964,7 +964,7 @@ export const useUIStore = create<UIStore>()(
userMessageRenderingMode: 'markdown',
collapsibleUserMessages: true,
stickyUserHeader: false,
promptNavigatorEnabled: false,
promptNavigatorEnabled: true,
expandedEditorToolbar: false,
showSplitAssistantMessageActions: false,
allowPromptingSubagentSessions: false,