"use client";
import * as React from "react";
import { RotateCcw } from "lucide-react";
import { AgentToolTimelineChatInput } from "@/components/ui/agent-tool-timeline-chat-input";
import type { AgentToolCall, AgentToolStatus } from "@/components/ui/agent-tool-timeline-chat-input";
const TASK = "Review the sample release plan";
const RESULT = "Checks passed after one retry. The plan is ready for review.";
const SEARCH: AgentToolCall = { id: "search", tool: "search", title: "Find release guidance", status: "queued", detail: "3 sample references matched the release checklist." };
const READ: AgentToolCall = { id: "read", tool: "read", title: "Read launch-notes.md", status: "running", detail: "version: 0.8 ยท owner: not set ยท checks: 6" };
const CODE: AgentToolCall = { id: "validate", tool: "code", title: "Validate configuration", status: "running" };
const RETRY: AgentToolCall = { id: "retry", tool: "code", title: "Validate with a fallback", status: "running", retryOf: "validate", detail: "owner: sample-team\n6 / 6 checks passed" };
const completed = (): AgentToolCall[] => [
{ ...SEARCH, status: "done", durationMs: 700 },
{ ...READ, status: "done", durationMs: 900 },
{ ...CODE, status: "error", durationMs: 700, detail: "Missing field: release.owner" },
{ ...RETRY, status: "done", durationMs: 800 },
];
type DemoState = { status: AgentToolStatus; prompt: string; value: string; calls: AgentToolCall[]; answer: string };
const blank = (): DemoState => ({ status: "idle", prompt: "", value: "", calls: [], answer: "" });
export default function AgentToolTimelineChatInputDemo({ autoplay = true, loop = true }: { autoplay?: boolean; loop?: boolean }) {
const [state, setState] = React.useState(blank);
const [motion, setMotion] = React.useState({ ready: false, reduced: true });
const [session, setSession] = React.useState({ id: 0, manual: false, prompt: TASK });
const [paused, setPaused] = React.useState(false);
const [activityOpen, setActivityOpen] = React.useState(false);
const interacted = React.useRef(false);
React.useEffect(() => {
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
const update = () => setMotion({ ready: true, reduced: media.matches });
update(); media.addEventListener("change", update);
return () => media.removeEventListener("change", update);
}, []);
React.useEffect(() => {
if (!motion.ready || (!session.manual && (!autoplay || motion.reduced || paused))) return;
const response = session.prompt === TASK ? RESULT : "Sample tool sequence completed after a retry.\nConnect your agent to provide a response to this request.";
if (motion.reduced) { setState({ status: "done", prompt: session.prompt, value: "", calls: completed(), answer: response }); return; }
const timers: number[] = [];
let stream = 0;
const later = (time: number, action: () => void) => timers.push(window.setTimeout(action, time));
const offset = session.manual ? 0 : 900;
setActivityOpen(false);
if (!session.manual) {
setState(blank());
for (let count = 1; count <= TASK.length; count++) later(200 + count / TASK.length * 600, () => setState((old) => ({ ...old, value: TASK.slice(0, count) })));
}
later(offset, () => setState({ status: "running", prompt: session.prompt, value: "", calls: [], answer: "" }));
const show = (calls: AgentToolCall[]) => setState((old) => ({ ...old, calls }));
later(offset + 120, () => show([{ ...SEARCH }]));
later(offset + 250, () => show([{ ...SEARCH, status: "running" }]));
later(offset + 800, () => show([{ ...SEARCH, status: "done", durationMs: 700 }, { ...READ }]));
later(offset + 1500, () => show([{ ...SEARCH, status: "done", durationMs: 700 }, { ...READ, status: "done", durationMs: 900 }, { ...CODE }]));
later(offset + 2050, () => show(completed().slice(0, 3)));
later(offset + 2350, () => show([...completed().slice(0, 3), { ...RETRY }]));
later(offset + 3000, () => show(completed()));
later(offset + 3300, () => {
setState((old) => ({ ...old, status: "answering" }));
let cursor = 0;
stream = window.setInterval(() => {
cursor += 3;
setState((old) => ({ ...old, answer: response.slice(0, cursor) }));
if (cursor >= response.length) window.clearInterval(stream);
}, 32);
});
later(offset + 4300, () => { window.clearInterval(stream); setState((old) => ({ ...old, status: "done", answer: response })); });
if (session.prompt === TASK) later(offset + 4600, () => { if (!interacted.current) setActivityOpen(true); });
if (loop && !session.manual) later(11000, () => { if (!interacted.current) setSession((old) => ({ ...old, id: old.id + 1 })); });
return () => { timers.forEach((timer) => window.clearTimeout(timer)); window.clearInterval(stream); };
}, [autoplay, loop, motion.ready, motion.reduced, paused, session]);
function send(prompt: string) {
setPaused(false); setSession((old) => ({ id: old.id + 1, manual: true, prompt }));
}
return (
<main className="flex min-h-screen w-full items-center justify-center bg-background p-5 font-sans text-foreground">
<div className="w-full max-w-[480px]">
<header className="mb-3 flex items-end justify-between gap-3">
<div><p className="mb-1 text-[9px] font-medium uppercase tracking-[0.18em] text-muted-foreground">Tool call timeline</p><h1 className="text-xl font-medium leading-7 tracking-tight">Watch the work take shape.</h1></div>
<span className="mb-1 rounded border border-border px-2 py-0.5 text-[9px] text-muted-foreground">Sample agent</span>
</header>
<div data-demo-stage className="h-[484px]">
<AgentToolTimelineChatInput status={state.status} prompt={state.prompt} calls={state.calls} answer={state.answer} value={state.value} activityOpen={activityOpen} onActivityOpenChange={(open) => { interacted.current = true; setActivityOpen(open); }} onValueChange={(value) => {
interacted.current = true;
if (!session.manual && state.status === "idle") setPaused(true);
setState((old) => ({ ...old, value }));
}} onSubmit={send} onStop={() => { setPaused(true); setSession((old) => ({ ...old, manual: false })); setState((old) => ({ ...blank(), value: old.prompt })); }} />
</div>
<footer className="mt-3 flex items-center justify-between text-[9px] text-muted-foreground"><span>Demo data ยท No tools are actually run</span><button type="button" data-replay onClick={() => send(TASK)} className="inline-flex items-center gap-1.5 rounded px-1 py-1 outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"><RotateCcw className="size-3" aria-hidden="true" />Replay</button></footer>
</div>
</main>
);
}