diff --git a/echo/frontend/src/components/chat/AgenticChatPanel.tsx b/echo/frontend/src/components/chat/AgenticChatPanel.tsx index 554d7b58c..b79a5c443 100644 --- a/echo/frontend/src/components/chat/AgenticChatPanel.tsx +++ b/echo/frontend/src/components/chat/AgenticChatPanel.tsx @@ -20,7 +20,6 @@ import { UnstyledButton, } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; -import { ChatCircleTextIcon } from "@phosphor-icons/react"; import { IconAlertCircle, IconChevronDown, @@ -39,13 +38,18 @@ import { useState, } from "react"; import { useLocation } from "react-router"; +import { + ChatComposerShell, + ConversationFocusChips, + ConversationPickerButton, +} from "@/components/chat/ChatComposer"; import { useChatHistory, useProjectChatContext, useUpdateChatMutation, } from "@/components/chat/hooks"; -import { InsertTemplateMenu } from "@/components/chat/InsertTemplateMenu"; import { consumeChatPrefill } from "@/components/chat/prefill"; +import { useStickToBottom } from "@/components/common/useStickToBottom"; import { ConversationLinks } from "@/components/conversation/ConversationLinks"; import { useClearChatContextMutation, @@ -54,8 +58,6 @@ import { import { ProjectConversationsPanel } from "@/components/conversation/ProjectConversationsPanel"; import { ErrorBoundary } from "@/components/error/ErrorBoundary"; import { GoalSuggestionCard } from "@/components/goal/GoalSuggestionCard"; -import { useElementOnScreen } from "@/hooks/useElementOnScreen"; -import { useI18nNavigate } from "@/hooks/useI18nNavigate"; import { useLanguage } from "@/hooks/useLanguage"; import { useWorkspace } from "@/hooks/useWorkspace"; import { useWorkspaceUsage } from "@/hooks/useWorkspaceUsage"; @@ -96,6 +98,7 @@ import { import { CanvasSuggestionCard } from "./CanvasSuggestionCard"; import { ChatAccordionItemMenu } from "./ChatAccordion"; import { ChatHistoryMessage } from "./ChatHistoryMessage"; +import { ChatTemplatesMenuConnected } from "./ChatTemplatesMenuConnected"; import { CustomVerificationTopicSuggestionCard } from "./CustomVerificationTopicSuggestionCard"; import { formatMessage } from "./chatUtils"; import { ChatTurnLimitCard, ChatUpgradeModal } from "./FreeTierChatGate"; @@ -564,14 +567,14 @@ const ToolActivityGroup = ({ } onClick={isSingle ? undefined : onToggle} > - - + + @@ -616,7 +619,6 @@ export const AgenticChatPanel = ({ const { iso639_1, language } = useLanguage(); const { workspace, workspaceId } = useWorkspace(); const location = useLocation(); - const navigate = useI18nNavigate(); // Seed question from the Ask home page (router state), consumed exactly once. const initialMessageRef = useRef( typeof (location.state as { initialMessage?: unknown } | null) @@ -665,11 +667,9 @@ export const AgenticChatPanel = ({ const stopArmedRunIdRef = useRef(null); const requestedStreamKeyRef = useRef(null); const currentRunIdRef = useRef(null); - const [scrollTargetRef, isVisible] = useElementOnScreen({ - root: null, - rootMargin: "-83px", - threshold: 0.1, - }); + const threadScrollRef = useRef(null); + const { isAtBottomRef, scrollToBottom, showScrollButton } = + useStickToBottom(threadScrollRef); useEffect(() => { currentRunIdRef.current = runId; @@ -1180,18 +1180,6 @@ export const AgenticChatPanel = ({ } }, [runStatus, stopStream]); - const scrollToBottom = useCallback( - (behavior: ScrollBehavior = "smooth") => { - window.requestAnimationFrame(() => { - scrollTargetRef.current?.scrollIntoView({ - behavior, - block: "end", - }); - }); - }, - [scrollTargetRef], - ); - // Stick to the bottom only when the reader is already there (or just sent a // message). Someone scrolled up to read must never be yanked back down by a // streaming event; the scroll-to-bottom button is their way back. Bottomness @@ -1200,10 +1188,6 @@ export const AgenticChatPanel = ({ // bounced against the stream. const hasScrolledInitiallyRef = useRef(false); const forceNextScrollRef = useRef(false); - const isAtBottomRef = useRef(true); - useEffect(() => { - isAtBottomRef.current = isVisible; - }, [isVisible]); // biome-ignore lint/correctness/useExhaustiveDependencies: chatId is the trigger, not a read — switching chats re-arms the initial jump useEffect(() => { hasScrolledInitiallyRef.current = false; @@ -1466,29 +1450,11 @@ export const AgenticChatPanel = ({ - - This is the new chat experience} - openDelay={300} - > - - - - - + + {error && ( )} -