({ call }: { call: ToolCallVizData })
| 79 | } |
| 80 | |
| 81 | export function ToolCallCard({ call }: { call: ToolCallVizData }) { |
| 82 | const t = useT(); |
| 83 | const [expanded, setExpanded] = useState(false); |
| 84 | const [rawView, setRawView] = useState(false); |
| 85 | const summary = summaryForArgs(call.args); |
| 86 | const adminLink = openInAdmin(call.args); |
| 87 | const result = call.result; |
| 88 | const pending = !result; |
| 89 | const ok = result?.ok ?? null; |
| 90 | |
| 91 | // read_page / read_section / read_block 的返回里带 markdown 正文(content 字段)—— |
| 92 | // 渲染成 markdown 效果,而不是把整坨 JSON 塞进 <pre>。结构化工具(search / outline / |
| 93 | // backlinks…)没有 content、继续看原始 JSON。可用 raw/rendered 开关切回 JSON 调试。 |
| 94 | const mdContent = |
| 95 | result?.ok && |
| 96 | result.data && |
| 97 | typeof result.data === "object" && |
| 98 | typeof (result.data as { content?: unknown }).content === "string" |
| 99 | ? (result.data as { content: string }).content |
| 100 | : null; |
| 101 | |
| 102 | const status = pending |
| 103 | ? { label: t("tool.pending"), color: "var(--amber)" } |
| 104 | : ok |
| 105 | ? { label: t("tool.ok"), color: "var(--emerald)" } |
| 106 | : { label: t("tool.fail"), color: "var(--vermilion)" }; |
| 107 | |
| 108 | const argEntries = Object.entries(call.args); |
| 109 | |
| 110 | return ( |
| 111 | <div |
| 112 | className={`relative my-3 max-w-[68ch] border bg-[var(--ink-2)]/60 ${ |
| 113 | pending |
| 114 | ? "border-[var(--amber)] shadow-[0_0_0_1px_rgba(255,179,71,0.15)]" |
| 115 | : ok |
| 116 | ? "border-[var(--line)]" |
| 117 | : "border-[var(--vermilion)]" |
| 118 | } ${call.synthetic ? "border-dashed" : ""}`} |
| 119 | > |
| 120 | <span className="k-corner-tl" /> |
| 121 | <span className="k-corner-br" /> |
| 122 | |
| 123 | {/* 折叠态 chip 行 */} |
| 124 | <button |
| 125 | onClick={() => setExpanded(!expanded)} |
| 126 | className="group flex w-full items-center gap-3 px-3 py-2 text-left hover:bg-[var(--ink-3)]/60" |
| 127 | > |
| 128 | {/* 状态印章 */} |
| 129 | <span |
| 130 | className="k-stamp-solid shrink-0" |
| 131 | style={{ |
| 132 | background: status.color, |
| 133 | color: "var(--ink)", |
| 134 | }} |
| 135 | > |
| 136 | {pending && ( |
| 137 | <span className="mr-0.5 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-[var(--ink)]" /> |
| 138 | )} |
nothing calls this directly
no test coverage detected