web: pretty tool cards — args rendered as the real command/path (priority keys, label-less single field), output label; 276 tests green
This commit is contained in:
+56
-6
@@ -162,6 +162,54 @@ function CopyButton({ text }: { text: string }): React.ReactNode {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ---------- pretty tool args ----------
|
||||
|
||||
// Keys shown first when present (most readable "what is this tool doing"
|
||||
// signal); the rest follow in their original order.
|
||||
const ARG_PRIORITY: string[] = [
|
||||
"command", "path", "file_path", "pattern", "url", "query", "content",
|
||||
"prompt", "task", "description",
|
||||
];
|
||||
|
||||
export interface ArgEntry {
|
||||
k: string | null;
|
||||
v: string;
|
||||
}
|
||||
|
||||
/** Parse a tool's args (JSON object, JSON string, or raw text) into labeled
|
||||
* display entries. A lone string entry renders without a label. */
|
||||
export function toolArgsEntries(argsText: string): ArgEntry[] {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(argsText);
|
||||
} catch {
|
||||
return [{ k: null, v: argsText }];
|
||||
}
|
||||
if (typeof parsed === "string")
|
||||
return [{ k: null, v: parsed }];
|
||||
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
||||
return [{ k: null, v: argsText }];
|
||||
const obj = parsed as Record<string, unknown>;
|
||||
const keys = Object.keys(obj);
|
||||
const ordered = [
|
||||
...ARG_PRIORITY.filter((k) => k in obj),
|
||||
...keys.filter((k) => !ARG_PRIORITY.includes(k)),
|
||||
];
|
||||
const entries: ArgEntry[] = [];
|
||||
for (const k of ordered) {
|
||||
const raw = obj[k];
|
||||
const v =
|
||||
typeof raw === "string"
|
||||
? raw
|
||||
: JSON.stringify(raw, null, 2) ?? String(raw);
|
||||
entries.push({ k, v });
|
||||
}
|
||||
if (entries.length === 1 && entries[0] !== undefined)
|
||||
return [{ k: null, v: entries[0].v }];
|
||||
return entries;
|
||||
}
|
||||
|
||||
function ToolCard({ tool }: { tool: ToolState }) {
|
||||
const status: string = tool.running
|
||||
? "running…"
|
||||
@@ -180,14 +228,16 @@ function ToolCard({ tool }: { tool: ToolState }) {
|
||||
</span>
|
||||
</summary>
|
||||
<div className="tool-body">
|
||||
<div>
|
||||
<strong>args</strong>
|
||||
<pre style={{ margin: "4px 0 10px", whiteSpace: "pre-wrap" }}>
|
||||
{tool.args}
|
||||
</pre>
|
||||
<div className="tool-args">
|
||||
{toolArgsEntries(tool.args).map((e, i) => (
|
||||
<div key={e.k ?? i} className="tool-arg">
|
||||
{e.k !== null && <span className="arg-k">{e.k}</span>}
|
||||
<pre className="arg-v">{e.v}</pre>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<strong>result</strong>
|
||||
<span className="arg-k">output</span>
|
||||
<pre style={{ margin: "4px 0 0", whiteSpace: "pre-wrap" }}>
|
||||
{tool.preview}
|
||||
</pre>
|
||||
|
||||
Reference in New Issue
Block a user