Files
infinite-canvas/web/src/components/agent/agent-log-view.tsx
T

82 lines
3.5 KiB
TypeScript

import { useRef, useState } from "react";
import { Button, Segmented } from "antd";
import copyToClipboard from "copy-to-clipboard";
import { Copy, Trash2 } from "lucide-react";
import { canvasThemes } from "@/lib/canvas-theme";
import type { AgentEventLog } from "@/stores/use-agent-store";
import { formatLogJson, formatLogText, type AgentLogContext } from "./agent-event-formatters";
export function AgentLogView({
logs,
theme,
context,
onClear,
onCopied,
onCopyBlocked,
}: {
logs: AgentEventLog[];
theme: (typeof canvasThemes)[keyof typeof canvasThemes];
context: AgentLogContext;
onClear: () => void;
onCopied: (text: string) => void;
onCopyBlocked: (text: string) => void;
}) {
const [mode, setMode] = useState<"text" | "json">("text");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const content = mode === "text" ? formatLogText(logs, context) : formatLogJson(logs, context);
const lastError = [...logs].reverse().find((item) => /错误|失败|error/i.test(`${item.title}\n${item.text}`));
const copy = async (value = content, tip = "日志已复制") => {
if (await copyToClipboard(value)) {
onCopied(tip);
return;
}
textareaRef.current?.focus();
textareaRef.current?.select();
onCopyBlocked("已选中日志,请手动复制");
};
return (
<div className="thin-scrollbar min-h-0 flex-1 overflow-y-auto p-4">
<div className="flex min-h-full flex-col gap-3">
<div>
<div className="text-base font-semibold leading-6"></div>
</div>
<div className="flex flex-wrap items-center justify-between gap-2">
<Segmented
size="small"
value={mode}
onChange={(value) => setMode(value as "text" | "json")}
options={[
{ label: "排查日志", value: "text" },
{ label: "原始 JSON", value: "json" },
]}
/>
<div className="flex items-center gap-2">
<span className="text-xs" style={{ color: theme.node.muted }}>
{logs.length}
</span>
<Button size="small" icon={<Copy className="size-3.5" />} onClick={() => void copy()}>
</Button>
<Button size="small" disabled={!lastError} onClick={() => lastError && void copy(formatLogText([lastError], context), "最近错误已复制")}>
</Button>
<Button size="small" danger type="text" icon={<Trash2 className="size-3.5" />} disabled={!logs.length} onClick={onClear}>
</Button>
</div>
</div>
<textarea
ref={textareaRef}
readOnly
value={content}
className="thin-scrollbar min-h-[360px] flex-1 resize-none rounded-lg border bg-transparent p-3 font-mono text-xs leading-5 outline-none"
style={{ borderColor: theme.node.stroke, color: theme.node.text }}
onFocus={(event) => event.currentTarget.select()}
/>
</div>
</div>
);
}