"use client"; import { useEffect, useState } from "react"; import { Check } from "lucide-react"; import { InlineSpinner } from "@/components/inline-spinner"; import { activityCompletedSteps, activityElapsedLabel } from "@/lib/chat-message-view"; export type AgentActivityState = "working" | "searching" | "solving" | "listening" | "composing" | "shaping"; const labels = { working: "正在处理任务…", searching: "正在搜索相关信息…", solving: "正在分析问题…", listening: "正在聆听…", composing: "正在组织回答…", shaping: "正在生成结果…", } as const satisfies Record; function ActivityElapsed({ startedAt }: Readonly<{ startedAt: number }>) { const [now, setNow] = useState(() => Date.now()); useEffect(() => { const timer = window.setInterval(() => setNow(Date.now()), 1000); return () => window.clearInterval(timer); }, []); const label = activityElapsedLabel(startedAt, now); if (!label) return null; return ; } function MessageThinkingTrace({ text, hasAnswer, }: Readonly<{ text: string; hasAnswer: boolean; }>) { const [userOpen, setUserOpen] = useState(null); const open = userOpen ?? !hasAnswer; if (!text.trim()) return null; return (
{ setUserOpen((event.currentTarget as HTMLDetailsElement).open); }} > 思考
{text}
); } function VargaTraceStep({ sentence }: Readonly<{ sentence: string }>) { return (
  • {sentence}
  • ); } /** * Fallback activity panel for an assistant row that carries no step timeline. * Both chat surfaces normally render `ConsultationRunTimeline`; this remains * for rows that only have a phase label, a completed trail or thinking text. * The live marker is the shared `InlineSpinner`, the same one the timeline uses. */ export function AgentActivityStatus({ state, label = labels[state], startedAt, completedTrail, thinkingText, vargaSentence, hasAnswer = false, showLive = true, }: Readonly<{ state: AgentActivityState; label?: string; startedAt?: number; completedTrail?: string; thinkingText?: string; vargaSentence?: string | null; hasAnswer?: boolean; showLive?: boolean; }>) { const completedSteps = activityCompletedSteps(completedTrail); const live = showLive && !hasAnswer; const varga = vargaSentence?.trim() ?? ""; const showVarga = Boolean(varga); if (!live && completedSteps.length === 0 && !thinkingText?.trim() && !showVarga) return null; return (
    {(live || completedSteps.length > 0 || showVarga) && (
      {completedSteps.map((step) => (
    1. {step}
    2. ))} {live ? (
    3. {label} {startedAt ? : null}
    4. ) : null} {showVarga ? : null}
    )} {thinkingText ? : null}
    ); }