"use client"; import { Handle, Position, type Node, type NodeProps } from "@xyflow/react"; import { useTranslations } from "next-intl"; import BaseNodeWrapper from "./base-node-wrapper"; type AgentOutputNodeData = { isSkeleton?: boolean; stepId?: string; stepIndex?: number; stepTotal?: number; title?: string; channel?: string; artifactType?: string; previewText?: string; sections?: Array<{ id?: string; label?: string; content?: string; }>; metadata?: Record; qualityChecks?: string[]; outputType?: string; body?: string; _status?: string; _statusMessage?: string; }; type AgentOutputNodeType = Node; function tryFormatJsonBody(body: string): string | null { const trimmed = body.trim(); if (!trimmed) { return null; } const looksLikeJsonObject = trimmed.startsWith("{") && trimmed.endsWith("}"); const looksLikeJsonArray = trimmed.startsWith("[") && trimmed.endsWith("]"); if (!looksLikeJsonObject && !looksLikeJsonArray) { return null; } try { const parsed = JSON.parse(trimmed) as unknown; return JSON.stringify(parsed, null, 2); } catch { return null; } } function normalizeSections(raw: AgentOutputNodeData["sections"]) { if (!Array.isArray(raw)) { return [] as Array<{ id: string; label: string; content: string }>; } const sections: Array<{ id: string; label: string; content: string }> = []; for (const item of raw) { const label = typeof item?.label === "string" ? item.label.trim() : ""; const content = typeof item?.content === "string" ? item.content.trim() : ""; if (label === "" || content === "") { continue; } const id = typeof item.id === "string" && item.id.trim() !== "" ? item.id.trim() : label; sections.push({ id, label, content }); } return sections; } function normalizeMetadata(raw: AgentOutputNodeData["metadata"]) { if (!raw || typeof raw !== "object" || Array.isArray(raw)) { return [] as Array<[string, string]>; } const entries: Array<[string, string]> = []; for (const [rawKey, rawValue] of Object.entries(raw)) { const key = rawKey.trim(); if (key === "") { continue; } if (typeof rawValue === "string") { const value = rawValue.trim(); if (value !== "") { entries.push([key, value]); } continue; } if (Array.isArray(rawValue)) { const values = rawValue .filter((value): value is string => typeof value === "string") .map((value) => value.trim()) .filter((value) => value !== ""); if (values.length > 0) { entries.push([key, values.join(", ")]); } } } return entries; } function normalizeQualityChecks(raw: AgentOutputNodeData["qualityChecks"]): string[] { if (!Array.isArray(raw)) { return []; } return raw .filter((value): value is string => typeof value === "string") .map((value) => value.trim()) .filter((value) => value !== ""); } export default function AgentOutputNode({ data, selected }: NodeProps) { const t = useTranslations("agentOutputNode"); const nodeData = data as AgentOutputNodeData; const isSkeleton = nodeData.isSkeleton === true; const hasStepCounter = typeof nodeData.stepIndex === "number" && Number.isFinite(nodeData.stepIndex) && typeof nodeData.stepTotal === "number" && Number.isFinite(nodeData.stepTotal) && nodeData.stepTotal > 0; const safeStepIndex = typeof nodeData.stepIndex === "number" && Number.isFinite(nodeData.stepIndex) ? Math.max(0, Math.floor(nodeData.stepIndex)) : 0; const safeStepTotal = typeof nodeData.stepTotal === "number" && Number.isFinite(nodeData.stepTotal) ? Math.max(1, Math.floor(nodeData.stepTotal)) : 1; const stepCounter = hasStepCounter ? `${safeStepIndex + 1}/${safeStepTotal}` : null; const resolvedTitle = nodeData.title ?? (isSkeleton ? t("plannedOutputDefaultTitle") : t("defaultTitle")); const body = nodeData.body ?? ""; const artifactType = nodeData.artifactType ?? nodeData.outputType ?? ""; const sections = normalizeSections(nodeData.sections); const metadataEntries = normalizeMetadata(nodeData.metadata); const qualityChecks = normalizeQualityChecks(nodeData.qualityChecks); const previewText = typeof nodeData.previewText === "string" && nodeData.previewText.trim() !== "" ? nodeData.previewText.trim() : sections[0]?.content ?? ""; const hasStructuredOutput = sections.length > 0 || metadataEntries.length > 0 || qualityChecks.length > 0 || previewText !== ""; const formattedJsonBody = isSkeleton ? null : tryFormatJsonBody(body); return (

{resolvedTitle}

{isSkeleton ? ( {t("skeletonBadge")} ) : null}
{isSkeleton ? (

{t("plannedOutputLabel")} {stepCounter ? ` - ${stepCounter}` : ""} {nodeData.stepId ? ` - ${nodeData.stepId}` : ""}

) : null}

{t("channelLabel")}

{nodeData.channel ?? t("emptyValue")}

{t("artifactTypeLabel")}

{artifactType || t("emptyValue")}

{isSkeleton ? (

{t("bodyLabel")}

{t("plannedContent")}

) : hasStructuredOutput ? ( <> {sections.length > 0 ? (

{t("sectionsLabel")}

{sections.map((section) => (

{section.label}

{section.content}

))}
) : null} {metadataEntries.length > 0 ? (

{t("metadataLabel")}

{metadataEntries.map(([key, value]) => (

{key}: {value}

))}
) : null} {qualityChecks.length > 0 ? (

{t("qualityChecksLabel")}

{qualityChecks.map((qualityCheck) => ( {qualityCheck} ))}
) : null}

{t("previewLabel")}

{previewText || t("previewFallback")}

) : formattedJsonBody ? (

{t("bodyLabel")}

              {formattedJsonBody}
            
) : (

{t("bodyLabel")}

{body}

)}
); }