@mcp-b/react-components 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/PromptInput-D6-G24p7.d.ts +528 -0
  2. package/dist/ThinkChatToolParts-DnUiZgCC.d.ts +83 -0
  3. package/dist/components/AgentChat.d.ts +11 -14
  4. package/dist/components/AgentChat.js +76 -57
  5. package/dist/components/AgentConnection.js +21 -18
  6. package/dist/components/Context.d.ts +6 -10
  7. package/dist/components/Context.js +4 -2
  8. package/dist/components/Conversation.d.ts +28 -28
  9. package/dist/components/Conversation.js +35 -54
  10. package/dist/components/Marker.d.ts +57 -0
  11. package/dist/components/Marker.js +89 -0
  12. package/dist/components/McpServerPicker.js +8 -10
  13. package/dist/components/MessageScroller.d.ts +52 -0
  14. package/dist/components/MessageScroller.js +62 -0
  15. package/dist/components/PromptInput.d.ts +2 -534
  16. package/dist/components/PromptInput.js +17 -11
  17. package/dist/components/ThinkChat.d.ts +10 -77
  18. package/dist/components/ThinkChat.js +59 -856
  19. package/dist/components/ThinkChatActivity.d.ts +73 -0
  20. package/dist/components/ThinkChatActivity.js +429 -0
  21. package/dist/components/ThinkChatContext.d.ts +28 -0
  22. package/dist/components/ThinkChatContext.js +10 -0
  23. package/dist/components/ThinkChatToolPanels.d.ts +39 -0
  24. package/dist/components/ThinkChatToolPanels.js +355 -0
  25. package/dist/components/ThinkChatToolParts.d.ts +2 -0
  26. package/dist/components/ThinkChatToolParts.js +66 -0
  27. package/dist/components/WorkspaceExplorer.d.ts +1 -1
  28. package/dist/components/useStickToBottom.d.ts +5 -2
  29. package/dist/components/useStickToBottom.js +5 -2
  30. package/dist/styles/agent-chat.css +15 -3
  31. package/dist/styles/agent-connection.css +12 -0
  32. package/dist/styles/code-block.css +4 -4
  33. package/dist/styles/conversation.css +10 -0
  34. package/dist/styles/index.css +2 -0
  35. package/dist/styles/marker.css +95 -0
  36. package/dist/styles/message-scroller.css +90 -0
  37. package/dist/styles/message.css +2 -1
  38. package/dist/styles/scrollbars.css +10 -2
  39. package/dist/styles/streamdown.css +2 -0
  40. package/dist/utils/mcp-server-catalog.d.ts +392 -1
  41. package/dist/utils/mcp-server-catalog.js +137 -1
  42. package/package.json +3 -2
@@ -0,0 +1,73 @@
1
+ import { AgentChatProps, AgentChatRenderPartContext } from "./AgentChat.js";
2
+ import { _ as isThinkChatActivityPart, s as ThinkChatPart } from "../ThinkChatToolParts-DnUiZgCC.js";
3
+ import * as React from "react";
4
+ import { DynamicToolUIPart, ToolUIPart, UIMessage } from "ai";
5
+
6
+ //#region src/components/ThinkChatActivity.d.ts
7
+ /**
8
+ * Bespoke rendering for one tool inside the activity log. Both hooks fall
9
+ * through to the built-in families when they return `undefined`, so an
10
+ * override can adjust just the label or just the panel.
11
+ */
12
+ interface ThinkChatToolRenderer {
13
+ /** One-line step label. `null` hides the step from the log. */
14
+ describe?: (part: ToolUIPart | DynamicToolUIPart) => string | null | undefined;
15
+ /** Expanded detail under the step. `null` keeps the step a plain row. */
16
+ panel?: (part: ToolUIPart | DynamicToolUIPart) => React.ReactNode;
17
+ }
18
+ type ThinkChatToolRenderers = Record<string, ThinkChatToolRenderer>;
19
+ interface ThinkChatActivityRendererOptions {
20
+ /**
21
+ * Tool names to keep out of activity folding entirely — the run splits
22
+ * around them and they fall through to your own renderPart / AgentChat's
23
+ * generic standalone activity row. For tools that should stay in the log
24
+ * with custom UI, use `toolRenderers` instead.
25
+ */
26
+ excludeTools?: readonly string[];
27
+ /** Per-tool label/panel overrides inside the activity log, keyed by tool name. */
28
+ toolRenderers?: ThinkChatToolRenderers;
29
+ }
30
+ /**
31
+ * One-line natural-language label for an activity part ("Ran vp test",
32
+ * "Fetched developers.cloudflare.com/agents/"); `null` for boundaries that
33
+ * aren't steps (step-start). Exported so a custom `renderPart` can reuse the
34
+ * built-in labels for tools it doesn't override.
35
+ */
36
+ declare function describeThinkChatActivityPart(part: ThinkChatPart, toolRenderers?: ThinkChatToolRenderers): string | null;
37
+ interface ThinkChatActivityProps {
38
+ /** Render with the log expanded. */
39
+ defaultOpen?: boolean;
40
+ parts: readonly ThinkChatPart[];
41
+ /** Defaults to deriving from part states (reasoning streaming, tool awaiting output). */
42
+ isStreaming?: boolean;
43
+ /** Per-tool label/panel overrides, keyed by tool name. */
44
+ toolRenderers?: ThinkChatToolRenderers;
45
+ }
46
+ /**
47
+ * One run of agent work as a collapsed Activity block: while streaming the
48
+ * label shimmers with the live step; settled, it becomes a short summary
49
+ * ("Thought, read 2 files and ran a command") over an expandable step log
50
+ * with per-tool panels. Normally reached through `ThinkChat.Messages`;
51
+ * exported for apps that lay out messages themselves.
52
+ */
53
+ declare function ThinkChatActivity({
54
+ parts,
55
+ defaultOpen,
56
+ isStreaming,
57
+ toolRenderers
58
+ }: ThinkChatActivityProps): React.JSX.Element | null;
59
+ /**
60
+ * Builds a renderPart handler that folds every run of consecutive activity
61
+ * parts (reasoning + tool calls) into one collapsed Activity box. Everything
62
+ * is derived from the UIMessage stream: part order, tool states, reasoning
63
+ * state. Excluded tools split the run and return undefined so your own
64
+ * renderPart (or AgentChat's generic activity row) takes over for them.
65
+ */
66
+ declare function createThinkChatActivityRenderer({
67
+ excludeTools,
68
+ toolRenderers
69
+ }?: ThinkChatActivityRendererOptions): NonNullable<AgentChatProps["renderPart"]>;
70
+ /** Default activity renderer — `createThinkChatActivityRenderer()` with no overrides. */
71
+ declare const renderThinkChatActivityPart: (context: AgentChatRenderPartContext<UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>>) => React.ReactNode | undefined;
72
+ //#endregion
73
+ export { ThinkChatActivity, ThinkChatActivityProps, ThinkChatActivityRendererOptions, ThinkChatToolRenderer, ThinkChatToolRenderers, createThinkChatActivityRenderer, describeThinkChatActivityPart, isThinkChatActivityPart, renderThinkChatActivityPart };
@@ -0,0 +1,429 @@
1
+ import { M as TerminalIcon, N as WarningIcon, P as WrenchIcon, S as MonitorIcon, T as PencilIcon, k as SearchIcon, m as FileIcon, v as InfoIcon, y as LinkIcon } from "../icons-BCNawx-T.js";
2
+ import { Activity, ActivityContent, ActivityItem, ActivityItemGroup, ActivityText, ActivityTrigger } from "./Activity.js";
3
+ import { ThinkChatContext } from "./ThinkChatContext.js";
4
+ import { displayUrl, isBrowserToolPart, isExecuteToolPart, isFetchToolPart, isPartFailed, isPartWorking, isSkillToolPart, isThinkChatActivityPart, isWorkspaceToolPart } from "./ThinkChatToolParts.js";
5
+ import { ThinkChatBrowserPanel, ThinkChatExecutePanel, ThinkChatFetchPanel, ThinkChatSkillPanel, ThinkChatWorkspacePanel } from "./ThinkChatToolPanels.js";
6
+ import * as React from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { getToolName, isReasoningUIPart, isToolUIPart } from "ai";
9
+ //#region src/components/ThinkChatActivity.tsx
10
+ function summarizeToolInput(input) {
11
+ if (!input || typeof input !== "object") return null;
12
+ for (const value of Object.values(input)) if (typeof value === "string" && value.trim()) {
13
+ const line = value.trim().split("\n", 1)[0] ?? "";
14
+ return line.length > 64 ? `${line.slice(0, 64)}…` : line;
15
+ }
16
+ return null;
17
+ }
18
+ const HTTP_URL_PATTERN = /https?:\/\/[^\s"'<>)}\]]+/i;
19
+ function firstHttpUrl(value) {
20
+ const text = typeof value === "string" ? value : JSON.stringify(value) ?? "";
21
+ const raw = HTTP_URL_PATTERN.exec(text)?.[0]?.replace(/[.,;:!?]+$/, "");
22
+ if (!raw) return null;
23
+ try {
24
+ return new URL(raw).href;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ function ActivityFavicon({ url }) {
30
+ const src = faviconHref(url);
31
+ const [failed, setFailed] = React.useState(false);
32
+ React.useEffect(() => setFailed(false), [src]);
33
+ if (!src || failed) return /* @__PURE__ */ jsx(LinkIcon, {});
34
+ return /* @__PURE__ */ jsx("img", {
35
+ className: "activity__favicon",
36
+ src,
37
+ alt: "",
38
+ loading: "lazy",
39
+ decoding: "async",
40
+ onError: () => setFailed(true)
41
+ });
42
+ }
43
+ function faviconHref(url) {
44
+ try {
45
+ return new URL("/favicon.ico", url).href;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ function ActivityKindIcon({ kind }) {
51
+ switch (kind) {
52
+ case "thought": return /* @__PURE__ */ jsx(InfoIcon, {});
53
+ case "read": return /* @__PURE__ */ jsx(FileIcon, {});
54
+ case "edit": return /* @__PURE__ */ jsx(PencilIcon, {});
55
+ case "search": return /* @__PURE__ */ jsx(SearchIcon, {});
56
+ case "web": return /* @__PURE__ */ jsx(LinkIcon, {});
57
+ case "command": return /* @__PURE__ */ jsx(TerminalIcon, {});
58
+ case "browser": return /* @__PURE__ */ jsx(MonitorIcon, {});
59
+ case "tool": return /* @__PURE__ */ jsx(WrenchIcon, {});
60
+ }
61
+ }
62
+ function genericToolLabels(name, hasUrl) {
63
+ const normalized = name.toLowerCase();
64
+ if (normalized.includes("grep") || normalized.includes("find") || normalized.includes("code")) return {
65
+ working: "Searching code",
66
+ done: "Searched code",
67
+ failed: "Code search failed",
68
+ summary: "searched code",
69
+ icon: "search"
70
+ };
71
+ if (normalized.includes("search") || normalized.includes("web") || normalized.includes("browser") || normalized.includes("google")) return {
72
+ working: "Searching the web",
73
+ done: "Searched the web",
74
+ failed: "Web search failed",
75
+ summary: "searched the web",
76
+ icon: "web"
77
+ };
78
+ if (hasUrl || normalized.includes("fetch") || normalized.includes("http") || normalized.includes("url")) return {
79
+ working: "Reading a URL",
80
+ done: "Read a URL",
81
+ failed: "URL read failed",
82
+ summary: "read a URL",
83
+ icon: "web"
84
+ };
85
+ if (normalized.includes("edit") || normalized.includes("write") || normalized.includes("delete") || normalized.includes("patch") || normalized.includes("apply") || normalized.includes("create") || normalized.includes("update")) return {
86
+ working: "Editing files",
87
+ done: "Edited files",
88
+ failed: "Edit failed",
89
+ summary: "edited files",
90
+ icon: "edit"
91
+ };
92
+ if (normalized.includes("exec") || normalized.includes("bash") || normalized.includes("shell") || normalized.includes("terminal") || normalized.includes("command")) return {
93
+ working: "Running a command",
94
+ done: "Ran a command",
95
+ failed: "Command failed",
96
+ summary: "ran a command",
97
+ icon: "command"
98
+ };
99
+ if (normalized.includes("read") || normalized.includes("open") || normalized.includes("list") || normalized.includes("file")) return {
100
+ working: "Reading files",
101
+ done: "Read files",
102
+ failed: "Read failed",
103
+ summary: "read files",
104
+ icon: "read"
105
+ };
106
+ return {
107
+ working: "Using a tool",
108
+ done: "Used a tool",
109
+ failed: "Tool failed",
110
+ summary: "used a tool",
111
+ icon: "tool"
112
+ };
113
+ }
114
+ function describeGenericToolPart(part) {
115
+ const labels = genericToolLabels(getToolName(part), activityPartUrl(part) !== null);
116
+ const label = isPartFailed(part) ? labels.failed : isPartWorking(part) ? labels.working : labels.done;
117
+ const detail = summarizeToolInput(part.input);
118
+ return detail ? `${label} · ${detail}` : label;
119
+ }
120
+ function activityPartUrl(part) {
121
+ if (isFetchToolPart(part)) return firstHttpUrl(part.input?.url ?? part.input?.path);
122
+ if (isBrowserToolPart(part) && part.type !== "tool-browser_execute") return firstHttpUrl(part.input?.url);
123
+ if (isToolUIPart(part) && !isExecuteToolPart(part)) return firstHttpUrl(part.input) ?? (part.state === "output-available" ? firstHttpUrl(part.output) : null);
124
+ return null;
125
+ }
126
+ function activityPartIconKind(part) {
127
+ if (isReasoningUIPart(part)) return "thought";
128
+ if (isWorkspaceToolPart(part)) switch (part.type) {
129
+ case "tool-read":
130
+ case "tool-list": return "read";
131
+ case "tool-write":
132
+ case "tool-edit":
133
+ case "tool-delete": return "edit";
134
+ case "tool-find":
135
+ case "tool-grep": return "search";
136
+ case "tool-bash": return "command";
137
+ }
138
+ if (isSkillToolPart(part)) return "tool";
139
+ if (isFetchToolPart(part)) return "web";
140
+ if (isBrowserToolPart(part)) return part.type === "tool-browser_execute" ? "browser" : "web";
141
+ if (isExecuteToolPart(part)) return "command";
142
+ if (isToolUIPart(part)) return genericToolLabels(getToolName(part), activityPartUrl(part) !== null).icon;
143
+ return "tool";
144
+ }
145
+ function activityPartIcon(part) {
146
+ const url = activityPartUrl(part);
147
+ return url ? /* @__PURE__ */ jsx(ActivityFavicon, { url }) : /* @__PURE__ */ jsx(ActivityKindIcon, { kind: activityPartIconKind(part) });
148
+ }
149
+ function activitySummaryIcon(parts) {
150
+ if (parts.find((part) => isToolUIPart(part) && isPartFailed(part))) return /* @__PURE__ */ jsx(WarningIcon, {});
151
+ const representative = parts.find((part) => activityPartIconKind(part) === "edit") ?? parts.find((part) => activityPartUrl(part)) ?? parts.find((part) => describeThinkChatActivityPart(part));
152
+ return representative ? activityPartIcon(representative) : /* @__PURE__ */ jsx(ActivityKindIcon, { kind: "tool" });
153
+ }
154
+ /**
155
+ * One-line natural-language label for an activity part ("Ran vp test",
156
+ * "Fetched developers.cloudflare.com/agents/"); `null` for boundaries that
157
+ * aren't steps (step-start). Exported so a custom `renderPart` can reuse the
158
+ * built-in labels for tools it doesn't override.
159
+ */
160
+ function describeThinkChatActivityPart(part, toolRenderers) {
161
+ if (isToolUIPart(part)) {
162
+ const custom = toolRenderers?.[getToolName(part)]?.describe?.(part);
163
+ if (custom !== void 0) return custom;
164
+ }
165
+ if (isReasoningUIPart(part)) {
166
+ const snippet = summarizeToolInput({ text: part.text });
167
+ const label = part.state === "streaming" ? "Thinking…" : "Thought";
168
+ return snippet ? `${label} · ${snippet}` : label;
169
+ }
170
+ if (isWorkspaceToolPart(part)) return describeWorkspaceToolPart(part);
171
+ if (isSkillToolPart(part)) return describeSkillToolPart(part);
172
+ if (isFetchToolPart(part)) return describeFetchToolPart(part);
173
+ if (isBrowserToolPart(part)) return describeBrowserToolPart(part);
174
+ if (isExecuteToolPart(part)) return describeExecuteToolPart(part);
175
+ if (isToolUIPart(part)) return describeGenericToolPart(part);
176
+ return null;
177
+ }
178
+ function basename(path) {
179
+ return path?.split("/").filter(Boolean).at(-1) ?? null;
180
+ }
181
+ function describeWorkspaceToolPart(part) {
182
+ const working = isPartWorking(part);
183
+ const failed = isPartFailed(part);
184
+ if (part.type === "tool-bash") {
185
+ const command = part.input?.script?.trim() ?? "";
186
+ if (failed) return command ? `Command failed · ${command}` : "Command failed";
187
+ const verb = working ? "Running" : "Ran";
188
+ return command ? `${verb} ${command}` : `${verb} a command`;
189
+ }
190
+ const label = (() => {
191
+ switch (part.type) {
192
+ case "tool-read": return `${working ? "Reading" : "Read"} ${basename(part.input?.path) ?? "a file"}`;
193
+ case "tool-write": return `${working ? "Writing" : "Wrote"} ${basename(part.input?.path) ?? "a file"}`;
194
+ case "tool-edit": return `${working ? "Editing" : "Edited"} ${basename(part.input?.path) ?? "a file"}`;
195
+ case "tool-delete": return `${working ? "Deleting" : "Deleted"} ${part.input?.path || "a file"}`;
196
+ case "tool-list": {
197
+ const path = part.input?.path;
198
+ return `${working ? "Listing" : "Listed"} ${path && path !== "/" ? path : "files"}`;
199
+ }
200
+ case "tool-find": return `${working ? "Searching" : "Searched"} for ${part.input?.pattern || "files"}`;
201
+ case "tool-grep": return `${working ? "Searching" : "Searched"} for ${part.input?.query || "text"}`;
202
+ }
203
+ })();
204
+ return failed ? `${label} · failed` : label;
205
+ }
206
+ function describeSkillToolPart(part) {
207
+ const working = isPartWorking(part);
208
+ const failed = isPartFailed(part);
209
+ const label = (() => {
210
+ switch (part.type) {
211
+ case "tool-activate_skill": {
212
+ const name = part.input?.name;
213
+ return `${working ? "Using" : "Used"} ${name ? `the ${name} skill` : "a skill"}`;
214
+ }
215
+ case "tool-read_skill_resource": return `${working ? "Reading" : "Read"} ${part.input?.path ?? "a skill resource"}`;
216
+ case "tool-run_skill_script": return `${working ? "Running" : "Ran"} ${part.input?.path ?? "a skill script"}`;
217
+ }
218
+ })();
219
+ return failed ? `${label} · failed` : label;
220
+ }
221
+ function describeBrowserToolPart(part) {
222
+ const working = isPartWorking(part);
223
+ const failed = isPartFailed(part);
224
+ if (part.type === "tool-browser_execute") {
225
+ const output = part.state === "output-available" ? part.output : void 0;
226
+ if (failed || output?.status === "error") return "Browser automation failed";
227
+ if (output?.status === "paused") return "Browser waiting for approval";
228
+ return working ? "Using the browser" : "Used the browser";
229
+ }
230
+ const page = displayUrl(part.input?.url) || "a page";
231
+ const label = (() => {
232
+ switch (part.type) {
233
+ case "tool-browser_extract": return `${working ? "Extracting" : "Extracted"} data from ${page}`;
234
+ case "tool-browser_links": return `${working ? "Listing" : "Listed"} links on ${page}`;
235
+ case "tool-browser_scrape": return `${working ? "Scraping" : "Scraped"} ${page}`;
236
+ default: return `${working ? "Browsing" : "Browsed"} ${page}`;
237
+ }
238
+ })();
239
+ return failed ? `${label} · failed` : label;
240
+ }
241
+ function describeExecuteToolPart(part) {
242
+ const output = part.state === "output-available" ? part.output : void 0;
243
+ if (isPartFailed(part) || output?.status === "error") return "Code failed";
244
+ if (output?.status === "paused") return "Code waiting for approval";
245
+ const label = isPartWorking(part) ? "Running code" : "Ran code";
246
+ const snippet = summarizeToolInput(part.input);
247
+ return snippet ? `${label} · ${snippet}` : label;
248
+ }
249
+ function describeFetchToolPart(part) {
250
+ const target = part.input?.url ?? part.input?.path ?? "";
251
+ const output = part.state === "output-available" ? part.output : void 0;
252
+ if (isPartFailed(part) || output?.ok === false) return target ? `Fetch failed · ${target}` : "Fetch failed";
253
+ const verb = isPartWorking(part) ? "Fetching" : "Fetched";
254
+ return target ? `${verb} ${target}` : `${verb} a URL`;
255
+ }
256
+ function summarizeActivitySteps(parts) {
257
+ const byTool = /* @__PURE__ */ new Map();
258
+ const genericSegments = /* @__PURE__ */ new Set();
259
+ const skillNames = /* @__PURE__ */ new Set();
260
+ let thoughts = 0;
261
+ let skillParts = 0;
262
+ let fetches = 0;
263
+ let browses = 0;
264
+ let codeRuns = 0;
265
+ for (const part of parts) if (isReasoningUIPart(part)) thoughts++;
266
+ else if (isWorkspaceToolPart(part)) {
267
+ const name = getToolName(part);
268
+ byTool.set(name, (byTool.get(name) ?? 0) + 1);
269
+ } else if (isSkillToolPart(part)) {
270
+ skillParts++;
271
+ if (typeof part.input?.name === "string") skillNames.add(part.input.name);
272
+ } else if (isFetchToolPart(part)) fetches++;
273
+ else if (isBrowserToolPart(part)) browses++;
274
+ else if (isExecuteToolPart(part)) codeRuns++;
275
+ else if (isToolUIPart(part)) genericSegments.add(genericToolLabels(getToolName(part), activityPartUrl(part) !== null).summary);
276
+ const count = (name) => byTool.get(name) ?? 0;
277
+ const files = (n) => n === 1 ? "a file" : `${n} files`;
278
+ const reads = count("read");
279
+ const edits = count("edit") + count("write");
280
+ const deletes = count("delete");
281
+ const commands = count("bash");
282
+ const segments = [];
283
+ if (thoughts > 0) segments.push("thought");
284
+ if (skillParts > 0) segments.push(skillNames.size === 1 ? `used the ${[...skillNames][0]} skill` : skillNames.size > 1 ? `used ${skillNames.size} skills` : "used a skill");
285
+ if (reads > 0) segments.push(`read ${files(reads)}`);
286
+ if (edits > 0) segments.push(`edited ${files(edits)}`);
287
+ if (deletes > 0) segments.push(`deleted ${files(deletes)}`);
288
+ if (count("find") + count("grep") > 0) segments.push("searched code");
289
+ if (count("list") > 0) segments.push("listed files");
290
+ if (fetches > 0) segments.push(fetches === 1 ? "fetched a URL" : `fetched ${fetches} URLs`);
291
+ if (browses > 0) segments.push("used the browser");
292
+ if (codeRuns > 0) segments.push("ran code");
293
+ if (commands > 0) segments.push(commands === 1 ? "ran a command" : `ran ${commands} commands`);
294
+ for (const segment of [
295
+ "edited files",
296
+ "searched code",
297
+ "searched the web",
298
+ "read a URL",
299
+ "ran a command",
300
+ "read files",
301
+ "used a tool"
302
+ ]) if (genericSegments.has(segment)) segments.push(segment);
303
+ if (segments.length === 0) return "Worked";
304
+ const top = segments.slice(0, 3);
305
+ const joined = top.length === 1 ? top[0] : `${top.slice(0, -1).join(", ")} and ${top.at(-1)}`;
306
+ return joined.charAt(0).toUpperCase() + joined.slice(1);
307
+ }
308
+ /**
309
+ * One run of agent work as a collapsed Activity block: while streaming the
310
+ * label shimmers with the live step; settled, it becomes a short summary
311
+ * ("Thought, read 2 files and ran a command") over an expandable step log
312
+ * with per-tool panels. Normally reached through `ThinkChat.Messages`;
313
+ * exported for apps that lay out messages themselves.
314
+ */
315
+ function ThinkChatActivity({ parts, defaultOpen, isStreaming, toolRenderers }) {
316
+ const steps = parts.flatMap((part, index) => {
317
+ const text = describeThinkChatActivityPart(part, toolRenderers);
318
+ if (!text) return [];
319
+ return [{
320
+ key: isToolUIPart(part) ? part.toolCallId : `${part.type}-${index}`,
321
+ text,
322
+ part
323
+ }];
324
+ });
325
+ if (steps.length === 0) return null;
326
+ const streaming = isStreaming ?? parts.some(isPartWorking);
327
+ const settledLabel = summarizeActivitySteps(parts);
328
+ const latest = steps.at(-1);
329
+ return /* @__PURE__ */ jsxs(Activity, {
330
+ isStreaming: streaming,
331
+ defaultOpen,
332
+ children: [/* @__PURE__ */ jsx(ActivityTrigger, {
333
+ icon: streaming && latest ? activityPartIcon(latest.part) : activitySummaryIcon(parts),
334
+ label: streaming && latest ? latest.text : settledLabel
335
+ }), /* @__PURE__ */ jsx(ActivityContent, { children: steps.map((step) => /* @__PURE__ */ jsx(ThinkChatActivityStep, {
336
+ text: step.text,
337
+ part: step.part,
338
+ toolRenderers
339
+ }, step.key)) })]
340
+ });
341
+ }
342
+ function ThinkChatActivityStep({ text, part, toolRenderers }) {
343
+ const icon = activityPartIcon(part);
344
+ if (isToolUIPart(part)) {
345
+ const customPanel = toolRenderers?.[getToolName(part)]?.panel?.(part);
346
+ if (customPanel === null) return /* @__PURE__ */ jsx(ActivityItem, {
347
+ icon,
348
+ children: text
349
+ });
350
+ if (customPanel !== void 0) return /* @__PURE__ */ jsx(ActivityItemGroup, {
351
+ label: text,
352
+ icon,
353
+ children: customPanel
354
+ });
355
+ }
356
+ if (isReasoningUIPart(part) && part.text.trim()) return /* @__PURE__ */ jsx(ActivityItemGroup, {
357
+ label: text,
358
+ icon,
359
+ children: /* @__PURE__ */ jsx(ActivityText, { children: part.text })
360
+ });
361
+ if (isWorkspaceToolPart(part) && part.type !== "tool-delete") return /* @__PURE__ */ jsx(ActivityItemGroup, {
362
+ label: text,
363
+ icon,
364
+ children: /* @__PURE__ */ jsx(ThinkChatWorkspacePanel, { part })
365
+ });
366
+ if (isSkillToolPart(part)) return /* @__PURE__ */ jsx(ActivityItemGroup, {
367
+ label: text,
368
+ icon,
369
+ children: /* @__PURE__ */ jsx(ThinkChatSkillPanel, { part })
370
+ });
371
+ if (isFetchToolPart(part)) return /* @__PURE__ */ jsx(ActivityItemGroup, {
372
+ label: text,
373
+ icon,
374
+ children: /* @__PURE__ */ jsx(ThinkChatFetchPanel, { part })
375
+ });
376
+ if (isBrowserToolPart(part)) return /* @__PURE__ */ jsx(ActivityItemGroup, {
377
+ label: text,
378
+ icon,
379
+ children: /* @__PURE__ */ jsx(ThinkChatBrowserPanel, { part })
380
+ });
381
+ if (isExecuteToolPart(part)) return /* @__PURE__ */ jsx(ActivityItemGroup, {
382
+ label: text,
383
+ icon,
384
+ children: /* @__PURE__ */ jsx(ThinkChatExecutePanel, { part })
385
+ });
386
+ return /* @__PURE__ */ jsx(ActivityItem, {
387
+ icon,
388
+ children: text
389
+ });
390
+ }
391
+ function ThinkChatActivityGroup({ message, parts, isTrailing, toolRenderers }) {
392
+ const chat = React.useContext(ThinkChatContext);
393
+ const chatStillWorking = isTrailing && Boolean(chat?.isStreaming) && chat?.messages.at(-1)?.id === message.id;
394
+ return /* @__PURE__ */ jsx(ThinkChatActivity, {
395
+ parts,
396
+ isStreaming: parts.some(isPartWorking) || chatStillWorking,
397
+ toolRenderers
398
+ });
399
+ }
400
+ /**
401
+ * Builds a renderPart handler that folds every run of consecutive activity
402
+ * parts (reasoning + tool calls) into one collapsed Activity box. Everything
403
+ * is derived from the UIMessage stream: part order, tool states, reasoning
404
+ * state. Excluded tools split the run and return undefined so your own
405
+ * renderPart (or AgentChat's generic activity row) takes over for them.
406
+ */
407
+ function createThinkChatActivityRenderer({ excludeTools, toolRenderers } = {}) {
408
+ const excluded = excludeTools?.length ? new Set(excludeTools) : void 0;
409
+ const isActivity = (part) => isThinkChatActivityPart(part, excluded);
410
+ return (context) => {
411
+ const { message, part, partIndex } = context;
412
+ if (!isActivity(part)) return void 0;
413
+ let start = partIndex;
414
+ while (start > 0 && isActivity(message.parts[start - 1])) start--;
415
+ if (partIndex !== start) return null;
416
+ let end = partIndex;
417
+ while (end + 1 < message.parts.length && isActivity(message.parts[end + 1])) end++;
418
+ return /* @__PURE__ */ jsx(ThinkChatActivityGroup, {
419
+ message,
420
+ parts: message.parts.slice(start, end + 1),
421
+ isTrailing: end === message.parts.length - 1,
422
+ toolRenderers
423
+ });
424
+ };
425
+ }
426
+ /** Default activity renderer — `createThinkChatActivityRenderer()` with no overrides. */
427
+ const renderThinkChatActivityPart = createThinkChatActivityRenderer();
428
+ //#endregion
429
+ export { ThinkChatActivity, createThinkChatActivityRenderer, describeThinkChatActivityPart, isThinkChatActivityPart, renderThinkChatActivityPart };
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ import { UIMessage } from "ai";
3
+ import { useAgentChat } from "@cloudflare/think/react";
4
+
5
+ //#region src/components/ThinkChatContext.d.ts
6
+ type ThinkChatValue = ReturnType<typeof useAgentChat<unknown, UIMessage>>;
7
+ declare const ThinkChatContext: React.Context<(Omit<import("@ai-sdk/react").UseChatHelpers<UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>>, "addToolOutput"> & {
8
+ clearHistory: () => void;
9
+ addToolOutput: (opts: {
10
+ toolCallId: string;
11
+ toolName?: string;
12
+ output?: unknown;
13
+ state?: "output-available" | "output-error";
14
+ errorText?: string;
15
+ }) => void;
16
+ isServerStreaming: boolean;
17
+ isStreaming: boolean;
18
+ isRecovering: boolean;
19
+ isToolContinuation: boolean;
20
+ connectionError: (Error & {
21
+ code: number;
22
+ reason: string;
23
+ wasClean: boolean;
24
+ }) | null;
25
+ }) | null>;
26
+ declare function useThinkChatContext(component: string): ThinkChatValue;
27
+ //#endregion
28
+ export { ThinkChatContext, ThinkChatValue, useThinkChatContext };
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ //#region src/components/ThinkChatContext.ts
3
+ const ThinkChatContext = React.createContext(null);
4
+ function useThinkChatContext(component) {
5
+ const context = React.useContext(ThinkChatContext);
6
+ if (!context) throw new Error(`${component} must be used inside <ThinkChat.Provider> or <ThinkChat.Root>.`);
7
+ return context;
8
+ }
9
+ //#endregion
10
+ export { ThinkChatContext, useThinkChatContext };
@@ -0,0 +1,39 @@
1
+ import { i as SkillToolPart, l as WorkspaceToolPart, n as ExecuteToolPart, r as FetchToolPart, t as BrowserToolPart } from "../ThinkChatToolParts-DnUiZgCC.js";
2
+ import * as React from "react";
3
+
4
+ //#region src/components/ThinkChatToolPanels.d.ts
5
+ /** Default panels for the Think workspace tools, Claude Code-style: read/write
6
+ show the file with line numbers, edit shows a mini diff, list/find/grep
7
+ show their result lists, bash gets the shell panel. Everything is typed by
8
+ the upstream tool contract. */
9
+ declare function ThinkChatWorkspacePanel({
10
+ part
11
+ }: {
12
+ part: WorkspaceToolPart;
13
+ }): React.JSX.Element | null;
14
+ /** Skill panels show what the model actually received: the activated skill's
15
+ body, the resource text, or the script result. */
16
+ declare function ThinkChatSkillPanel({
17
+ part
18
+ }: {
19
+ part: SkillToolPart;
20
+ }): React.JSX.Element;
21
+ declare function ThinkChatFetchPanel({
22
+ part
23
+ }: {
24
+ part: FetchToolPart;
25
+ }): React.JSX.Element;
26
+ /** The codemode execute tool as an activity panel: highlighted TypeScript
27
+ plus logs, result, and the paused pending-approval list. */
28
+ declare function ThinkChatExecutePanel({
29
+ part
30
+ }: {
31
+ part: ExecuteToolPart;
32
+ }): React.JSX.Element;
33
+ declare function ThinkChatBrowserPanel({
34
+ part
35
+ }: {
36
+ part: BrowserToolPart;
37
+ }): React.JSX.Element;
38
+ //#endregion
39
+ export { ThinkChatBrowserPanel, ThinkChatExecutePanel, ThinkChatFetchPanel, ThinkChatSkillPanel, ThinkChatWorkspacePanel };