forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcustom-catchall-renderer.tsx
More file actions
154 lines (141 loc) · 4.17 KB
/
Copy pathcustom-catchall-renderer.tsx
File metadata and controls
154 lines (141 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"use client";
import React from "react";
// Branded catch-all renderer for tools that don't have a dedicated
// per-tool renderer. Registered via `useDefaultRenderTool` in page.tsx,
// this component handles every tool call NOT claimed by a named
// `useRenderTool` registration (e.g. get_stock_price, roll_dice).
//
// Shows the tool name, a status badge, pretty-printed arguments, and
// the result (as JSON). Each cell is self-contained, so this file is
// intentionally duplicated from the `tool-rendering-custom-catchall`
// cell rather than imported across cell boundaries.
export type CatchallToolStatus = "inProgress" | "executing" | "complete";
export interface CustomCatchallRendererProps {
name: string;
status: CatchallToolStatus;
parameters: unknown;
result: string | undefined;
}
export function CustomCatchallRenderer({
name,
status,
parameters,
result,
}: CustomCatchallRendererProps) {
const parsedResult = parseResult(result);
const done = status === "complete";
return (
<div
data-testid="custom-catchall-card"
data-tool-name={name}
data-status={status}
className="my-3 overflow-hidden rounded-2xl border border-[#DBDBE5] bg-white shadow-sm"
>
<div className="flex items-center justify-between border-b border-[#E9E9EF] bg-[#FAFAFC] px-4 py-2.5">
<div className="flex items-center gap-2">
<span className="text-[10px] uppercase tracking-[0.14em] text-[#838389]">
Tool
</span>
<span
data-testid="custom-catchall-tool-name"
className="font-mono text-sm text-[#010507]"
>
{name}
</span>
</div>
<StatusBadge status={status} />
</div>
<div className="grid gap-3 p-4 text-sm">
<Section label="Arguments">
<pre
data-testid="custom-catchall-args"
className="overflow-x-auto rounded-lg border border-[#E9E9EF] bg-[#FAFAFC] p-2.5 font-mono text-xs text-[#010507]"
>
{safeStringify(parameters)}
</pre>
</Section>
<Section label="Result">
{done ? (
<pre
data-testid="custom-catchall-result"
className="overflow-x-auto rounded-lg border border-[#85ECCE4D] bg-[#85ECCE]/10 p-2.5 font-mono text-xs text-[#010507]"
>
{parsedResult !== undefined
? safeStringify(parsedResult)
: "(empty)"}
</pre>
) : (
<p className="text-xs italic text-[#838389]">
waiting for tool to finish…
</p>
)}
</Section>
</div>
</div>
);
}
function Section({
label,
children,
}: {
label: string;
children: React.ReactNode;
}) {
return (
<div>
<div className="mb-1.5 text-[10px] font-medium uppercase tracking-[0.14em] text-[#838389]">
{label}
</div>
{children}
</div>
);
}
function StatusBadge({ status }: { status: CatchallToolStatus }) {
const { label, tone } = describeStatus(status);
return (
<span
data-testid="custom-catchall-status"
className={`rounded-full px-2.5 py-0.5 text-[10px] font-medium uppercase tracking-[0.14em] ${tone}`}
>
{label}
</span>
);
}
function describeStatus(status: CatchallToolStatus): {
label: string;
tone: string;
} {
switch (status) {
case "inProgress":
return {
label: "streaming",
tone: "border border-[#FFAC4D33] bg-[#FFAC4D]/15 text-[#57575B]",
};
case "executing":
return {
label: "running",
tone: "border border-[#BEC2FF] bg-[#BEC2FF1A] text-[#010507]",
};
case "complete":
return {
label: "done",
tone: "border border-[#85ECCE4D] bg-[#85ECCE]/20 text-[#189370]",
};
}
}
function parseResult(result: string | undefined): unknown {
if (result === undefined || result === null) return undefined;
if (typeof result !== "string") return result;
try {
return JSON.parse(result);
} catch {
return result;
}
}
function safeStringify(value: unknown): string {
try {
return JSON.stringify(value, null, 2);
} catch {
return String(value);
}
}