diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index a41d2e143..5b4e430d7 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -21,7 +21,9 @@ import { RefreshCwIcon, SquareIcon, } from "lucide-react"; +import Image from "next/image"; import type { FC } from "react"; +import { useAtomValue } from "jotai"; import { ComposerAddAttachment, ComposerAttachments, @@ -32,6 +34,7 @@ import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; +import { currentUserAtom } from "@/atoms/user/user-query.atoms"; export const Thread: FC = () => { return ( @@ -59,7 +62,11 @@ export const Thread: FC = () => { - + !thread.isEmpty}> +
+ +
+
@@ -80,62 +87,109 @@ const ThreadScrollToBottom: FC = () => { ); }; -const ThreadWelcome: FC = () => { - return ( -
-
-
-

- Hello there! -

-

- How can I help you today? -

-
-
- -
- ); +const getTimeBasedGreeting = (userEmail?: string): string => { + const hour = new Date().getHours(); + + // Extract first name from email if available + const firstName = userEmail + ? userEmail.split("@")[0].split(".")[0].charAt(0).toUpperCase() + + userEmail.split("@")[0].split(".")[0].slice(1) + : null; + + // Array of greeting variations for each time period + const morningGreetings = [ + "Good morning", + "Rise and shine", + "Morning", + "Hey there", + "Welcome back", + ]; + + const afternoonGreetings = [ + "Good afternoon", + "Afternoon", + "Hey there", + "Welcome back", + "Hope you're having a great day", + ]; + + const eveningGreetings = [ + "Good evening", + "Evening", + "Hey there", + "Welcome back", + "Hope you had a great day", + ]; + + const nightGreetings = [ + "Late night", + "Still up", + "Hey there", + "Welcome back", + "Burning the midnight oil", + ]; + + // Select a random greeting based on time + let greeting: string; + if (hour < 12) { + greeting = morningGreetings[Math.floor(Math.random() * morningGreetings.length)]; + } else if (hour < 17) { + greeting = afternoonGreetings[Math.floor(Math.random() * afternoonGreetings.length)]; + } else if (hour < 21) { + greeting = eveningGreetings[Math.floor(Math.random() * eveningGreetings.length)]; + } else { + greeting = nightGreetings[Math.floor(Math.random() * nightGreetings.length)]; + } + + // Add personalization with first name if available + if (firstName) { + return `${greeting}, ${firstName}!`; + } + + return `${greeting}!`; }; -const SUGGESTIONS = [ - { - title: "What's the weather", - label: "in San Francisco?", - prompt: "What's the weather in San Francisco?", - }, - { - title: "Explain React hooks", - label: "like useState and useEffect", - prompt: "Explain React hooks like useState and useEffect", - }, -] as const; - -const ThreadSuggestions: FC = () => { +const ThreadWelcome: FC = () => { + const { data: user } = useAtomValue(currentUserAtom); + return ( -
- {SUGGESTIONS.map((suggestion, index) => ( -
- - - -
- ))} +
+ {/* Greeting positioned near the composer */} +
+

+ {/** biome-ignore lint/a11y/noStaticElementInteractions: wrong lint error, this is a workaround to fix the lint error */} +
{ + const rect = e.currentTarget.getBoundingClientRect(); + const x = (e.clientX - rect.left - rect.width / 2) / 3; + const y = (e.clientY - rect.top - rect.height / 2) / 3; + e.currentTarget.style.setProperty("--mag-x", `${x}px`); + e.currentTarget.style.setProperty("--mag-y", `${y}px`); + }} + onMouseLeave={(e) => { + e.currentTarget.style.setProperty("--mag-x", "0px"); + e.currentTarget.style.setProperty("--mag-y", "0px"); + }} + > + SurfSense +
+ {getTimeBasedGreeting(user?.email)} +

+
+ {/* Composer centered in the middle of the screen */} +
+ +
); }; @@ -143,10 +197,10 @@ const ThreadSuggestions: FC = () => { const Composer: FC = () => { return ( - + { }) ); + // Check if composer text is empty + const isComposerEmpty = useAssistantState(({ composer }) => { + const text = composer.text?.trim() || ""; + return text.length === 0; + }); + + const isSendDisabled = hasProcessingAttachments || isComposerEmpty; + return (
@@ -183,19 +245,25 @@ const ComposerAction: FC = () => { )} !thread.isRunning}> - +