diff --git a/apps/web/app/onboarding/setup/chat-sidebar.tsx b/apps/web/app/onboarding/setup/chat-sidebar.tsx index c82528bf..b9d9bb81 100644 --- a/apps/web/app/onboarding/setup/chat-sidebar.tsx +++ b/apps/web/app/onboarding/setup/chat-sidebar.tsx @@ -7,6 +7,8 @@ import { Button } from "@ui/components/button" import { PanelRightCloseIcon, SendIcon } from "lucide-react" import { collectValidUrls } from "@/utils/url-helpers" import { $fetch } from "@lib/api" +import { cn } from "@lib/utils" +import { dmSansFont } from "@/utils/fonts" interface ChatSidebarProps { formData: { @@ -22,11 +24,17 @@ export function ChatSidebar({ formData }: ChatSidebarProps) { const [isChatOpen, setIsChatOpen] = useState(true) const [messages, setMessages] = useState< { - url: string - title?: string - description: string - fullContent: string + message: string type?: "formData" | "exa" | "memory" | "waiting" + memories?: { + url: string + title: string + description: string + fullContent: string + }[] + url?: string + title?: string + description?: string }[] >([]) const [isLoading, setIsLoading] = useState(false) @@ -48,82 +56,73 @@ export function ChatSidebar({ formData }: ChatSidebarProps) { setIsChatOpen(!isChatOpen) } - const pollForMemories = useCallback(async (documentIds: string[]) => { - const maxAttempts = 30 // 30 attempts * 3 seconds = 90 seconds max - const pollInterval = 3000 // 3 seconds + const pollForMemories = useCallback( + async (documentIds: string[]) => { + const maxAttempts = 30 // 30 attempts * 3 seconds = 90 seconds max + const pollInterval = 3000 // 3 seconds - for (let attempt = 0; attempt < maxAttempts; attempt++) { - try { - const response = await $fetch("@get/documents/:id", { - params: { id: documentIds[0] ?? "" }, - disableValidation: true, - }) + for (let attempt = 0; attempt < maxAttempts; attempt++) { + try { + const response = await $fetch("@get/documents/:id", { + params: { id: documentIds[0] ?? "" }, + disableValidation: true, + }) - console.log("response", response) + console.log("response", response) - if (response.data) { - const document = response.data + if (response.data) { + const document = response.data - if (document.memories && document.memories.length > 0) { - const newMemories: typeof messages = [] + if (document.memories && document.memories.length > 0) { + const newMemories: { + url: string + title: string + description: string + fullContent: string + }[] = [] - document.memories.forEach((memory) => { - if (!displayedMemoriesRef.current.has(memory.memory)) { - displayedMemoriesRef.current.add(memory.memory) - newMemories.push({ - url: document.url || "", - title: memory.title || document.title || "Memory", - description: memory.memory || "", - fullContent: memory.memory || "", - type: "memory" as const, - }) - } - }) - - if (newMemories.length > 0) { - setMessages((prev) => [...prev, ...newMemories]) - } - } - - if (document.memories && document.memories.length > 0) { - const waitingMessage = { - url: "", - title: "", - description: "Waiting for your input", - fullContent: "Waiting for your input", - type: "waiting" as const, - } - - setMessages((prev) => { - const withoutWaiting = prev.filter( - (msg) => msg.type !== "waiting", + document.memories.forEach( + (memory: { memory: string; title?: string }) => { + if (!displayedMemoriesRef.current.has(memory.memory)) { + displayedMemoriesRef.current.add(memory.memory) + newMemories.push({ + url: document.url || "", + title: memory.title || document.title || "Memory", + description: memory.memory || "", + fullContent: memory.memory || "", + }) + } + }, ) - return [...withoutWaiting, waitingMessage] - }) - break + + if (newMemories.length > 0 && messages.length < 10) { + setMessages((prev) => [ + ...prev, + { + message: newMemories + .map((memory) => memory.description) + .join("\n"), + type: "memory" as const, + memories: newMemories, + }, + ]) + } + } + + if (document.memories && document.memories.length > 0) { + break + } } + + await new Promise((resolve) => setTimeout(resolve, pollInterval)) + } catch (error) { + console.warn("Error polling for memories:", error) + await new Promise((resolve) => setTimeout(resolve, pollInterval)) } - - await new Promise((resolve) => setTimeout(resolve, pollInterval)) - } catch (error) { - console.warn("Error polling for memories:", error) - await new Promise((resolve) => setTimeout(resolve, pollInterval)) } - } - - const waitingMessage = { - url: "", - title: "", - description: "Waiting for your input", - fullContent: "Waiting for your input", - type: "waiting" as const, - } - - setMessages((prev) => { - const withoutWaiting = prev.filter((msg) => msg.type !== "waiting") - return [...withoutWaiting, waitingMessage] - }) - }, []) + }, + [messages.length], + ) useEffect(() => { if (!formData) return @@ -203,20 +202,20 @@ export function ChatSidebar({ formData }: ChatSidebarProps) { if (formData.twitter) { formDataMessages.push({ + message: `Twitter: ${formData.twitter}`, url: formData.twitter, title: "Twitter Profile", description: `Twitter: ${formData.twitter}`, - fullContent: formData.twitter, type: "formData" as const, }) } if (formData.linkedin) { formDataMessages.push({ + message: `LinkedIn: ${formData.linkedin}`, url: formData.linkedin, title: "LinkedIn Profile", description: `LinkedIn: ${formData.linkedin}`, - fullContent: formData.linkedin, type: "formData" as const, }) } @@ -224,20 +223,20 @@ export function ChatSidebar({ formData }: ChatSidebarProps) { if (formData.otherLinks.length > 0) { formData.otherLinks.forEach((link) => { formDataMessages.push({ + message: `Link: ${link}`, url: link, title: "Other Link", description: `Link: ${link}`, - fullContent: link, type: "formData" as const, }) }) } const waitingMessage = { + message: "Waiting for your input", url: "", title: "", description: "Waiting for your input", - fullContent: "Waiting for your input", type: "waiting" as const, } @@ -247,10 +246,10 @@ export function ChatSidebar({ formData }: ChatSidebarProps) { console.warn("Error processing content:", error) const waitingMessage = { + message: "Waiting for your input", url: "", title: "", description: "Waiting for your input", - fullContent: "Waiting for your input", type: "waiting" as const, } @@ -267,23 +266,30 @@ export function ChatSidebar({ formData }: ChatSidebarProps) { {!isChatOpen ? ( + Chat with Nova ) : ( Close chat -
+
{messages.length === 0 && !isLoading && !formData && ( -
+
Waiting for your input
)} {isLoading && ( -
- Fetching your memories... +
+ + Fetching your memories...
)} {messages.map((msg, i) => (
{msg.type === "waiting" ? (
- {msg.description} + {msg.message}
) : ( <> -
-
- {msg.title && ( -

- {msg.title} -

+
- {msg.url} - - )} - {msg.description && msg.type === "memory" && ( -

- {msg.description} -

+ > + {i === 0 && ( +
)} +
+ {msg.type === "memory" && ( +
+ {msg.memories?.map((memory) => ( +
+ {memory.title && ( +

+ {memory.title} +

+ )} + {memory.url && ( + + {memory.url} + + )} + {memory.description && ( +

+ {memory.description} +

+ )} +
+ ))} +
+ )} )}
diff --git a/apps/web/app/onboarding/setup/relatable-question.tsx b/apps/web/app/onboarding/setup/relatable-question.tsx index e48699d5..909c5574 100644 --- a/apps/web/app/onboarding/setup/relatable-question.tsx +++ b/apps/web/app/onboarding/setup/relatable-question.tsx @@ -4,6 +4,8 @@ import { useState } from "react" import { motion } from "motion/react" import { Button } from "@ui/components/button" import { useRouter } from "next/navigation" +import { cn } from "@lib/utils" +import { dmSansFont } from "@/utils/fonts" const relatableOptions = [ { @@ -53,7 +55,7 @@ export function RelatableQuestion() { Which of these sound most relatable? -
+
{relatableOptions.map((option, index) => (