| 1 | // Adapted from DeepSeek Harness c291e7961a, ui-tool/components/ToolRow.tsx (MIT). |
| 2 | // Reasonix provides authorized content and inspection through slots, without Cordis. |
| 3 | import { useState, type ReactNode } from "react"; |
| 4 | import { ScanSearch } from "lucide-react"; |
| 5 | import { DisclosureRow } from "./DisclosureRow"; |
| 6 | import { StateDot, type StateDotState } from "./StateDot"; |
| 7 | import css from "./ToolRow.styles"; |
| 8 | |
| 9 | export function ToolRow({ icon, title, summary, state, dot, statusLabel, errorSummary, children, inspectLabel, inspect, beforeToggle }: { |
| 10 | dot?: StateDotState; |
| 11 | icon: ReactNode; title: string; summary: string; state: "running" | "done" | "error" | "stopped" | "unknown"; |
| 12 | statusLabel: string; errorSummary?: string; children: ReactNode; |
| 13 | inspectLabel: string; inspect: (trigger: HTMLElement) => void; beforeToggle: () => void; |
| 14 | }) { |
| 15 | const [open, setOpen] = useState(false); |
| 16 | const failureLine = state === "error" ? errorSummary : undefined; |
| 17 | const leading = dot ? <StateDot state={dot} /> : state === "error" || state === "stopped" |
| 18 | ? <StateDot state={state === "error" ? "error" : "warning"} /> : icon; |
| 19 | return <div className={`${css.root} chat-tool`} data-state={state}> |
| 20 | {state !== "done" && <span className="sr-only">{statusLabel}</span>} |
| 21 | <DisclosureRow icon={leading} title={title} open={open} expandable expandOnRowClick keepContentWhenOpen |
| 22 | rowClassName={css.row} leadingClassName={css.leading} titleClassName={css.title} chevronClassName={css.chevron} |
| 23 | onToggle={() => { beforeToggle(); setOpen(value => !value); }} |
| 24 | collapsedContent={(failureLine || summary) && <><span className={css.sep} aria-hidden /> |
| 25 | <span className={`${css.summary}${failureLine ? ` ${css.errorSummary}` : ""}`}>{failureLine || summary}</span></>}> |
| 26 | <div className={css.bodyWrap}> |
| 27 | {children} |
| 28 | <button className={css.inspectButton} type="button" onClick={event => inspect(event.currentTarget)}><ScanSearch size={12} />{inspectLabel}</button> |
| 29 | </div> |
| 30 | </DisclosureRow> |
| 31 | </div>; |
| 32 | } |
| 33 |