@mcp-b/react-components 0.6.5 → 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.
- package/dist/PromptInput-D6-G24p7.d.ts +528 -0
- package/dist/ThinkChatToolParts-DnUiZgCC.d.ts +83 -0
- package/dist/components/AgentChat.d.ts +11 -14
- package/dist/components/AgentChat.js +76 -57
- package/dist/components/Context.d.ts +6 -10
- package/dist/components/Context.js +4 -2
- package/dist/components/Conversation.d.ts +28 -28
- package/dist/components/Conversation.js +35 -54
- package/dist/components/Marker.d.ts +57 -0
- package/dist/components/Marker.js +89 -0
- package/dist/components/MessageScroller.d.ts +52 -0
- package/dist/components/MessageScroller.js +62 -0
- package/dist/components/PromptInput.d.ts +2 -534
- package/dist/components/PromptInput.js +17 -11
- package/dist/components/ThinkChat.d.ts +10 -77
- package/dist/components/ThinkChat.js +59 -856
- package/dist/components/ThinkChatActivity.d.ts +73 -0
- package/dist/components/ThinkChatActivity.js +429 -0
- package/dist/components/ThinkChatContext.d.ts +28 -0
- package/dist/components/ThinkChatContext.js +10 -0
- package/dist/components/ThinkChatToolPanels.d.ts +39 -0
- package/dist/components/ThinkChatToolPanels.js +355 -0
- package/dist/components/ThinkChatToolParts.d.ts +2 -0
- package/dist/components/ThinkChatToolParts.js +66 -0
- package/dist/components/WorkspaceExplorer.d.ts +1 -1
- package/dist/components/useStickToBottom.d.ts +5 -2
- package/dist/components/useStickToBottom.js +5 -2
- package/dist/styles/agent-chat.css +15 -3
- package/dist/styles/agent-connection.css +1 -0
- package/dist/styles/code-block.css +4 -4
- package/dist/styles/conversation.css +10 -0
- package/dist/styles/index.css +2 -0
- package/dist/styles/marker.css +95 -0
- package/dist/styles/message-scroller.css +90 -0
- package/dist/styles/message.css +2 -1
- package/dist/styles/scrollbars.css +10 -2
- package/dist/styles/streamdown.css +2 -0
- package/package.json +3 -2
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { ActivityOutput, ActivityPanel } from "./Activity.js";
|
|
2
|
+
import { Tab, TabPanel, Tabs, TabsList } from "./Tabs.js";
|
|
3
|
+
import { t as CodeBlock } from "../CodeBlock-BK0sOAbV.js";
|
|
4
|
+
import { BrowserLiveView } from "./BrowserLiveView.js";
|
|
5
|
+
import { displayUrl } from "./ThinkChatToolParts.js";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { getToolName } from "ai";
|
|
9
|
+
//#region src/components/ThinkChatToolPanels.tsx
|
|
10
|
+
function renderDeniedPanel(label, reason) {
|
|
11
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
12
|
+
label,
|
|
13
|
+
text: reason,
|
|
14
|
+
status: "denied",
|
|
15
|
+
error: true
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function numberLines(content, from = 1) {
|
|
19
|
+
const lines = content.split("\n");
|
|
20
|
+
const width = String(from + lines.length - 1).length;
|
|
21
|
+
return lines.map((line, i) => `${String(from + i).padStart(width)} ${line}`).join("\n");
|
|
22
|
+
}
|
|
23
|
+
function prefixLines(text, prefix) {
|
|
24
|
+
return text.split("\n").map((line) => `${prefix}${line}`).join("\n");
|
|
25
|
+
}
|
|
26
|
+
/** Default panels for the Think workspace tools, Claude Code-style: read/write
|
|
27
|
+
show the file with line numbers, edit shows a mini diff, list/find/grep
|
|
28
|
+
show their result lists, bash gets the shell panel. Everything is typed by
|
|
29
|
+
the upstream tool contract. */
|
|
30
|
+
function ThinkChatWorkspacePanel({ part }) {
|
|
31
|
+
if (part.type === "tool-bash") return /* @__PURE__ */ jsx(ThinkChatShellPanel, { part });
|
|
32
|
+
if (part.state === "output-error") return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
33
|
+
label: getToolName(part),
|
|
34
|
+
text: part.errorText,
|
|
35
|
+
status: "error",
|
|
36
|
+
error: true
|
|
37
|
+
});
|
|
38
|
+
if (part.state === "output-denied") return renderDeniedPanel(getToolName(part), part.approval.reason);
|
|
39
|
+
switch (part.type) {
|
|
40
|
+
case "tool-read": {
|
|
41
|
+
const label = part.input?.path ?? "file";
|
|
42
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
43
|
+
if (output && "error" in output) return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
44
|
+
label,
|
|
45
|
+
text: output.error,
|
|
46
|
+
status: "error",
|
|
47
|
+
error: true
|
|
48
|
+
});
|
|
49
|
+
if (output && "content" in output) return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
50
|
+
label,
|
|
51
|
+
text: output.content,
|
|
52
|
+
status: `${output.totalLines} lines`
|
|
53
|
+
});
|
|
54
|
+
if (output) return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
55
|
+
label,
|
|
56
|
+
text: `${output.name} · ${output.mediaType} · ${output.sizeBytes} bytes`
|
|
57
|
+
});
|
|
58
|
+
return /* @__PURE__ */ jsx(ActivityPanel, { label });
|
|
59
|
+
}
|
|
60
|
+
case "tool-write": {
|
|
61
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
62
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
63
|
+
label: part.input?.path ?? "file",
|
|
64
|
+
text: part.input?.content ? numberLines(part.input.content) : void 0,
|
|
65
|
+
status: output ? `${output.lines} lines written` : void 0
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
case "tool-edit": {
|
|
69
|
+
const input = part.input;
|
|
70
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
71
|
+
const status = !output ? void 0 : output.error ? output.error : output.created ? "created" : output.fuzzyMatch ? "replaced (fuzzy match)" : "replaced";
|
|
72
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
73
|
+
label: input?.path ?? "file",
|
|
74
|
+
status,
|
|
75
|
+
error: Boolean(output?.error),
|
|
76
|
+
children: input?.old_string !== void 0 && input.new_string !== void 0 ? /* @__PURE__ */ jsxs(ActivityOutput, { children: [
|
|
77
|
+
/* @__PURE__ */ jsx("span", {
|
|
78
|
+
className: "activity__diff-del",
|
|
79
|
+
children: prefixLines(input.old_string, "- ")
|
|
80
|
+
}),
|
|
81
|
+
"\n",
|
|
82
|
+
/* @__PURE__ */ jsx("span", {
|
|
83
|
+
className: "activity__diff-add",
|
|
84
|
+
children: prefixLines(input.new_string, "+ ")
|
|
85
|
+
})
|
|
86
|
+
] }) : null
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
case "tool-list": {
|
|
90
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
91
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
92
|
+
label: part.input?.path ?? "/",
|
|
93
|
+
text: output?.entries.join("\n"),
|
|
94
|
+
status: output ? `${output.count} entries` : void 0
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
case "tool-find": {
|
|
98
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
99
|
+
const files = Array.isArray(output?.files) ? output.files.map(String) : [];
|
|
100
|
+
const count = typeof output?.count === "number" ? output.count : files.length;
|
|
101
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
102
|
+
label: part.input?.pattern ?? "files",
|
|
103
|
+
text: files.join("\n"),
|
|
104
|
+
status: output ? `${count} files${output.truncated ? " (truncated)" : ""}` : void 0
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
case "tool-grep": {
|
|
108
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
109
|
+
const matches = Array.isArray(output?.matches) ? output.matches.map((m) => typeof m === "string" ? m : m.context ?? JSON.stringify(m)) : [];
|
|
110
|
+
const status = output && typeof output.totalMatches === "number" ? `${output.totalMatches} matches in ${String(output.filesWithMatches)} files` : void 0;
|
|
111
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
112
|
+
label: part.input?.query ?? "search",
|
|
113
|
+
text: matches.join("\n"),
|
|
114
|
+
status
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
default: return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Skill panels show what the model actually received: the activated skill's
|
|
121
|
+
body, the resource text, or the script result. */
|
|
122
|
+
function ThinkChatSkillPanel({ part }) {
|
|
123
|
+
if (part.state === "output-error") return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
124
|
+
label: getToolName(part),
|
|
125
|
+
text: part.errorText,
|
|
126
|
+
status: "error",
|
|
127
|
+
error: true
|
|
128
|
+
});
|
|
129
|
+
if (part.state === "output-denied") return renderDeniedPanel(getToolName(part), part.approval.reason);
|
|
130
|
+
switch (part.type) {
|
|
131
|
+
case "tool-activate_skill": {
|
|
132
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
133
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
134
|
+
label: part.input?.name ?? "skill",
|
|
135
|
+
text: output
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
case "tool-read_skill_resource": {
|
|
139
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
140
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
141
|
+
label: part.input?.path ?? "resource",
|
|
142
|
+
text: output
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
case "tool-run_skill_script": {
|
|
146
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
147
|
+
const text = output === void 0 ? void 0 : typeof output === "string" ? output : JSON.stringify(output, null, 2);
|
|
148
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
149
|
+
label: part.input?.path ?? "script",
|
|
150
|
+
text
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function ThinkChatFetchPanel({ part }) {
|
|
156
|
+
const label = part.input?.url ?? part.input?.path ?? "fetch";
|
|
157
|
+
if (part.state === "output-error") return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
158
|
+
label,
|
|
159
|
+
text: part.errorText,
|
|
160
|
+
status: "error",
|
|
161
|
+
error: true
|
|
162
|
+
});
|
|
163
|
+
if (part.state === "output-denied") return renderDeniedPanel(label, part.approval.reason);
|
|
164
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
165
|
+
if (!output) return /* @__PURE__ */ jsx(ActivityPanel, { label });
|
|
166
|
+
if (!output.ok) return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
167
|
+
label: output.finalUrl ?? label,
|
|
168
|
+
text: output.message,
|
|
169
|
+
status: output.code,
|
|
170
|
+
error: true
|
|
171
|
+
});
|
|
172
|
+
const body = output.body ?? (output.json !== void 0 ? JSON.stringify(output.json, null, 2) : output.path ? `saved to ${output.path}` : "");
|
|
173
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
174
|
+
label: output.finalUrl,
|
|
175
|
+
text: body || void 0,
|
|
176
|
+
status: `${output.status} · ${output.bytes} bytes${output.truncated ? " (truncated)" : ""}`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function findBrowserResultMedia(value, depth = 0) {
|
|
180
|
+
const media = {};
|
|
181
|
+
const visit = (node, level) => {
|
|
182
|
+
if (level > 4 || node === null) return;
|
|
183
|
+
if (typeof node === "string") {
|
|
184
|
+
if (!media.liveViewUrl && node.includes("live.browser.run")) media.liveViewUrl = node;
|
|
185
|
+
else if (!media.screenshot && node.startsWith("data:image/")) media.screenshot = node;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (Array.isArray(node)) {
|
|
189
|
+
for (const item of node) visit(item, level + 1);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (typeof node === "object") {
|
|
193
|
+
const record = node;
|
|
194
|
+
if (media.expiresInMs === void 0 && typeof record.url === "string" && typeof record.expiresInMs === "number") media.expiresInMs = record.expiresInMs;
|
|
195
|
+
for (const item of Object.values(record)) visit(item, level + 1);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
visit(value, depth);
|
|
199
|
+
return media;
|
|
200
|
+
}
|
|
201
|
+
function ExecuteCodeView({ code, output, hideResult }) {
|
|
202
|
+
const sections = [
|
|
203
|
+
output && "logs" in output && output.logs?.length ? output.logs.join("\n") : "",
|
|
204
|
+
output?.status === "error" ? output.error : "",
|
|
205
|
+
output?.status === "paused" ? formatPendingActions(output.pending) : "",
|
|
206
|
+
output?.status === "completed" && output.result !== void 0 && !hideResult ? typeof output.result === "string" ? output.result : JSON.stringify(output.result, null, 2) : ""
|
|
207
|
+
].filter(Boolean);
|
|
208
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [code ? /* @__PURE__ */ jsx(CodeBlock, {
|
|
209
|
+
className: "activity__code-block",
|
|
210
|
+
code,
|
|
211
|
+
language: "ts"
|
|
212
|
+
}) : null, sections.length ? /* @__PURE__ */ jsx(ActivityOutput, { children: sections.join("\n\n") }) : null] });
|
|
213
|
+
}
|
|
214
|
+
function formatPendingActions(pending) {
|
|
215
|
+
return pending.map((action) => {
|
|
216
|
+
const args = action.args === void 0 ? "" : JSON.stringify(action.args);
|
|
217
|
+
return `awaiting approval: ${action.connector}.${action.method}(${args})`;
|
|
218
|
+
}).join("\n");
|
|
219
|
+
}
|
|
220
|
+
function executeStatusText(output) {
|
|
221
|
+
if (!output) return void 0;
|
|
222
|
+
return output.status === "paused" ? "paused · needs approval" : output.status;
|
|
223
|
+
}
|
|
224
|
+
/** The codemode execute tool as an activity panel: highlighted TypeScript
|
|
225
|
+
plus logs, result, and the paused pending-approval list. */
|
|
226
|
+
function ThinkChatExecutePanel({ part }) {
|
|
227
|
+
if (part.state === "output-error") return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
228
|
+
label: "Code",
|
|
229
|
+
text: part.errorText,
|
|
230
|
+
status: "error",
|
|
231
|
+
error: true
|
|
232
|
+
});
|
|
233
|
+
if (part.state === "output-denied") return renderDeniedPanel("Code", part.approval.reason);
|
|
234
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
235
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
236
|
+
label: "Code",
|
|
237
|
+
status: executeStatusText(output),
|
|
238
|
+
error: output?.status === "error",
|
|
239
|
+
children: /* @__PURE__ */ jsx(ExecuteCodeView, {
|
|
240
|
+
code: part.input?.code ?? "",
|
|
241
|
+
output
|
|
242
|
+
})
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function ThinkChatBrowserPanel({ part }) {
|
|
246
|
+
const [seenAt] = React.useState(() => Date.now());
|
|
247
|
+
if (part.state === "output-error") return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
248
|
+
label: "Browser",
|
|
249
|
+
text: part.errorText,
|
|
250
|
+
status: "error",
|
|
251
|
+
error: true
|
|
252
|
+
});
|
|
253
|
+
if (part.state === "output-denied") return renderDeniedPanel("Browser", part.approval.reason);
|
|
254
|
+
if (part.type === "tool-browser_execute") {
|
|
255
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
256
|
+
const code = part.input?.code ?? "";
|
|
257
|
+
const media = output?.status === "completed" ? findBrowserResultMedia(output.result) : {};
|
|
258
|
+
const hasMedia = Boolean(media.liveViewUrl || media.screenshot);
|
|
259
|
+
const browserView = media.liveViewUrl ? /* @__PURE__ */ jsx(BrowserLiveView, {
|
|
260
|
+
url: media.liveViewUrl,
|
|
261
|
+
expiresAt: seenAt + (media.expiresInMs ?? 300 * 1e3),
|
|
262
|
+
fallback: media.screenshot ? /* @__PURE__ */ jsx("img", {
|
|
263
|
+
src: media.screenshot,
|
|
264
|
+
alt: "Last browser state"
|
|
265
|
+
}) : void 0
|
|
266
|
+
}) : media.screenshot ? /* @__PURE__ */ jsx("img", {
|
|
267
|
+
className: "activity__browser-screenshot",
|
|
268
|
+
src: media.screenshot,
|
|
269
|
+
alt: "Browser screenshot"
|
|
270
|
+
}) : null;
|
|
271
|
+
const codeView = /* @__PURE__ */ jsx(ExecuteCodeView, {
|
|
272
|
+
code,
|
|
273
|
+
output,
|
|
274
|
+
hideResult: hasMedia
|
|
275
|
+
});
|
|
276
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
277
|
+
label: "Browser",
|
|
278
|
+
status: executeStatusText(output),
|
|
279
|
+
error: output?.status === "error",
|
|
280
|
+
children: browserView && code ? /* @__PURE__ */ jsxs(Tabs, {
|
|
281
|
+
className: "activity__browser-tabs",
|
|
282
|
+
defaultValue: "browser",
|
|
283
|
+
children: [
|
|
284
|
+
/* @__PURE__ */ jsxs(TabsList, {
|
|
285
|
+
variant: "line",
|
|
286
|
+
children: [/* @__PURE__ */ jsx(Tab, {
|
|
287
|
+
value: "browser",
|
|
288
|
+
children: "Preview"
|
|
289
|
+
}), /* @__PURE__ */ jsx(Tab, {
|
|
290
|
+
value: "code",
|
|
291
|
+
children: "Code"
|
|
292
|
+
})]
|
|
293
|
+
}),
|
|
294
|
+
/* @__PURE__ */ jsx(TabPanel, {
|
|
295
|
+
value: "browser",
|
|
296
|
+
children: browserView
|
|
297
|
+
}),
|
|
298
|
+
/* @__PURE__ */ jsx(TabPanel, {
|
|
299
|
+
value: "code",
|
|
300
|
+
children: codeView
|
|
301
|
+
})
|
|
302
|
+
]
|
|
303
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [browserView, codeView] })
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
307
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
308
|
+
label: displayUrl(part.input?.url) || "page",
|
|
309
|
+
text: output === void 0 ? void 0 : typeof output === "string" ? output : Array.isArray(output) ? output.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join("\n") : JSON.stringify(output, null, 2),
|
|
310
|
+
status: part.type === "tool-browser_links" && Array.isArray(output) ? `${output.length} links` : void 0
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function ThinkChatShellPanel({ part }) {
|
|
314
|
+
const output = part.state === "output-available" ? part.output : void 0;
|
|
315
|
+
const errorText = part.state === "output-error" ? part.errorText : "";
|
|
316
|
+
const deniedReason = part.state === "output-denied" ? part.approval.reason ?? "denied" : "";
|
|
317
|
+
const failed = Boolean(errorText) || Boolean(deniedReason) || output !== void 0 && (output.exitCode !== 0 || Boolean(output.errors?.length));
|
|
318
|
+
return /* @__PURE__ */ jsx(ActivityPanel, {
|
|
319
|
+
label: "Shell",
|
|
320
|
+
text: formatBashPanelText(part.input, output, errorText || deniedReason),
|
|
321
|
+
status: output !== void 0 || errorText || deniedReason ? getBashStatusText(output, errorText, deniedReason) : void 0,
|
|
322
|
+
error: failed
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
function formatBashPanelText(input, output, errorText) {
|
|
326
|
+
return [
|
|
327
|
+
input?.script?.trim() ? `$ bash\n${input.script}` : "",
|
|
328
|
+
output?.stdout,
|
|
329
|
+
output?.stderr,
|
|
330
|
+
errorText,
|
|
331
|
+
output?.skippedFiles?.length ? ["skipped files", ...output.skippedFiles].join("\n") : "",
|
|
332
|
+
output?.errors?.length ? ["sync errors", ...output.errors].join("\n") : "",
|
|
333
|
+
output ? formatChangedFiles(output.changedFiles) : ""
|
|
334
|
+
].filter(Boolean).join("\n\n");
|
|
335
|
+
}
|
|
336
|
+
function formatChangedFiles(changedFiles) {
|
|
337
|
+
if (!changedFiles) return "";
|
|
338
|
+
const lines = [
|
|
339
|
+
...changedFiles.created.map((path) => `created ${path}`),
|
|
340
|
+
...changedFiles.updated.map((path) => `updated ${path}`),
|
|
341
|
+
...changedFiles.deleted.map((path) => `deleted ${path}`),
|
|
342
|
+
...changedFiles.directoriesCreated.map((path) => `created directory ${path}`),
|
|
343
|
+
...changedFiles.directoriesDeleted.map((path) => `deleted directory ${path}`)
|
|
344
|
+
];
|
|
345
|
+
return lines.length ? ["workspace changes", ...lines].join("\n") : "";
|
|
346
|
+
}
|
|
347
|
+
function getBashStatusText(output, errorText, deniedReason) {
|
|
348
|
+
if (errorText) return "error";
|
|
349
|
+
if (deniedReason) return "denied";
|
|
350
|
+
if (output?.errors?.length) return "sync errors";
|
|
351
|
+
if (output && output.exitCode !== 0) return `exit ${output.exitCode}`;
|
|
352
|
+
return "success";
|
|
353
|
+
}
|
|
354
|
+
//#endregion
|
|
355
|
+
export { ThinkChatBrowserPanel, ThinkChatExecutePanel, ThinkChatFetchPanel, ThinkChatSkillPanel, ThinkChatWorkspacePanel };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as isThinkChatActivityPart, a as ThinkBashToolOutput, c as ThinkWorkspaceToolName, d as isBrowserToolPart, f as isExecuteToolPart, g as isSkillToolPart, h as isPartWorking, i as SkillToolPart, l as WorkspaceToolPart, m as isPartFailed, n as ExecuteToolPart, o as ThinkBashToolPart, p as isFetchToolPart, r as FetchToolPart, s as ThinkChatPart, t as BrowserToolPart, u as displayUrl, v as isWorkspaceToolPart } from "../ThinkChatToolParts-DnUiZgCC.js";
|
|
2
|
+
export { BrowserToolPart, ExecuteToolPart, FetchToolPart, SkillToolPart, ThinkBashToolOutput, ThinkBashToolPart, ThinkChatPart, ThinkWorkspaceToolName, WorkspaceToolPart, displayUrl, isBrowserToolPart, isExecuteToolPart, isFetchToolPart, isPartFailed, isPartWorking, isSkillToolPart, isThinkChatActivityPart, isWorkspaceToolPart };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { getStaticToolName, getToolName, isReasoningUIPart, isStaticToolUIPart, isToolUIPart } from "ai";
|
|
2
|
+
import { getToolPartState } from "@cloudflare/think/react";
|
|
3
|
+
//#region src/components/ThinkChatToolParts.ts
|
|
4
|
+
const WORKSPACE_TOOL_NAMES = new Set([
|
|
5
|
+
"read",
|
|
6
|
+
"write",
|
|
7
|
+
"edit",
|
|
8
|
+
"list",
|
|
9
|
+
"find",
|
|
10
|
+
"grep",
|
|
11
|
+
"delete",
|
|
12
|
+
"bash"
|
|
13
|
+
]);
|
|
14
|
+
function isWorkspaceToolPart(part) {
|
|
15
|
+
return isStaticToolUIPart(part) && WORKSPACE_TOOL_NAMES.has(getStaticToolName(part));
|
|
16
|
+
}
|
|
17
|
+
const SKILL_TOOL_NAMES = new Set([
|
|
18
|
+
"activate_skill",
|
|
19
|
+
"read_skill_resource",
|
|
20
|
+
"run_skill_script"
|
|
21
|
+
]);
|
|
22
|
+
function isSkillToolPart(part) {
|
|
23
|
+
return isStaticToolUIPart(part) && SKILL_TOOL_NAMES.has(getStaticToolName(part));
|
|
24
|
+
}
|
|
25
|
+
function isFetchToolPart(part) {
|
|
26
|
+
return isStaticToolUIPart(part) && getStaticToolName(part).startsWith("fetch_");
|
|
27
|
+
}
|
|
28
|
+
const BROWSER_TOOL_NAMES = new Set([
|
|
29
|
+
"browser_execute",
|
|
30
|
+
"browser_markdown",
|
|
31
|
+
"browser_extract",
|
|
32
|
+
"browser_links",
|
|
33
|
+
"browser_scrape",
|
|
34
|
+
"browser_content"
|
|
35
|
+
]);
|
|
36
|
+
function isBrowserToolPart(part) {
|
|
37
|
+
return isStaticToolUIPart(part) && BROWSER_TOOL_NAMES.has(getStaticToolName(part));
|
|
38
|
+
}
|
|
39
|
+
function isExecuteToolPart(part) {
|
|
40
|
+
return isStaticToolUIPart(part) && getStaticToolName(part) === "execute";
|
|
41
|
+
}
|
|
42
|
+
/** Agent work (reasoning, tool calls, step boundaries) vs. content shown to the user. */
|
|
43
|
+
function isThinkChatActivityPart(part, excludeTools) {
|
|
44
|
+
if (part === void 0) return false;
|
|
45
|
+
if (isToolUIPart(part) && excludeTools?.has(getToolName(part))) return false;
|
|
46
|
+
return part.type === "step-start" || isReasoningUIPart(part) || isToolUIPart(part);
|
|
47
|
+
}
|
|
48
|
+
/** In-flight: the stream opened this part but hasn't delivered its result yet. */
|
|
49
|
+
function isPartWorking(part) {
|
|
50
|
+
if (isReasoningUIPart(part)) return part.state === "streaming";
|
|
51
|
+
if (isToolUIPart(part)) {
|
|
52
|
+
const state = getToolPartState(part);
|
|
53
|
+
return state === "loading" || state === "streaming" || state === "waiting-approval";
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/** Errored or denied — upstream's simplified tool-part state vocabulary. */
|
|
58
|
+
function isPartFailed(part) {
|
|
59
|
+
const state = getToolPartState(part);
|
|
60
|
+
return state === "error" || state === "denied";
|
|
61
|
+
}
|
|
62
|
+
function displayUrl(url) {
|
|
63
|
+
return url ? url.replace(/^https?:\/\//, "") : "";
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { displayUrl, isBrowserToolPart, isExecuteToolPart, isFetchToolPart, isPartFailed, isPartWorking, isSkillToolPart, isThinkChatActivityPart, isWorkspaceToolPart };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { A as CodeBlockLanguage } from "../CodeBlock-CxQXv3C4.js";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { FileInfo, Workspace } from "@cloudflare/shell";
|
|
4
3
|
import { WorkspaceLike } from "@cloudflare/think/tools/workspace";
|
|
4
|
+
import { FileInfo, Workspace } from "@cloudflare/shell";
|
|
5
5
|
|
|
6
6
|
//#region src/components/WorkspaceExplorer.d.ts
|
|
7
7
|
/** Workspace summary, as returned by `Workspace.getWorkspaceInfo()` upstream. */
|
|
@@ -22,8 +22,11 @@ interface UseStickToBottomResult {
|
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Pin a scrollable viewport to the bottom as its content grows, unless the
|
|
25
|
-
* user has scrolled up.
|
|
26
|
-
*
|
|
25
|
+
* user has scrolled up.
|
|
26
|
+
*
|
|
27
|
+
* @deprecated Use MessageScroller for chat transcripts. Keep this only for
|
|
28
|
+
* standalone streaming panes that do not need transcript row anchoring,
|
|
29
|
+
* history prepending, visibility tracking, or jump-to-message behavior.
|
|
27
30
|
*/
|
|
28
31
|
declare function useStickToBottom(options?: UseStickToBottomOptions): UseStickToBottomResult;
|
|
29
32
|
//#endregion
|
|
@@ -3,8 +3,11 @@ import * as React from "react";
|
|
|
3
3
|
const DEFAULT_STICK_SLACK_PX = 160;
|
|
4
4
|
/**
|
|
5
5
|
* Pin a scrollable viewport to the bottom as its content grows, unless the
|
|
6
|
-
* user has scrolled up.
|
|
7
|
-
*
|
|
6
|
+
* user has scrolled up.
|
|
7
|
+
*
|
|
8
|
+
* @deprecated Use MessageScroller for chat transcripts. Keep this only for
|
|
9
|
+
* standalone streaming panes that do not need transcript row anchoring,
|
|
10
|
+
* history prepending, visibility tracking, or jump-to-message behavior.
|
|
8
11
|
*/
|
|
9
12
|
function useStickToBottom(options = {}) {
|
|
10
13
|
const slackPx = options.slackPx ?? DEFAULT_STICK_SLACK_PX;
|
|
@@ -15,11 +15,23 @@
|
|
|
15
15
|
|
|
16
16
|
/* Room for the last message to scroll clear of the floating composer.
|
|
17
17
|
--agent-chat-composer-size is set from a ResizeObserver in AgentChat. */
|
|
18
|
-
.agent-
|
|
19
|
-
|
|
18
|
+
.agent-chat__transcript-content {
|
|
19
|
+
gap: 0.875rem;
|
|
20
|
+
padding-block: 1rem calc(var(--agent-chat-composer-size, 5rem) + 0.5rem);
|
|
21
|
+
padding-inline: 1rem;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
.agent-
|
|
24
|
+
.agent-chat__empty-row {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex: 1;
|
|
27
|
+
min-block-size: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.agent-chat__empty-row > .conversation__empty {
|
|
31
|
+
flex: 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.agent-chat__scroll-button {
|
|
23
35
|
inset-block-end: calc(var(--agent-chat-composer-size, 5rem) + 0.5rem);
|
|
24
36
|
}
|
|
25
37
|
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
|
|
48
48
|
.code-block__container {
|
|
49
49
|
position: relative;
|
|
50
|
-
overflow: auto;
|
|
51
|
-
|
|
50
|
+
overflow-x: auto;
|
|
51
|
+
overflow-y: clip;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
.code-block__content {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
font-size: 0.8125rem;
|
|
60
60
|
line-height: 1.55;
|
|
61
61
|
color: var(--sigvelo-color-text);
|
|
62
|
-
overflow
|
|
62
|
+
overflow: visible;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
.code-block__content--highlighted {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
font-family: var(--sigvelo-font-mono);
|
|
75
75
|
font-size: 0.8125rem;
|
|
76
76
|
line-height: 1.55;
|
|
77
|
-
overflow
|
|
77
|
+
overflow: visible;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
.code-block__content--numbered:is(pre) > code,
|
|
@@ -27,6 +27,16 @@
|
|
|
27
27
|
min-height: 100%;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
.conversation__empty-row {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex: 1;
|
|
33
|
+
min-block-size: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.conversation__empty-row > .conversation__empty {
|
|
37
|
+
flex: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
.conversation__empty {
|
|
31
41
|
display: flex;
|
|
32
42
|
flex: 1;
|
package/dist/styles/index.css
CHANGED
|
@@ -30,8 +30,10 @@
|
|
|
30
30
|
@import "./github-motion-mark.css" layer(components);
|
|
31
31
|
@import "./input.css" layer(components);
|
|
32
32
|
@import "./label.css" layer(components);
|
|
33
|
+
@import "./marker.css" layer(components);
|
|
33
34
|
@import "./menu.css" layer(components);
|
|
34
35
|
@import "./menubar.css" layer(components);
|
|
36
|
+
@import "./message-scroller.css" layer(components);
|
|
35
37
|
@import "./meter.css" layer(components);
|
|
36
38
|
@import "./navigation-menu.css" layer(components);
|
|
37
39
|
@import "./nanite-scene.css" layer(components);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
.marker {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 0.5rem;
|
|
6
|
+
width: 100%;
|
|
7
|
+
min-height: 1rem;
|
|
8
|
+
font-family: var(--sigvelo-font-sans);
|
|
9
|
+
font-size: var(--sigvelo-text-sm);
|
|
10
|
+
line-height: 1.5;
|
|
11
|
+
color: var(--sigvelo-color-text-muted);
|
|
12
|
+
text-align: start;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.marker:where(a, button) {
|
|
16
|
+
padding: 0;
|
|
17
|
+
font: inherit;
|
|
18
|
+
color: var(--sigvelo-color-text-muted);
|
|
19
|
+
text-decoration: none;
|
|
20
|
+
background: transparent;
|
|
21
|
+
border: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.marker:where(button) {
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.marker:where(a, button):hover {
|
|
29
|
+
color: var(--sigvelo-color-text);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.marker:where(a, button):focus-visible {
|
|
33
|
+
outline: none;
|
|
34
|
+
border-radius: var(--sigvelo-radius-sm);
|
|
35
|
+
box-shadow:
|
|
36
|
+
0 0 0 2px var(--sigvelo-color-canvas),
|
|
37
|
+
0 0 0 4px var(--sigvelo-color-focus);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.marker--separator::before,
|
|
41
|
+
.marker--separator::after {
|
|
42
|
+
content: "";
|
|
43
|
+
flex: 1 1 0;
|
|
44
|
+
min-width: 0;
|
|
45
|
+
height: var(--sigvelo-border-width);
|
|
46
|
+
background-color: var(--sigvelo-color-neutral-border-subtle);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.marker--separator::before {
|
|
50
|
+
margin-inline-end: 0.25rem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.marker--separator::after {
|
|
54
|
+
margin-inline-start: 0.25rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.marker--border {
|
|
58
|
+
padding-block-end: 0.5rem;
|
|
59
|
+
border-block-end: var(--sigvelo-border-width) var(--sigvelo-border-style)
|
|
60
|
+
var(--sigvelo-color-neutral-border-subtle);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.marker__icon {
|
|
64
|
+
display: inline-flex;
|
|
65
|
+
flex-shrink: 0;
|
|
66
|
+
width: 1rem;
|
|
67
|
+
height: 1rem;
|
|
68
|
+
align-items: center;
|
|
69
|
+
justify-content: center;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.marker__icon svg {
|
|
73
|
+
width: 1rem;
|
|
74
|
+
height: 1rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.marker__content {
|
|
78
|
+
min-width: 0;
|
|
79
|
+
overflow-wrap: anywhere;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.marker--separator .marker__content {
|
|
83
|
+
flex: 0 1 auto;
|
|
84
|
+
text-align: center;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.marker a {
|
|
88
|
+
color: inherit;
|
|
89
|
+
text-decoration: underline;
|
|
90
|
+
text-underline-offset: 0.1875rem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.marker a:hover {
|
|
94
|
+
color: var(--sigvelo-color-text);
|
|
95
|
+
}
|