diff --git a/components/AtlasChat.tsx b/components/AtlasChat.tsx index 7acc6fb..a37b0ad 100644 --- a/components/AtlasChat.tsx +++ b/components/AtlasChat.tsx @@ -1,28 +1,19 @@ "use client"; import { useEffect, useRef } from "react"; +import { useSession } from "next-auth/react"; import { AssistantRuntimeProvider, useLocalRuntime, type ChatModelAdapter, } from "@assistant-ui/react"; import { Thread } from "@/components/assistant-ui/thread"; -import { Button } from "@/components/ui/button"; -import { RotateCcw } from "lucide-react"; - -// --------------------------------------------------------------------------- -// Props -// --------------------------------------------------------------------------- interface AtlasChatProps { projectId: string; projectName?: string; } -// --------------------------------------------------------------------------- -// Runtime adapter — calls our existing /api/projects/[id]/atlas-chat endpoint -// --------------------------------------------------------------------------- - function makeAtlasAdapter(projectId: string): ChatModelAdapter { return { async run({ messages, abortSignal }) { @@ -46,78 +37,49 @@ function makeAtlasAdapter(projectId: string): ChatModelAdapter { } const data = await res.json(); - - return { - content: [{ type: "text", text: data.reply || "…" }], - }; + return { content: [{ type: "text", text: data.reply || "…" }] }; }, }; } -// --------------------------------------------------------------------------- -// Inner component — has access to runtime context -// --------------------------------------------------------------------------- - function AtlasChatInner({ projectId, projectName, + userInitial, runtime, -}: AtlasChatProps & { runtime: ReturnType }) { +}: AtlasChatProps & { + userInitial: string; + runtime: ReturnType; +}) { const greeted = useRef(false); - // Send Atlas's opening message automatically on first load useEffect(() => { if (greeted.current) return; greeted.current = true; - - const opener = - `Hey — I'm starting a new project called "${projectName || "my project"}". I'd love your help defining what we're building.`; - - // Small delay so the thread is mounted before we submit + const opener = `Hey — I'm starting a new project called "${projectName || "my project"}". I'd love your help defining what we're building.`; const t = setTimeout(() => { runtime.thread.composer.setText(opener); runtime.thread.composer.send(); }, 300); - return () => clearTimeout(t); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const handleReset = async () => { - if (!confirm("Start the discovery conversation over from scratch?")) return; - await fetch(`/api/projects/${projectId}/atlas-chat`, { method: "DELETE" }); - // Reload to get a fresh runtime state - window.location.reload(); - }; - return ( -
- {/* Minimal header bar */} -
- -
- - {/* Thread */} -
- -
+ // No card — fills the layout space directly +
+
); } -// --------------------------------------------------------------------------- -// Main export — wraps with runtime provider -// --------------------------------------------------------------------------- - export function AtlasChat({ projectId, projectName }: AtlasChatProps) { + const { data: session } = useSession(); + const userInitial = + session?.user?.name?.[0]?.toUpperCase() ?? + session?.user?.email?.[0]?.toUpperCase() ?? + "Y"; + const adapter = makeAtlasAdapter(projectId); const runtime = useLocalRuntime(adapter); @@ -126,6 +88,7 @@ export function AtlasChat({ projectId, projectName }: AtlasChatProps) { diff --git a/components/assistant-ui/thread.tsx b/components/assistant-ui/thread.tsx index 6b918e6..24a649b 100644 --- a/components/assistant-ui/thread.tsx +++ b/components/assistant-ui/thread.tsx @@ -8,7 +8,6 @@ import { ThreadPrimitive, } from "@assistant-ui/react"; import { - ArrowUpIcon, CheckIcon, ChevronLeftIcon, ChevronRightIcon, @@ -20,69 +19,140 @@ import type { FC } from "react"; import { MarkdownText } from "./markdown-text"; // --------------------------------------------------------------------------- -// Thread root — Grok-style layout -// When empty: logo + composer centered. When not empty: viewport + composer bottom. +// Thread root — Stackless style: flat on beige, no card // --------------------------------------------------------------------------- -export const Thread: FC = () => ( - - - {/* Empty state: centered welcome + composer */} +export const Thread: FC<{ userInitial?: string }> = ({ userInitial = "Y" }) => ( + + {/* Empty state */} -
-
-
- A -
-

Atlas

-

+

+
+ A +
+
+

Atlas

+

Your product strategist. Let's define what you're building.

-
- +
+
- {/* Messages viewport */} - + {/* Messages */} + - , + AssistantMessage, + }} /> - + {/* Input bar */} +
+ +
); // --------------------------------------------------------------------------- -// Composer — Grok pill style with inverted send button +// Composer — Stackless white pill input bar // --------------------------------------------------------------------------- -const Composer: FC = () => ( - -
+const Composer: FC<{ userInitial?: string }> = () => ( + +
- - @@ -91,61 +161,31 @@ const Composer: FC = () => ( ); // --------------------------------------------------------------------------- -// Followup suggestions -// --------------------------------------------------------------------------- - -const FollowupSuggestion: FC<{ suggestion: { prompt: string } }> = ({ suggestion }) => ( - - - -); - -// --------------------------------------------------------------------------- -// User message — subtle right-aligned bubble -// --------------------------------------------------------------------------- - -const UserMessage: FC = () => ( - -
- - -
-
-); - -const UserText: FC<{ text: string }> = ({ text }) => ( -
- {text} -
-); - -const UserActionBar: FC = () => ( - - - - - -); - -// --------------------------------------------------------------------------- -// Assistant message — plain text, no bubble, small avatar +// Assistant message — black avatar, "Atlas" label, plain text // --------------------------------------------------------------------------- const AssistantMessage: FC = () => ( - -
+ + {/* Avatar */} +
A
-
-
+
+ {/* Sender label */} +
+ Atlas +
+ {/* Message content */} +
@@ -154,37 +194,97 @@ const AssistantMessage: FC = () => ( ); -const AssistantText: FC = () => ( - -); +const AssistantText: FC = () => ; const AssistantActionBar: FC = () => ( - - ); +// --------------------------------------------------------------------------- +// User message — warm avatar, "You" label, same layout as Atlas +// --------------------------------------------------------------------------- + +const UserMessage: FC<{ userInitial?: string }> = ({ userInitial = "Y" }) => ( + + {/* Avatar */} +
+ {userInitial} +
+
+ {/* Sender label */} +
+ You +
+ {/* Message content */} +
+ +
+ +
+
+); + +const UserText: FC<{ text: string }> = ({ text }) => {text}; + +const UserActionBar: FC = () => ( + + + + + +); + // --------------------------------------------------------------------------- // Branch picker // --------------------------------------------------------------------------- @@ -192,19 +292,20 @@ const AssistantActionBar: FC = () => ( const BranchPicker: FC = () => ( - - + / -