Files
wehub-resource-sync e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

180 lines
4.8 KiB
TypeScript

"use client";
import { ToolErrorCard, ToolStatusCard, ToolTraceCard } from "@/lib/tool-trace";
import type { ToolCallMessagePartProps } from "@assistant-ui/react";
import {
BookOpenIcon,
ExternalLinkIcon,
FileCodeIcon,
FileTextIcon,
FolderTreeIcon,
InfoIcon,
LayoutTemplateIcon,
TerminalIcon,
type LucideIcon,
} from "lucide-react";
import { type ReactNode } from "react";
function getToolIcon(toolName: string): ReactNode {
const Icon = getToolIconComponent(toolName);
return <Icon className="size-3.5" />;
}
function getToolIconComponent(toolName: string): LucideIcon {
switch (toolName) {
case "listDocs":
return FolderTreeIcon;
case "readDoc":
return FileTextIcon;
case "bash":
case "inspectSourceMap":
return TerminalIcon;
case "readFile":
case "readSourceMapFile":
return FileCodeIcon;
case "getTemplateList":
return LayoutTemplateIcon;
case "getTemplateDetails":
return InfoIcon;
case "openTemplatePreview":
return ExternalLinkIcon;
default:
return BookOpenIcon;
}
}
function getRunningMessage(toolName: string): string {
switch (toolName) {
case "listDocs":
return "Listing docs...";
case "readDoc":
return "Reading page...";
case "bash":
case "inspectSourceMap":
return "Running command...";
case "readFile":
case "readSourceMapFile":
return "Reading file...";
case "getTemplateList":
return "Loading templates...";
case "getTemplateDetails":
return "Reading template...";
case "openTemplatePreview":
return "Opening preview...";
default:
return "Running...";
}
}
function extractToolError(result: unknown): string | null {
if (!result || typeof result !== "object") return null;
const record = result as Record<string, unknown>;
if (record.success === false) {
if (typeof record.error === "string") return record.error;
return "Tool failed";
}
if (typeof record.error === "string") return record.error;
return null;
}
function summarizeXuluxResult(toolName: string, result: unknown): string {
if (result === undefined || result === null) return "Done";
const error = extractToolError(result);
if (error) return error;
if (typeof result !== "object") return String(result);
const record = result as Record<string, unknown>;
switch (toolName) {
case "listDocs": {
const children = record.children;
if (Array.isArray(children)) {
return `${children.length} item${children.length === 1 ? "" : "s"}`;
}
return "Listed";
}
case "readDoc":
return typeof record.title === "string" ? record.title : "Read page";
case "bash":
case "inspectSourceMap": {
const stdout = record.stdout;
if (typeof stdout === "string" && stdout.trim()) {
const line = stdout.trim().split("\n")[0] ?? "";
return line.length > 48 ? `${line.slice(0, 45)}...` : line;
}
return "Completed";
}
case "readFile":
case "readSourceMapFile": {
const path = typeof record.path === "string" ? record.path : "";
return path ? path.split("/").slice(-2).join("/") : "Read file";
}
case "getTemplateList": {
const templates = record.templates;
if (Array.isArray(templates)) {
return `${templates.length} template${templates.length === 1 ? "" : "s"}`;
}
return "Templates loaded";
}
case "getTemplateDetails": {
const name =
typeof record.name === "string"
? record.name
: typeof record.id === "string"
? record.id
: "Template details";
return name;
}
case "openTemplatePreview": {
if (typeof record.title === "string") return record.title;
const templateId =
typeof record.templateId === "string" ? record.templateId : "";
const versionId =
typeof record.versionId === "string" ? record.versionId : "";
if (templateId && versionId) return `${templateId} · ${versionId}`;
return templateId || "Preview opened";
}
default:
return "Completed";
}
}
export function XuluxToolCall({
toolName,
args,
result,
status,
}: ToolCallMessagePartProps): ReactNode {
const signature = toolName;
const icon = getToolIcon(toolName);
const isRunning = status?.type === "running";
const error = !isRunning ? extractToolError(result) : null;
if (isRunning) {
return (
<ToolStatusCard
icon={icon}
signature={signature}
message={getRunningMessage(toolName)}
loading
/>
);
}
if (error) {
return <ToolErrorCard signature={signature} error={error} args={args} />;
}
return (
<ToolTraceCard
icon={icon}
signature={signature}
description={summarizeXuluxResult(toolName, result)}
args={args}
result={result}
/>
);
}