import { ChatApp } from "@/components/ui/chat-app";
import { useRef, useState } from "react";
import {
Bot,
Clock3,
MessageSquarePlus,
PanelLeft,
Search,
Send,
User,
} from "lucide-react";
import {
AnimatedSidebar,
AnimatedSidebarContent,
AnimatedSidebarGroup,
AnimatedSidebarGroupContent,
AnimatedSidebarInset,
AnimatedSidebarMenu,
AnimatedSidebarMenuButton,
AnimatedSidebarMenuItem,
AnimatedSidebarRail,
AnimatedSidebarTrigger,
} from "@/components/ui/chat-app-utils/animated-sidebar";
import { cn } from "@/lib/utils";
interface ThreadMessage {
id: string;
from: "user" | "assistant";
content: string;
}
const initialMessages: ThreadMessage[] = [
{
id: "m1",
from: "user",
content: "Audit the checkout flow and prepare a release-ready patch.",
},
{
id: "m2",
from: "assistant",
content:
"I traced the validation gap in checkout/submit.ts and prepared a focused patch. Running the checks now.",
},
];
export default function ChatAppDemo() {
const [messages, setMessages] = useState<ThreadMessage[]>(initialMessages);
const [input, setInput] = useState("");
const [pending, setPending] = useState(false);
const runId = useRef(0);
const submit = () => {
const value = input.trim();
if (!value || pending) return;
const id = runId.current++;
setMessages((current) => [
...current,
{ id: `user-${id}`, from: "user", content: value },
]);
setInput("");
setPending(true);
window.setTimeout(() => {
setMessages((current) => [
...current,
{
id: `assistant-${id}`,
from: "assistant",
content:
"Got it — I'll keep this change focused and run the same checks before shipping.",
},
]);
setPending(false);
}, 900);
};
return (
<ChatApp sidebarWidth="16rem" className="h-[640px]">
<AnimatedSidebar
ariaLabel="Agent workspace"
collapsible="offcanvas"
panelClassName="h-full bg-background"
>
<AnimatedSidebarContent className="gap-1 px-2 py-4">
<AnimatedSidebarGroup>
<AnimatedSidebarGroupContent>
<AnimatedSidebarMenu className="gap-1">
{[
{ label: "New task", icon: MessageSquarePlus },
{ label: "Search", icon: Search },
{ label: "Runs", icon: Clock3 },
].map(({ label, icon: Icon }) => (
<AnimatedSidebarMenuItem key={label}>
<AnimatedSidebarMenuButton
icon={<Icon className="size-4" />}
className="font-normal"
>
{label}
</AnimatedSidebarMenuButton>
</AnimatedSidebarMenuItem>
))}
</AnimatedSidebarMenu>
</AnimatedSidebarGroupContent>
</AnimatedSidebarGroup>
</AnimatedSidebarContent>
<AnimatedSidebarRail />
</AnimatedSidebar>
<AnimatedSidebarInset className="bg-background">
<header className="flex h-14 shrink-0 items-center gap-2.5 border-border border-b px-4">
<AnimatedSidebarTrigger className="text-muted-foreground hover:bg-muted hover:text-foreground">
<PanelLeft className="size-4" />
</AnimatedSidebarTrigger>
<div className="min-w-0">
<p className="truncate text-sm font-medium text-foreground">
Checkout release
</p>
<p className="truncate text-[11px] text-muted-foreground">
Agent workspace
</p>
</div>
</header>
<div className="flex-1 overflow-y-auto px-4 py-5">
<div className="mx-auto flex max-w-2xl flex-col gap-4">
{messages.map((message) => (
<div
key={message.id}
className={cn(
"flex items-start gap-2.5",
message.from === "user" && "flex-row-reverse",
)}
>
<span className="grid size-7 shrink-0 place-items-center rounded-full bg-muted text-muted-foreground">
{message.from === "assistant" ? (
<Bot className="size-4" />
) : (
<User className="size-4" />
)}
</span>
<p
className={cn(
"max-w-[75%] rounded-2xl px-3.5 py-2 text-sm",
message.from === "user"
? "bg-foreground text-background"
: "bg-muted text-foreground",
)}
>
{message.content}
</p>
</div>
))}
{pending ? (
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="grid size-7 shrink-0 place-items-center rounded-full bg-muted">
<Bot className="size-4" />
</span>
Thinking…
</div>
) : null}
</div>
</div>
<div className="shrink-0 border-border border-t bg-background p-3">
<form
className="mx-auto flex max-w-2xl items-end gap-2"
onSubmit={(event) => {
event.preventDefault();
submit();
}}
>
<textarea
value={input}
onChange={(event) => setInput(event.target.value)}
placeholder="Ask the agent to continue…"
rows={1}
className="flex-1 resize-none rounded-xl border border-border bg-background px-3 py-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
/>
<button
type="submit"
disabled={pending || !input.trim()}
className="grid size-9 shrink-0 place-items-center rounded-full bg-foreground text-background disabled:opacity-40"
>
<Send className="size-4" />
</button>
</form>
</div>
</AnimatedSidebarInset>
</ChatApp>
);
}