48 lines
3.3 KiB
TypeScript
48 lines
3.3 KiB
TypeScript
"use client";
|
||
import { CircleAlert, Check, ChevronDown, Loader2, RotateCcw, Square } from "lucide-react";
|
||
import type { AgentRunView } from "@/lib/ai/canvas-agent-runtime/protocol";
|
||
import { canvasAgentProgressLabel, type CanvasAgentProgress } from "@/lib/ai/canvas-agent-runtime/progress";
|
||
|
||
export function CanvasAgentProgressMessage(props: {
|
||
progress: CanvasAgentProgress; run?: AgentRunView | null; busy?: boolean;
|
||
stopping?: boolean; sending?: boolean; onCancel?: () => void; onResume?: () => void;
|
||
}) {
|
||
const { progress, run } = props;
|
||
const running = progress.status === "running";
|
||
const label = progress.status === "completed" ? "处理完成"
|
||
: props.stopping && running ? "正在停止后续步骤,已完成的结果会保留"
|
||
: canvasAgentProgressLabel(progress.message || "正在处理你的任务");
|
||
return (
|
||
<div className="my-2 w-full min-w-0 text-xs text-zinc-500 dark:text-zinc-400" data-testid="canvas-agent-progress" data-agent-message-id={run?.assistantMessageId}>
|
||
<div className="flex items-start gap-2">
|
||
<details className="min-w-0 flex-1" open={running}>
|
||
<summary className="flex cursor-pointer list-none items-center gap-2 py-1 [&::-webkit-details-marker]:hidden">
|
||
{running ? <Loader2 className="h-3.5 w-3.5 shrink-0 animate-spin" /> : progress.status === "completed" ? <Check className="h-3.5 w-3.5 shrink-0" /> : <CircleAlert className="h-3.5 w-3.5 shrink-0" />}
|
||
<span className="min-w-0 flex-1" role="status" aria-live="polite">{label}</span>
|
||
<ChevronDown className="h-3 w-3 shrink-0" />
|
||
</summary>
|
||
{progress.activities?.length ? (
|
||
<ol className="ml-1.5 mt-1 space-y-1.5 border-l border-zinc-200 pl-4 dark:border-zinc-800" aria-label="任务执行步骤">
|
||
{progress.activities.map(activity => (
|
||
<li key={activity.id} className="flex items-center gap-2">
|
||
{activity.status === "started" ? <Loader2 className="h-3 w-3 shrink-0 animate-spin" /> : <span className="h-1 w-1 shrink-0 rounded-full bg-current" />}
|
||
<span>{canvasAgentProgressLabel(activity.title)}</span>
|
||
<span className="text-[11px] opacity-75">{activity.status === "succeeded" ? "已完成" : activity.status === "failed" ? "待处理" : activity.status === "queued" ? "待执行" : "进行中"}</span>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
) : null}
|
||
{run?.inputs.slice(-2).map(input => (
|
||
<p key={input.id} className="mt-2 whitespace-pre-wrap break-words">{input.applied ? "已应用补充要求" : "已收到补充要求"}:{input.text}</p>
|
||
))}
|
||
</details>
|
||
{running && props.onCancel ? (
|
||
<button type="button" onClick={props.onCancel} disabled={props.sending || props.stopping} className="rounded-md p-1.5 hover:bg-zinc-100 disabled:opacity-40 dark:hover:bg-white/10" title="停止后续步骤" aria-label="停止后续步骤"><Square className="h-3.5 w-3.5" /></button>
|
||
) : run?.resumable && props.onResume ? (
|
||
<button type="button" onClick={props.onResume} disabled={props.busy} className="inline-flex shrink-0 items-center gap-1 rounded-md px-2 py-1 hover:bg-zinc-100 disabled:opacity-40 dark:hover:bg-white/10"><RotateCcw className="h-3.5 w-3.5" />继续任务</button>
|
||
) : null}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|