web: tweak scrolling behavior

This commit is contained in:
2026-05-02 17:53:45 -07:00
parent 815655a73c
commit 2125c5dfa4

View File

@@ -452,6 +452,7 @@ export default function App() {
const searchRunCounterRef = useRef(0);
const shouldAutoScrollRef = useRef(true);
const wasSendingRef = useRef(false);
const pendingReplyScrollRef = useRef(false);
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
const [sidebarQuery, setSidebarQuery] = useState("");
@@ -643,6 +644,12 @@ export default function App() {
}, [providerModelPreferences]);
const selectedKey = selectedItem ? `${selectedItem.kind}:${selectedItem.id}` : null;
const isChatReplyStreamingInView =
isSending &&
draftKind !== "search" &&
selectedItem?.kind !== "search" &&
!!pendingChatState &&
(!pendingChatState.chatId || (selectedItem?.kind === "chat" && selectedItem.id === pendingChatState.chatId));
useEffect(() => {
shouldAutoScrollRef.current = true;
@@ -675,11 +682,27 @@ export default function App() {
if (draftKind === "search" || selectedItem?.kind === "search") return;
const wasSending = wasSendingRef.current;
wasSendingRef.current = isSending;
if (wasSending && !isSending) return;
if (isSending) return;
if (wasSending) {
shouldAutoScrollRef.current = false;
return;
}
if (!shouldAutoScrollRef.current) return;
transcriptEndRef.current?.scrollIntoView({ behavior: isSending ? "smooth" : "auto", block: "end" });
transcriptEndRef.current?.scrollIntoView({ behavior: "auto", block: "end" });
}, [draftKind, selectedChat?.messages.length, isSending, selectedItem?.kind, selectedKey]);
useEffect(() => {
if (!isChatReplyStreamingInView || !pendingReplyScrollRef.current) return;
pendingReplyScrollRef.current = false;
shouldAutoScrollRef.current = true;
window.requestAnimationFrame(() => {
const container = transcriptContainerRef.current;
if (!container) return;
container.scrollTo({ top: container.scrollHeight, behavior: "smooth" });
});
}, [isChatReplyStreamingInView, pendingChatState?.chatId]);
useEffect(() => {
if (isSending) return;
const hasWorkspaceSelection = Boolean(selectedItem) || draftKind !== null;
@@ -697,13 +720,7 @@ export default function App() {
const messages = selectedChat?.messages ?? [];
const isSearchMode = draftKind ? draftKind === "search" : selectedItem?.kind === "search";
const isSearchRunning = isSending && isSearchMode;
const isSendingActiveChat =
isSending &&
!isSearchMode &&
!!pendingChatState &&
!!pendingChatState.chatId &&
selectedItem?.kind === "chat" &&
selectedItem.id === pendingChatState.chatId;
const isSendingActiveChat = isChatReplyStreamingInView;
const displayMessages = useMemo(() => {
if (!pendingChatState) return messages.filter(isDisplayableMessage);
if (pendingChatState.chatId) {
@@ -837,6 +854,8 @@ export default function App() {
}, [contextMenu]);
const handleSendChat = async (content: string) => {
pendingReplyScrollRef.current = true;
const optimisticUserMessage: Message = {
id: `temp-user-${Date.now()}`,
createdAt: new Date().toISOString(),
@@ -1424,7 +1443,7 @@ export default function App() {
<div
ref={transcriptContainerRef}
className="flex-1 overflow-y-auto px-4 pt-8 md:px-10 lg:px-14 pb-36 md:pb-44"
className="flex-1 overflow-y-auto px-4 pt-8 md:px-10 lg:px-14 pb-36 md:pb-44 [overflow-anchor:none]"
onScroll={() => {
const container = transcriptContainerRef.current;
if (!container) return;
@@ -1443,6 +1462,9 @@ export default function App() {
onStartChat={selectedSearch ? handleStartChatFromSearch : undefined}
/>
)}
{isChatReplyStreamingInView ? (
<div className="mx-auto mt-6 h-[52vh] min-h-72 max-h-[36rem] max-w-4xl" aria-hidden="true" />
) : null}
<div ref={transcriptEndRef} />
</div>