web: tweak scrolling behavior
This commit is contained in:
@@ -452,6 +452,7 @@ export default function App() {
|
|||||||
const searchRunCounterRef = useRef(0);
|
const searchRunCounterRef = useRef(0);
|
||||||
const shouldAutoScrollRef = useRef(true);
|
const shouldAutoScrollRef = useRef(true);
|
||||||
const wasSendingRef = useRef(false);
|
const wasSendingRef = useRef(false);
|
||||||
|
const pendingReplyScrollRef = useRef(false);
|
||||||
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
|
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
|
||||||
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
|
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
|
||||||
const [sidebarQuery, setSidebarQuery] = useState("");
|
const [sidebarQuery, setSidebarQuery] = useState("");
|
||||||
@@ -643,6 +644,12 @@ export default function App() {
|
|||||||
}, [providerModelPreferences]);
|
}, [providerModelPreferences]);
|
||||||
|
|
||||||
const selectedKey = selectedItem ? `${selectedItem.kind}:${selectedItem.id}` : null;
|
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(() => {
|
useEffect(() => {
|
||||||
shouldAutoScrollRef.current = true;
|
shouldAutoScrollRef.current = true;
|
||||||
@@ -675,11 +682,27 @@ export default function App() {
|
|||||||
if (draftKind === "search" || selectedItem?.kind === "search") return;
|
if (draftKind === "search" || selectedItem?.kind === "search") return;
|
||||||
const wasSending = wasSendingRef.current;
|
const wasSending = wasSendingRef.current;
|
||||||
wasSendingRef.current = isSending;
|
wasSendingRef.current = isSending;
|
||||||
if (wasSending && !isSending) return;
|
if (isSending) return;
|
||||||
|
if (wasSending) {
|
||||||
|
shouldAutoScrollRef.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!shouldAutoScrollRef.current) 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]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (isSending) return;
|
if (isSending) return;
|
||||||
const hasWorkspaceSelection = Boolean(selectedItem) || draftKind !== null;
|
const hasWorkspaceSelection = Boolean(selectedItem) || draftKind !== null;
|
||||||
@@ -697,13 +720,7 @@ export default function App() {
|
|||||||
const messages = selectedChat?.messages ?? [];
|
const messages = selectedChat?.messages ?? [];
|
||||||
const isSearchMode = draftKind ? draftKind === "search" : selectedItem?.kind === "search";
|
const isSearchMode = draftKind ? draftKind === "search" : selectedItem?.kind === "search";
|
||||||
const isSearchRunning = isSending && isSearchMode;
|
const isSearchRunning = isSending && isSearchMode;
|
||||||
const isSendingActiveChat =
|
const isSendingActiveChat = isChatReplyStreamingInView;
|
||||||
isSending &&
|
|
||||||
!isSearchMode &&
|
|
||||||
!!pendingChatState &&
|
|
||||||
!!pendingChatState.chatId &&
|
|
||||||
selectedItem?.kind === "chat" &&
|
|
||||||
selectedItem.id === pendingChatState.chatId;
|
|
||||||
const displayMessages = useMemo(() => {
|
const displayMessages = useMemo(() => {
|
||||||
if (!pendingChatState) return messages.filter(isDisplayableMessage);
|
if (!pendingChatState) return messages.filter(isDisplayableMessage);
|
||||||
if (pendingChatState.chatId) {
|
if (pendingChatState.chatId) {
|
||||||
@@ -837,6 +854,8 @@ export default function App() {
|
|||||||
}, [contextMenu]);
|
}, [contextMenu]);
|
||||||
|
|
||||||
const handleSendChat = async (content: string) => {
|
const handleSendChat = async (content: string) => {
|
||||||
|
pendingReplyScrollRef.current = true;
|
||||||
|
|
||||||
const optimisticUserMessage: Message = {
|
const optimisticUserMessage: Message = {
|
||||||
id: `temp-user-${Date.now()}`,
|
id: `temp-user-${Date.now()}`,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
@@ -1424,7 +1443,7 @@ export default function App() {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
ref={transcriptContainerRef}
|
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={() => {
|
onScroll={() => {
|
||||||
const container = transcriptContainerRef.current;
|
const container = transcriptContainerRef.current;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
@@ -1443,6 +1462,9 @@ export default function App() {
|
|||||||
onStartChat={selectedSearch ? handleStartChatFromSearch : undefined}
|
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 ref={transcriptEndRef} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user