import { Activity, BarChart3, Database, History, LineChart, MonitorCog } from "lucide-react";
import { AnimatePresence, MotionConfig, motion } from "motion/react";
import { memo, type ReactNode } from "react";
import { formatChartDisplayText, withChartDisplayTimes } from "@/shared/utils/date-time-text";
import { StatusIcon } from "@/shared/ui/status";
import { StandaloneInteractiveChart } from "../answer-ui/interactive-chart";
import { normalizeSafeChartData, parseChartSpec } from "../chart-data";
import type { AgentUiResult } from "../types";
import type { UIEnvelope } from "../ui-envelope";
import type { parseFrontendChart } from "../frontend-action/chart";
import {
agentLayoutTransition,
agentPanelItemVariants,
agentPanelListVariants
} from "./agent-motion";
type AgentUiEnvelopeRendererProps = {
results: AgentUiResult[];
};
export function AgentUiEnvelopeRenderer({ results }: AgentUiEnvelopeRendererProps) {
if (results.length === 0) {
return null;
}
return (
{results.map((result) => (
))}
);
}
function AgentUiResultCard({ result }: { result: AgentUiResult }) {
const { envelope } = result;
return (
{getEnvelopeIcon(envelope)}
{getEnvelopeTitle(envelope)}
{getSurfaceLabel(envelope.surface)}
agent-ui@1
{envelope.kind === "chart" ?
: null}
{envelope.kind === "registered_component" ?
: null}
{envelope.kind === "map_action" ? null : null}
);
}
export const AgentChartView = memo(function AgentChartView({ chart, embedded = false, expanded = false }: { chart: NonNullable>; embedded?: boolean; expanded?: boolean }) {
return ;
});
function SafeChartEnvelope({ envelope, embedded = false, expanded = false }: { envelope: Extract; embedded?: boolean; expanded?: boolean }) {
const parsed = parseChartSpec(envelope.spec);
const raw = normalizeSafeChartData(envelope.data, 100);
const displayed = withChartDisplayTimes({ title: parsed.title ?? "Agent 图表", x_axis_name: parsed.xAxisName,
x_data: raw?.xData ?? [], series: raw?.series ?? [] });
const spec = { ...parsed, title: displayed.title, xAxisName: displayed.x_axis_name };
const data = raw ? { ...raw, xData: displayed.x_data, series: displayed.series } : null;
if (!data || data.series.length === 0 || data.xData.length === 0) {
return ;
}
return ;
}
function RegisteredComponentEnvelope({
envelope
}: {
envelope: Extract;
}) {
if (envelope.component === "HistoryPanel") {
return (
} title="历史数据面板">
{envelope.data ? : null}
);
}
if (envelope.component === "ScadaPanel") {
return (
} title="SCADA 面板">
);
}
return ;
}
function TrustedPanelShell({
icon,
title,
children
}: {
icon: ReactNode;
title: string;
children: ReactNode;
}) {
return (
{icon}
{title}
{children}
);
}
function PropRows({ props, labels }: { props: unknown; labels: Record }) {
const rows = safePropRows(props, labels);
if (rows.length === 0) {
return 已打开可信面板,暂无可展示参数。
;
}
return (
{rows.map((row) => (
- {row.label}
-
{row.value}
))}
);
}
function FallbackText({ text }: { text: string }) {
return (
{text}
);
}
function safePropRows(props: unknown, labels: Record) {
if (!isRecord(props)) {
return [];
}
return Object.entries(labels).flatMap(([key, label]) => {
const value = props[key];
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
return [];
}
return [{ label, value: String(value) }];
});
}
function getEnvelopeIcon(envelope: UIEnvelope) {
if (envelope.kind === "chart") {
return envelope.spec && isRecord(envelope.spec) && envelope.spec.chart_type === "bar" ? (
) : (
);
}
if (envelope.kind === "registered_component") {
return ;
}
return ;
}
function getEnvelopeTitle(envelope: UIEnvelope) {
if (envelope.kind === "chart") {
const spec = parseChartSpec(envelope.spec);
return formatChartDisplayText(spec.title ?? "Agent 图表");
}
if (envelope.kind === "registered_component") {
return envelope.component;
}
return envelope.action;
}
function getSurfaceLabel(surface: UIEnvelope["surface"]) {
if (surface === "chat_inline") return "聊天内联展示";
if (surface === "side_panel") return "侧栏展示";
if (surface === "canvas") return "画布展示";
return "地图叠加";
}
function isRecord(value: unknown): value is Record {
return typeof value === "object" && value !== null;
}