diff --git a/apps/web/components/chat/index.tsx b/apps/web/components/chat/index.tsx index 40e112e3..9b29e248 100644 --- a/apps/web/components/chat/index.tsx +++ b/apps/web/components/chat/index.tsx @@ -248,6 +248,11 @@ export function ChatSidebar({ id: string messages: UIMessage[] } | null>(null) + const [loadedThreadScrollTarget, setLoadedThreadScrollTarget] = useState<{ + id: string + messageCount: number + lastMessageId: string | null + } | null>(null) // Adjust chat height based on scroll position (desktop only, grid mode only) useEffect(() => { @@ -298,6 +303,13 @@ export function ChatSidebar({ useEffect(() => { if (pendingThreadLoad && currentChatId === pendingThreadLoad.id) { setMessages(pendingThreadLoad.messages) + setLoadedThreadScrollTarget({ + id: pendingThreadLoad.id, + messageCount: pendingThreadLoad.messages.length, + lastMessageId: + pendingThreadLoad.messages[pendingThreadLoad.messages.length - 1]?.id ?? + null, + }) setPendingThreadLoad(null) } }, [currentChatId, pendingThreadLoad, setMessages]) @@ -653,15 +665,39 @@ export function ChatSidebar({ } }, [queuedMessage]) - // Scroll to bottom when a new user message is added + // Scroll to bottom when a new user message is added or a thread is loaded useEffect(() => { + const lastMessageId = messages[messages.length - 1]?.id ?? null + const loadedThreadIsRendered = + loadedThreadScrollTarget && + currentChatId === loadedThreadScrollTarget.id && + messages.length === loadedThreadScrollTarget.messageCount && + lastMessageId === loadedThreadScrollTarget.lastMessageId + + if (loadedThreadIsRendered) { + // Trigger the same scroll behavior as the button after loaded messages render. + scrollToBottom() + setTimeout(scrollToBottom, 50) + setTimeout(scrollToBottom, 150) + setTimeout(() => { + scrollToBottom() + setLoadedThreadScrollTarget(null) + }, 300) + return + } const lastMessage = messages[messages.length - 1] if (lastMessage?.role === "user" && messagesContainerRef.current) { scrollToBottom() } else { checkIfScrolledToBottom() } - }, [messages, checkIfScrolledToBottom, scrollToBottom]) + }, [ + currentChatId, + loadedThreadScrollTarget, + messages, + checkIfScrolledToBottom, + scrollToBottom, + ]) useEffect(() => { const isStreaming = status === "streaming" diff --git a/apps/web/components/dashboard-view.tsx b/apps/web/components/dashboard-view.tsx index 38f13ee3..6cfe2e51 100644 --- a/apps/web/components/dashboard-view.tsx +++ b/apps/web/components/dashboard-view.tsx @@ -268,6 +268,22 @@ const PROFESSION_LABELS: { { value: "medical", label: "Medical" }, ] +function getChangeProfessionPrompt(profession: Profession): string { + const prompts: Record, string> = { + developer: "Not a developer?", + research: "Not a researcher?", + finance: "Not in finance?", + design: "Not a designer?", + legal: "Not in the legal field?", + marketing: "Not in marketing?", + medical: "Not in healthcare?", + } + if (profession === "default") { + return "Not your role?" + } + return prompts[profession] ?? "Not your role?" +} + // Static plugin metadata — shared between PluginPromoCard and RecommendedPluginsCard const PLUGIN_STATIC = [ { @@ -446,11 +462,7 @@ function RecommendedPluginsCard({ onClick={() => setIsEditing(true)} className="text-left px-2 pb-1 text-[10px] text-fg-faint hover:text-fg-muted transition-colors cursor-pointer" > - Not a{" "} - {PROFESSION_LABELS.find( - (p) => p.value === profession, - )?.label.toLowerCase()} - ? Change → + {getChangeProfessionPrompt(profession)} Change role → )}