import { LocateButton, FollowUpForm } from "./answer-action-components";
import { Renderer, defineRegistry } from "@json-render/react";
import { useContext, useRef, useState, type ReactNode } from "react";
import {
Info,
TriangleAlert,
ClipboardList,
Database,
FileCheck2,
FlaskConical,
Lightbulb,
MessageSquare
} from "lucide-react";
import { Button } from "@/shared/ui/button";
import { Badge } from "@/shared/ui/badge";
import { Alert, AlertDescription, AlertTitle } from "@/shared/ui/alert";
import { Card, CardContent } from "@/shared/ui/card";
import { AnswerDisclosure, AnswerDisclosureChevron, AnswerMotionScope } from "./answer-motion";
import { cn } from "@/shared/ui/cn";
import { InteractiveChart } from "./interactive-chart";
import { InteractiveDataTable } from "./interactive-data-table";
import { AnswerResult } from "./answer-result";
import { AnswerInteractionContext as InteractionContext, type AnswerUiInteractions } from "./answer-ui-interaction-context";
export type { AnswerUiMapTarget, AnswerUiSelection } from "./answer-ui-interaction-context";
import { answerUiCatalog } from "./catalog";
import type { AnswerUiBlock } from "./contract";
import { AnswerContentNav } from "./answer-content-nav";
import { getAnswerTaskLayout } from "./answer-task-layout";
import { AnswerViewStateProvider, useAnswerBlockState, useAnswerNodeState, withAnswerNodeIdentity, withAnswerNodeIds } from "./answer-view-state-provider";
import { type AnswerEvidenceState, type AnswerViewState, type AnswerViewStateChange } from "./answer-view-state";
import { withAnswerDisplayTimes } from "./answer-display-time";
const { registry: baseRegistry } = defineRegistry(answerUiCatalog, {
actions: {},
components: {
Stack: ({ props, children }) => (
{children}
),
Section: ({ props, children }) => (
{props.title}
{props.description ? (
{props.description}
) : null}
{children}
),
Text: ({ props }) => props.tone === "neutral" ? (
{props.title ?
{props.title}
: null}
{props.text}
) : (
{props.tone === "warning" ? : }
{props.title ?? (props.tone === "warning" ? "注意事项" : "补充说明")}
{props.text}
),
Result: ({ props }) => ,
Metric: ({ props }) => (
{props.label}
{props.value}
{props.unit ? (
{props.unit}
) : null}
{props.source ? (
来源:{props.source}
) : null}
),
Table: ({ props }) => (
),
Chart: ({ props }) => ,
Evidence: ({ props }) => ,
MapLocation: ({ props }) => ,
FollowUpForm: ({ props }) =>
}
});
const registry = withAnswerNodeIdentity(baseRegistry);
type AnswerUiViewProps = { block: AnswerUiBlock; viewState?: AnswerViewState; onViewStateChange?: AnswerViewStateChange } & Partial;
export function AnswerUiView({ viewState, onViewStateChange, ...props }: AnswerUiViewProps) {
return
;
}
function AnswerUiBody({
block,
disabled = false,
onLocate,
onSubmit,
chartWindowKey,
submitMode = "send"
}: { block: AnswerUiBlock } & Partial) {
const [{ evidenceFilter: filter }, setFilter] = useAnswerBlockState();
const contentRef = useRef(null);
const taskLayout = getAnswerTaskLayout(block.spec);
const root = block.spec.elements[block.spec.root];
const density = root.type === "Stack" ? root.props.density : undefined;
const intent = root.type === "Stack" ? root.props.intent : undefined;
const hasTaskLayout = intent !== "response" && Boolean(taskLayout?.analysis.length && (taskLayout.evidence.length || taskLayout.followUp.length));
const kinds = [...new Set(Object.values(block.spec.elements).filter((node) => node.type === "Evidence").map((node) => String(node.props.kind)))];
const evidenceFilters = kinds.length > 1 ?
{["all", ...kinds].map((kind) => )}
: null;
const isGroup = (ids: string[]) => hasTaskLayout && ids.length > 1 && !ids.every((id) => block.spec.elements[id].type === "Section");
const renderNodes = (ids: string[]) => ids.map((id) => );
return (
<>
{taskLayout ?
{taskLayout.overview.length ?
{renderNodes(taskLayout.overview)}
: null}
{taskLayout.analysis.length ?
{renderNodes(taskLayout.analysis)}
: null}
{taskLayout.evidence.length || taskLayout.followUp.length ?
{taskLayout.evidence.length ? }>
{evidenceFilters}{renderNodes(taskLayout.evidence)}
: null}
{taskLayout.followUp.length ? }>
{renderNodes(taskLayout.followUp)}
: null}
: null}
: <>{evidenceFilters}
>}
>
);
}
function TaskSupport({ title, icon, children, grouped }: { title: string; icon: ReactNode; children: ReactNode; grouped: boolean }) {
const [open, setOpen] = useState(true);
return
{icon}{title}
}>{children};
}
const evidenceKinds = {
observed: { label: "查询记录", icon: Database },
human_input: { label: "用户提供", icon: MessageSquare },
inference: { label: "分析推断", icon: Lightbulb },
demo: { label: "演练数据", icon: FlaskConical }
};
function EvidenceCard(props: {
title: string;
summary: string;
source: string;
kind: keyof typeof evidenceKinds;
observed_at: string | null;
evidence_id: string | null;
}) {
const [{ open }, patch] = useAnswerNodeState("Evidence", props);
const setOpen = (open: boolean) => patch({ open });
const meta = evidenceKinds[props.kind];
const Icon = meta.icon;
const { evidenceFilter } = useContext(InteractionContext);
if (evidenceFilter && evidenceFilter !== "all" && evidenceFilter !== props.kind) return null;
return (
{props.title}
{props.source}
{meta.label}
}>
{props.summary}
{props.observed_at || props.evidence_id ? (
{props.observed_at ? (
- 记录时间:
- {props.observed_at}
) : null}
{props.evidence_id ? (
- 资料编号:
- {props.evidence_id}
) : null}
) : null}
);
}