"use client" import { memo, useMemo, useState } from "react" import { useTranslations } from "next-intl" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import type { LiveMessage } from "@/contexts/acp-connections-context" import type { PlanEntryInfo } from "@/lib/types" import { cn } from "@/lib/utils" import { CheckCircle2Icon, ChevronDownIcon, ChevronUpIcon, CircleDashedIcon, ListTodoIcon, Loader2Icon, } from "lucide-react" interface AgentPlanOverlayProps { message?: LiveMessage | null entries?: PlanEntryInfo[] | null planKey?: string | null visible?: boolean defaultExpanded?: boolean } function getLatestPlanEntries(message: LiveMessage | null): PlanEntryInfo[] { if (!message) return [] for (let i = message.content.length - 1; i >= 0; i -= 1) { const block = message.content[i] if (block.type === "plan") { return block.entries } } return [] } function getStatusKey( status: string ): | "status.completed" | "status.inProgress" | "status.pending" | "status.unknown" { switch (status) { case "completed": return "status.completed" case "in_progress": return "status.inProgress" case "pending": return "status.pending" default: return "status.unknown" } } type PriorityKey = | "priority.high" | "priority.medium" | "priority.low" | "priority.unknown" function getPriorityKey(priority: string): PriorityKey { switch (priority) { case "high": return "priority.high" case "medium": return "priority.medium" case "low": return "priority.low" default: return "priority.unknown" } } function getPriorityClassName(priority: string): string { switch (priority) { case "high": return "text-red-700 bg-red-500/10 border-red-500/20 dark:text-red-300" case "medium": return "text-amber-700 bg-amber-500/10 border-amber-500/20 dark:text-amber-300" case "low": return "text-slate-700 bg-slate-500/10 border-slate-500/20 dark:text-slate-300" default: return "text-muted-foreground" } } function StatusIcon({ status }: { status: string }) { if (status === "completed") { return } if (status === "in_progress") { return } return } export const AgentPlanOverlay = memo(function AgentPlanOverlay({ message, entries, planKey, visible = true, defaultExpanded = true, }: AgentPlanOverlayProps) { const t = useTranslations("Folder.chat.agentPlanOverlay") const liveEntries = useMemo( () => getLatestPlanEntries(message ?? null), [message] ) const resolvedEntries = useMemo( () => (liveEntries.length > 0 ? liveEntries : (entries ?? [])), [liveEntries, entries] ) const hasPlan = visible && resolvedEntries.length > 0 const fallbackPlanKey = useMemo(() => { if (resolvedEntries.length === 0) return null return resolvedEntries .map((entry) => `${entry.status}:${entry.priority}:${entry.content}`) .join("|") }, [resolvedEntries]) const currentPlanKey = planKey ?? message?.id ?? fallbackPlanKey const completedCount = useMemo( () => resolvedEntries.filter((entry) => entry.status === "completed").length, [resolvedEntries] ) const hasIncompleteEntries = completedCount < resolvedEntries.length const resolvedDefaultExpanded = defaultExpanded && hasIncompleteEntries const currentPlanStateKey = currentPlanKey ?? "__plan__default__" const [collapsedByPlanKey, setCollapsedByPlanKey] = useState< Record >({}) const isExpanded = !( collapsedByPlanKey[currentPlanStateKey] ?? !resolvedDefaultExpanded ) if (!hasPlan) { return null } if (!isExpanded) { return (
) } return (
{t("title")} {completedCount}/{resolvedEntries.length}
{resolvedEntries.map((entry, index) => (

{entry.content}

{t(getStatusKey(entry.status))} {t(getPriorityKey(entry.priority))}
))}
) })