diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a422ee26..03f8c8724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [EE] Fixed Ask Sourcebot mermaid diagrams overflowing their container by contain-fitting them to both width and height, and made revealing a diagram from the answer jump it into view instantly to avoid over/undershooting. [#1373](https://github.com/sourcebot-dev/sourcebot/pull/1373) - Verified GitHub review webhook deliveries before processing them. [#1378](https://github.com/sourcebot-dev/sourcebot/pull/1378) - Passed Zoekt index parameters via argv to preserve revision names with punctuation. [#1376](https://github.com/sourcebot-dev/sourcebot/pull/1376) +- [EE] Fixed Ask Sourcebot chat jittering when opening a citation while scrolled to the bottom, by releasing the stick-to-bottom lock on citation selection. [#1406](https://github.com/sourcebot-dev/sourcebot/pull/1406) - [EE] Validated OAuth bearer token scopes before allowing access to the Sourcebot MCP resource server. [#1396](https://github.com/sourcebot-dev/sourcebot/pull/1396) - Added HTTP security headers to all web app responses. [#1407](https://github.com/sourcebot-dev/sourcebot/pull/1407) diff --git a/packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx b/packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx index ddfd0a151..f02dab643 100644 --- a/packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx +++ b/packages/web/src/ee/features/chat/components/chatThread/chatThread.tsx @@ -76,7 +76,7 @@ export const ChatThread = ({ const [isErrorBannerVisible, setIsErrorBannerVisible] = useState(false); const hasSubmittedInputMessage = useRef(false); const chatBoxRef = useRef(null); - const { scrollRef, contentRef, scrollToBottom, isAtBottom } = useStickToBottom({ initial: false }); + const { scrollRef, contentRef, scrollToBottom, isAtBottom, stopScroll } = useStickToBottom({ initial: false }); const { toast } = useToast(); const router = useRouter(); const [isContextSelectorOpen, setIsContextSelectorOpen] = useState(false); @@ -435,6 +435,7 @@ export const ChatThread = ({ isNetworkActive={isPairNetworkActive} isAwaitingToolApproval={isPairAwaitingToolApproval} sources={sources} + onStickToBottomRelease={stopScroll} /> {index !== messagePairs.length - 1 && ( diff --git a/packages/web/src/ee/features/chat/components/chatThread/chatThreadListItem.tsx b/packages/web/src/ee/features/chat/components/chatThread/chatThreadListItem.tsx index d6388443c..8813f152b 100644 --- a/packages/web/src/ee/features/chat/components/chatThread/chatThreadListItem.tsx +++ b/packages/web/src/ee/features/chat/components/chatThread/chatThreadListItem.tsx @@ -29,6 +29,9 @@ interface ChatThreadListItemProps { sources: Source[]; chatId: string; index: number; + // Releases the chat's stick-to-bottom lock. Called when a citation is + // selected so the citation-reveal scroll doesn't fight the auto-scroll. + onStickToBottomRelease?: () => void; } const ChatThreadListItemComponent = forwardRef(({ @@ -40,6 +43,7 @@ const ChatThreadListItemComponent = forwardRef { const leftPanelRef = useRef(null); const [leftPanelHeight, setLeftPanelHeight] = useState(null); @@ -55,8 +59,16 @@ const ChatThreadListItemComponent = forwardRef (hovered?.kind === 'reference' ? hovered.reference : undefined), [hovered]); const setSelectedReference = useCallback((reference?: Reference) => { + // Selecting a citation scrolls it into view (see the effect below). If the + // chat is stuck to the bottom, that scroll fights the stick-to-bottom + // auto-scroll and the view jitters. Release the lock on selection so the + // reveal scroll can settle. This mirrors the state entered when the user + // scrolls up manually. + if (reference) { + onStickToBottomRelease?.(); + } setSelected(reference ? { kind: 'reference', reference } : undefined); - }, []); + }, [onStickToBottomRelease]); const setHoveredReference = useCallback((reference?: Reference) => { setHovered(reference ? { kind: 'reference', reference } : undefined); }, []);