@springbrand/message-panel 0.1.2 → 0.1.3-alpha.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 (99) hide show
  1. package/demo/camel-chat-showcase.tsx +1265 -0
  2. package/demo/fixtures.ts +292 -0
  3. package/demo/message-panel-chat-showcase.tsx +250 -0
  4. package/demo/message-panel-gallery.tsx +389 -0
  5. package/package.json +6 -16
  6. package/src/adapters/ai-sdk.ts +32 -0
  7. package/src/camel/camel-chat-messages.tsx +1423 -0
  8. package/src/camel/camel-model-select.tsx +88 -0
  9. package/src/camel/camel-plan-bar.tsx +115 -0
  10. package/src/camel/camel-prompt-input.tsx +547 -0
  11. package/src/camel/camel-tool-presentation.tsx +1270 -0
  12. package/src/camel/camel-turn.ts +316 -0
  13. package/src/camel/turn-summary-bar.tsx +81 -0
  14. package/src/chat-summary-panel.tsx +229 -0
  15. package/src/composer/chat-composer.tsx +216 -0
  16. package/src/composer/composer-attachments.tsx +138 -0
  17. package/src/composer/composer-trigger-popover.tsx +236 -0
  18. package/src/composer/composer.tsx +425 -0
  19. package/src/composer/drop-controller.ts +80 -0
  20. package/src/composer/types.ts +79 -0
  21. package/src/contracts.ts +138 -0
  22. package/src/conversation.tsx +76 -0
  23. package/src/internal/cn.ts +3 -0
  24. package/src/message-panel.tsx +368 -0
  25. package/src/message.tsx +254 -0
  26. package/src/parts/index.tsx +1149 -0
  27. package/src/parts/kind.ts +67 -0
  28. package/src/parts/plan.ts +63 -0
  29. package/src/parts/primitives.tsx +439 -0
  30. package/src/registry.ts +44 -0
  31. package/src/status.tsx +132 -0
  32. package/dist/demo/camel-chat-showcase.d.ts +0 -1
  33. package/dist/demo/camel-chat-showcase.js +0 -1007
  34. package/dist/demo/fixtures.d.ts +0 -32
  35. package/dist/demo/fixtures.js +0 -250
  36. package/dist/demo/index.js +0 -4
  37. package/dist/demo/message-panel-chat-showcase.d.ts +0 -1
  38. package/dist/demo/message-panel-chat-showcase.js +0 -114
  39. package/dist/demo/message-panel-gallery.d.ts +0 -1
  40. package/dist/demo/message-panel-gallery.js +0 -130
  41. package/dist/src/adapters/ai-sdk.d.ts +0 -4
  42. package/dist/src/adapters/ai-sdk.js +0 -25
  43. package/dist/src/camel/camel-chat-messages.d.ts +0 -44
  44. package/dist/src/camel/camel-chat-messages.js +0 -421
  45. package/dist/src/camel/camel-model-select.d.ts +0 -13
  46. package/dist/src/camel/camel-model-select.js +0 -7
  47. package/dist/src/camel/camel-plan-bar.d.ts +0 -5
  48. package/dist/src/camel/camel-plan-bar.js +0 -24
  49. package/dist/src/camel/camel-prompt-input.d.ts +0 -32
  50. package/dist/src/camel/camel-prompt-input.js +0 -182
  51. package/dist/src/camel/camel-tool-presentation.d.ts +0 -21
  52. package/dist/src/camel/camel-tool-presentation.js +0 -784
  53. package/dist/src/camel/camel-turn.d.ts +0 -57
  54. package/dist/src/camel/camel-turn.js +0 -203
  55. package/dist/src/camel/index.js +0 -7
  56. package/dist/src/camel/turn-summary-bar.d.ts +0 -8
  57. package/dist/src/camel/turn-summary-bar.js +0 -12
  58. package/dist/src/chat-summary-panel.d.ts +0 -22
  59. package/dist/src/chat-summary-panel.js +0 -26
  60. package/dist/src/composer/chat-composer.d.ts +0 -4
  61. package/dist/src/composer/chat-composer.js +0 -90
  62. package/dist/src/composer/composer-attachments.d.ts +0 -13
  63. package/dist/src/composer/composer-attachments.js +0 -47
  64. package/dist/src/composer/composer-trigger-popover.d.ts +0 -31
  65. package/dist/src/composer/composer-trigger-popover.js +0 -29
  66. package/dist/src/composer/composer.d.ts +0 -15
  67. package/dist/src/composer/composer.js +0 -180
  68. package/dist/src/composer/drop-controller.d.ts +0 -8
  69. package/dist/src/composer/drop-controller.js +0 -69
  70. package/dist/src/composer/index.js +0 -6
  71. package/dist/src/composer/types.d.ts +0 -62
  72. package/dist/src/composer/types.js +0 -1
  73. package/dist/src/contracts.d.ts +0 -94
  74. package/dist/src/contracts.js +0 -1
  75. package/dist/src/conversation.d.ts +0 -13
  76. package/dist/src/conversation.js +0 -18
  77. package/dist/src/index.js +0 -12
  78. package/dist/src/internal/cn.d.ts +0 -1
  79. package/dist/src/internal/cn.js +0 -3
  80. package/dist/src/message-panel.d.ts +0 -4
  81. package/dist/src/message-panel.js +0 -144
  82. package/dist/src/message.d.ts +0 -36
  83. package/dist/src/message.js +0 -83
  84. package/dist/src/parts/index.d.ts +0 -187
  85. package/dist/src/parts/index.js +0 -332
  86. package/dist/src/parts/kind.d.ts +0 -3
  87. package/dist/src/parts/kind.js +0 -69
  88. package/dist/src/parts/plan.d.ts +0 -10
  89. package/dist/src/parts/plan.js +0 -40
  90. package/dist/src/parts/primitives.d.ts +0 -70
  91. package/dist/src/parts/primitives.js +0 -153
  92. package/dist/src/registry.d.ts +0 -4
  93. package/dist/src/registry.js +0 -31
  94. package/dist/src/status.d.ts +0 -26
  95. package/dist/src/status.js +0 -74
  96. /package/{dist/demo/index.d.ts → demo/index.ts} +0 -0
  97. /package/{dist/src/camel/index.d.ts → src/camel/index.ts} +0 -0
  98. /package/{dist/src/composer/index.d.ts → src/composer/index.ts} +0 -0
  99. /package/{dist/src/index.d.ts → src/index.ts} +0 -0
@@ -1,784 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { CheckIcon, CopyIcon, } from "lucide-react";
3
- import { useEffect, useRef, useState, } from "react";
4
- import { MessageResponse } from "../message";
5
- import { toolNameOfPart } from "../parts";
6
- import { cn } from "../internal/cn";
7
- function recordOf(value) {
8
- return value != null && typeof value === "object" && !Array.isArray(value)
9
- ? value
10
- : {};
11
- }
12
- function safeStringify(value) {
13
- if (typeof value === "string")
14
- return value;
15
- try {
16
- return JSON.stringify(value, null, 2);
17
- }
18
- catch {
19
- return String(value);
20
- }
21
- }
22
- function stringField(record, ...keys) {
23
- for (const key of keys) {
24
- const value = record[key];
25
- if (typeof value === "string" && value.trim())
26
- return value.trim();
27
- }
28
- return "";
29
- }
30
- function numberField(record, ...keys) {
31
- for (const key of keys) {
32
- const value = record[key];
33
- if (typeof value === "number" && Number.isFinite(value))
34
- return value;
35
- }
36
- return undefined;
37
- }
38
- function normalizeOutputText(value) {
39
- if (value == null)
40
- return "";
41
- if (typeof value === "string")
42
- return value;
43
- if (Array.isArray(value)) {
44
- return value
45
- .map((item) => {
46
- const record = recordOf(item);
47
- return typeof record.text === "string"
48
- ? record.text
49
- : safeStringify(item);
50
- })
51
- .filter(Boolean)
52
- .join("\n\n");
53
- }
54
- const record = recordOf(value);
55
- if (typeof record.text === "string")
56
- return record.text;
57
- if (typeof record.content === "string")
58
- return record.content;
59
- if (Array.isArray(record.content))
60
- return normalizeOutputText(record.content);
61
- return safeStringify(value);
62
- }
63
- function stripAnsi(value) {
64
- // ANSI CSI and OSC sequences should never affect chat layout.
65
- // eslint-disable-next-line no-control-regex
66
- return value.replace(/\x1b\[[0-9;]*[a-zA-Z]|\x1b\].*?(?:\x07|\x1b\\)/g, "");
67
- }
68
- function previewLines(value, maxLines) {
69
- if (!value)
70
- return "";
71
- const lines = value.split(/\r?\n/);
72
- return lines.length <= maxLines
73
- ? value
74
- : [...lines.slice(0, maxLines), "..."].join("\n");
75
- }
76
- function truncate(value, maxLength) {
77
- return value.length <= maxLength
78
- ? value
79
- : `${value.slice(0, maxLength)}...`;
80
- }
81
- function filenameOf(path) {
82
- const trimmed = path.trim().replace(/\/+$/, "");
83
- return trimmed.split("/").pop() || trimmed;
84
- }
85
- function hostnameOf(url) {
86
- try {
87
- return new URL(url).hostname;
88
- }
89
- catch {
90
- return url;
91
- }
92
- }
93
- function humanize(name) {
94
- const spaced = name
95
- .replace(/^mcp__/, "")
96
- .replace(/__/g, " ")
97
- .replace(/[_-]+/g, " ")
98
- .replace(/([a-z])([A-Z])/g, "$1 $2")
99
- .trim();
100
- return spaced
101
- ? spaced.replace(/\b\w/g, (character) => character.toUpperCase())
102
- : "Tool";
103
- }
104
- function canonicalToolKind(name) {
105
- const normalized = name.trim().replaceAll("-", "_").toLowerCase();
106
- if (normalized.startsWith("mcp__"))
107
- return "mcp";
108
- if ([
109
- "read",
110
- "read_file",
111
- "read_text_file",
112
- "read_document",
113
- ].includes(normalized)) {
114
- return "read";
115
- }
116
- if ([
117
- "write",
118
- "write_file",
119
- "create_file",
120
- "save_file",
121
- ].includes(normalized)) {
122
- return "write";
123
- }
124
- if ([
125
- "edit",
126
- "edit_file",
127
- "apply_patch",
128
- "patch_file",
129
- ].includes(normalized)) {
130
- return "edit";
131
- }
132
- if ([
133
- "bash",
134
- "shell",
135
- "exec",
136
- "exec_command",
137
- "run_command",
138
- "terminal",
139
- ].includes(normalized)) {
140
- return "bash";
141
- }
142
- if (["glob", "find_files", "list_files_recursive"].includes(normalized)) {
143
- return "glob";
144
- }
145
- if ([
146
- "grep",
147
- "rg",
148
- "search_files",
149
- "search_code",
150
- ].includes(normalized)) {
151
- return "grep";
152
- }
153
- if (["ls", "list_directory", "list_files"].includes(normalized)) {
154
- return "list";
155
- }
156
- if (["find", "find_file"].includes(normalized))
157
- return "find";
158
- if ([
159
- "web_search",
160
- "search_web",
161
- "search_query",
162
- ].includes(normalized)) {
163
- return "web-search";
164
- }
165
- if ([
166
- "web_fetch",
167
- "fetch_url",
168
- "open_url",
169
- "fetch_page",
170
- ].includes(normalized)) {
171
- return "web-fetch";
172
- }
173
- if ([
174
- "javascript",
175
- "js_exec",
176
- "node_repl",
177
- "execute_javascript",
178
- ].includes(normalized)) {
179
- return "javascript";
180
- }
181
- if ([
182
- "todo_write",
183
- "update_todo",
184
- "update_plan",
185
- ].includes(normalized)) {
186
- return "tasks";
187
- }
188
- if ([
189
- "task",
190
- "task_output",
191
- "agent",
192
- "run_agent",
193
- "run_temporary_agent",
194
- "subagents",
195
- "fanout",
196
- "extract",
197
- "dispatch_background",
198
- "explore",
199
- "research",
200
- "oracle",
201
- ].includes(normalized)) {
202
- return "agent";
203
- }
204
- if (["skill", "read_skill"].includes(normalized))
205
- return "skill";
206
- if (normalized === "read_skill_resource")
207
- return "skill-resource";
208
- if ([
209
- "ask_user",
210
- "ask_user_question",
211
- "request_user_input",
212
- ].includes(normalized)) {
213
- return "ask-user";
214
- }
215
- if (["suggest_followups", "suggestions"].includes(normalized)) {
216
- return "suggestions";
217
- }
218
- if (["notebook_edit", "edit_notebook"].includes(normalized)) {
219
- return "notebook";
220
- }
221
- return "generic";
222
- }
223
- function parseMcpName(name) {
224
- if (!name.startsWith("mcp__"))
225
- return null;
226
- const [, server = "", ...toolParts] = name.split("__");
227
- if (!server || toolParts.length === 0)
228
- return null;
229
- return {
230
- server: humanize(server),
231
- tool: humanize(toolParts.join(" ")),
232
- };
233
- }
234
- function toolInput(part) {
235
- if (part.input != null)
236
- return recordOf(part.input);
237
- const data = recordOf(part.data);
238
- if (data.input != null)
239
- return recordOf(data.input);
240
- return part.type === "tool" ? data : {};
241
- }
242
- function toolOutput(part) {
243
- const data = recordOf(part.data);
244
- return part.output ?? data.output ?? part.result;
245
- }
246
- function parseCount(outputText, key) {
247
- const match = outputText.match(new RegExp(`Found\\s+(\\d+)\\s+${key}`, "i"));
248
- return match ? Number.parseInt(match[1] ?? "0", 10) : null;
249
- }
250
- function fileSummary(input, state, labels) {
251
- const path = stringField(input, "file_path", "path", "filename");
252
- const filename = path ? filenameOf(path) : "";
253
- if (state.running) {
254
- return filename
255
- ? `${labels.running} ${filename}...`
256
- : labels.runningGeneric;
257
- }
258
- if (state.failed) {
259
- return filename ? `${labels.failed} ${filename}` : labels.failed;
260
- }
261
- return filename ? `${labels.complete} ${filename}` : labels.complete;
262
- }
263
- function questionsOf(input) {
264
- if (Array.isArray(input.questions))
265
- return input.questions.map(recordOf);
266
- if (typeof input.question === "string") {
267
- return [
268
- {
269
- question: input.question,
270
- header: input.header,
271
- options: input.options,
272
- },
273
- ];
274
- }
275
- return [];
276
- }
277
- function summaryFor(kind, name, input, outputText, running, failed) {
278
- const state = { running, failed };
279
- switch (kind) {
280
- case "read":
281
- return fileSummary(input, state, {
282
- running: "Reading",
283
- runningGeneric: "Reading file...",
284
- failed: "Failed to read",
285
- complete: "Read",
286
- });
287
- case "write":
288
- return fileSummary(input, state, {
289
- running: "Creating",
290
- runningGeneric: "Creating file...",
291
- failed: "Failed to create",
292
- complete: "Created",
293
- });
294
- case "edit":
295
- return fileSummary(input, state, {
296
- running: "Editing",
297
- runningGeneric: "Editing file...",
298
- failed: "Failed to edit",
299
- complete: "Edited",
300
- });
301
- case "bash": {
302
- const command = stringField(input, "command", "cmd");
303
- const description = stringField(input, "description");
304
- const label = description || truncate(command || "command", 30);
305
- return running
306
- ? command || description
307
- ? `Running ${label}...`
308
- : "Running command..."
309
- : failed
310
- ? `Failed to run ${label}`
311
- : `Ran ${label}`;
312
- }
313
- case "glob": {
314
- const pattern = stringField(input, "pattern", "glob");
315
- if (running) {
316
- return pattern
317
- ? `Searching for "${truncate(pattern, 20)}"...`
318
- : "Searching for files...";
319
- }
320
- if (failed)
321
- return "Failed to search files";
322
- const count = parseCount(outputText, "files");
323
- if (count != null)
324
- return `Found ${count} files`;
325
- return /no files found/i.test(outputText)
326
- ? "No files found"
327
- : "Searched files";
328
- }
329
- case "grep": {
330
- const pattern = stringField(input, "pattern", "query");
331
- if (running) {
332
- return pattern
333
- ? `Searching for "${truncate(pattern, 20)}"...`
334
- : "Searching...";
335
- }
336
- if (failed)
337
- return "Failed to search codebase";
338
- const count = parseCount(outputText, "matches");
339
- if (count != null)
340
- return `Found ${count} matches`;
341
- return /no matches found/i.test(outputText)
342
- ? "No matches found"
343
- : "Searched codebase";
344
- }
345
- case "list": {
346
- const path = stringField(input, "path");
347
- const filename = path ? filenameOf(path) : "";
348
- if (running)
349
- return filename ? `Listing ${filename}...` : "Listing files...";
350
- if (failed)
351
- return filename ? `Failed to list ${filename}` : "Failed to list files";
352
- return filename ? `Listed ${filename}` : "Listed files";
353
- }
354
- case "find": {
355
- const pattern = stringField(input, "pattern", "query");
356
- if (running) {
357
- return pattern
358
- ? `Finding "${truncate(pattern, 20)}"...`
359
- : "Finding files...";
360
- }
361
- return failed ? "Failed to find files" : "Found files";
362
- }
363
- case "web-search":
364
- return running
365
- ? "Searching web..."
366
- : failed
367
- ? "Failed to search web"
368
- : "Searched web";
369
- case "web-fetch": {
370
- const url = stringField(input, "url");
371
- const host = url ? hostnameOf(url) : "web page";
372
- return running
373
- ? `Fetching ${host}...`
374
- : failed
375
- ? `Failed to fetch ${host}`
376
- : `Fetched ${host}`;
377
- }
378
- case "javascript": {
379
- const label = stringField(input, "description") || "JavaScript";
380
- return running
381
- ? `Running ${label}...`
382
- : failed
383
- ? `${label} failed`
384
- : `Ran ${label}`;
385
- }
386
- case "tasks":
387
- return running
388
- ? "Updating tasks..."
389
- : failed
390
- ? "Failed to update tasks"
391
- : "Updated tasks";
392
- case "agent": {
393
- const description = stringField(input, "description", "question", "prompt") || "subtask";
394
- return running
395
- ? `Working on ${truncate(description, 52)}`
396
- : failed
397
- ? `Could not finish ${truncate(description, 52)}`
398
- : `Finished ${truncate(description, 52)}`;
399
- }
400
- case "skill": {
401
- const skill = stringField(input, "skill", "name");
402
- return running
403
- ? skill
404
- ? `Reading instructions for ${skill}...`
405
- : "Reading instructions..."
406
- : failed
407
- ? skill
408
- ? `Could not read instructions for ${skill}`
409
- : "Could not read instructions"
410
- : `Read instructions for ${skill || "task"}`;
411
- }
412
- case "skill-resource": {
413
- const path = stringField(input, "path");
414
- const resource = path ? filenameOf(path) : "resource";
415
- return running
416
- ? `Reading ${resource}...`
417
- : failed
418
- ? `Could not read ${resource}`
419
- : `Read ${resource}`;
420
- }
421
- case "ask-user": {
422
- const questions = questionsOf(input);
423
- if (running)
424
- return "Waiting for your input";
425
- if (questions.length === 1) {
426
- const header = stringField(questions[0] ?? {}, "header");
427
- return header || "Asked a question";
428
- }
429
- return questions.length > 1
430
- ? `Asked ${questions.length} questions`
431
- : "Asked a question";
432
- }
433
- case "suggestions":
434
- return running ? "Preparing follow-ups..." : "Suggested follow-ups";
435
- case "notebook":
436
- return running
437
- ? "Editing notebook cell..."
438
- : failed
439
- ? "Failed to edit notebook cell"
440
- : "Edited notebook cell";
441
- case "mcp": {
442
- const parts = parseMcpName(name);
443
- if (!parts)
444
- break;
445
- return running
446
- ? `Calling ${parts.tool} on ${parts.server}...`
447
- : failed
448
- ? `Failed to call ${parts.tool} on ${parts.server}`
449
- : `Called ${parts.tool} on ${parts.server}`;
450
- }
451
- case "generic":
452
- break;
453
- }
454
- if (name === "create_agent") {
455
- const agent = stringField(input, "name") || "agent";
456
- return running
457
- ? `Creating ${agent}...`
458
- : failed
459
- ? `Could not create ${agent}`
460
- : `Created ${agent}`;
461
- }
462
- if (name === "publish_extension") {
463
- const extension = humanize(stringField(input, "name") || "extension");
464
- return running
465
- ? `Publishing ${extension}...`
466
- : failed
467
- ? `Could not publish ${extension}`
468
- : `Published ${extension}`;
469
- }
470
- if (name === "cancel_schedule") {
471
- return running
472
- ? "Cancelling schedule..."
473
- : failed
474
- ? "Schedule cancellation was denied"
475
- : "Cancelled schedule";
476
- }
477
- const displayName = humanize(name);
478
- return running
479
- ? `Using ${displayName}...`
480
- : failed
481
- ? `Could not use ${displayName}`
482
- : `Used ${displayName}`;
483
- }
484
- export function getCamelToolPresentation(part, isActive = false) {
485
- const record = recordOf(part);
486
- const name = toolNameOfPart(part) ?? (stringField(record, "name") || "tool");
487
- const input = toolInput(record);
488
- const output = toolOutput(record);
489
- const outputText = normalizeOutputText(output);
490
- const errorText = stringField(record, "errorText", "error");
491
- const state = stringField(record, "state", "status");
492
- const failed = state === "output-error" ||
493
- state === "output-denied" ||
494
- state === "failed" ||
495
- Boolean(errorText);
496
- const running = isActive &&
497
- [
498
- "input-streaming",
499
- "input-available",
500
- "approval-responded",
501
- "approval-requested",
502
- "running",
503
- "",
504
- ].includes(state);
505
- const kind = canonicalToolKind(name);
506
- const hasDetails = Object.keys(input).length > 0 ||
507
- output != null ||
508
- Boolean(errorText);
509
- return {
510
- kind,
511
- name,
512
- summary: summaryFor(kind, name, input, errorText || outputText, running, failed),
513
- input,
514
- output,
515
- outputText,
516
- errorText,
517
- running,
518
- failed,
519
- isSubAgent: kind === "agent",
520
- hasDetails,
521
- };
522
- }
523
- function ToolCopyButton({ value, label }) {
524
- const [copied, setCopied] = useState(false);
525
- const timerRef = useRef(null);
526
- useEffect(() => () => {
527
- if (timerRef.current != null)
528
- window.clearTimeout(timerRef.current);
529
- }, []);
530
- if (!value)
531
- return null;
532
- return (_jsx("button", { type: "button", className: "inline-flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground/60 opacity-0 transition-opacity hover:bg-muted group-hover/details:opacity-100 group-focus-within/details:opacity-100", onClick: () => {
533
- void navigator.clipboard.writeText(value).then(() => {
534
- setCopied(true);
535
- if (timerRef.current != null)
536
- window.clearTimeout(timerRef.current);
537
- timerRef.current = window.setTimeout(() => setCopied(false), 1500);
538
- });
539
- }, "aria-label": copied ? "Copied" : label, children: copied ? _jsx(CheckIcon, { className: "size-3" }) : _jsx(CopyIcon, { className: "size-3" }) }));
540
- }
541
- function DetailRow({ label, value, copyValue, mono = false, }) {
542
- if (value == null || value === "")
543
- return null;
544
- return (_jsxs("div", { className: "flex items-start gap-2 py-0.5", children: [_jsx("span", { className: "shrink-0 text-muted-foreground/60", children: label }), _jsx("div", { className: cn("min-w-0 flex-1 break-words", mono && "font-mono"), children: value }), copyValue ? _jsx(ToolCopyButton, { value: copyValue, label: `Copy ${label}` }) : null] }));
545
- }
546
- function OutputBlock({ label, value, className, }) {
547
- if (!value)
548
- return null;
549
- const displayValue = stripAnsi(value);
550
- return (_jsxs("div", { className: cn("mt-2", className), children: [_jsxs("div", { className: "mb-1 flex items-center justify-between text-[0.7rem] text-muted-foreground/60", children: [_jsx("span", { children: label }), _jsx(ToolCopyButton, { value: displayValue, label: `Copy ${label}` })] }), _jsx("div", { className: "max-h-32 overflow-auto rounded bg-muted/30 p-2 font-mono text-xs", children: _jsx("pre", { className: "whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground/80", children: displayValue }) })] }));
551
- }
552
- function ProjectRow({ input }) {
553
- const project = stringField(input, "project");
554
- return project ? (_jsx(DetailRow, { label: "Project:", value: project, copyValue: project, mono: true })) : null;
555
- }
556
- function ReadDetails({ presentation }) {
557
- const { input, outputText } = presentation;
558
- const path = stringField(input, "file_path", "path", "filename");
559
- const lines = outputText ? outputText.split(/\r?\n/).length : 0;
560
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Path:", value: path, copyValue: path, mono: true }), _jsx(ProjectRow, { input: input }), _jsx(DetailRow, { label: "Lines:", value: String(lines) }), _jsx(OutputBlock, { label: "Preview", value: previewLines(outputText, 10) })] }));
561
- }
562
- function WriteDetails({ presentation }) {
563
- const { input } = presentation;
564
- const path = stringField(input, "file_path", "path", "filename");
565
- const content = normalizeOutputText(input.content);
566
- const bytes = new TextEncoder().encode(content).byteLength;
567
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Path:", value: path, copyValue: path, mono: true }), _jsx(ProjectRow, { input: input }), content ? _jsx(DetailRow, { label: "Size:", value: formatBytes(bytes) }) : null, _jsx(OutputBlock, { label: "Preview", value: previewLines(content, 10) })] }));
568
- }
569
- function diffLines(input, output) {
570
- const outputRecord = recordOf(output);
571
- const rawDiff = stringField(outputRecord, "diff") ||
572
- stringField(recordOf(outputRecord.details), "diff");
573
- if (rawDiff) {
574
- const lines = rawDiff.split(/\r?\n/);
575
- return [
576
- ...lines.slice(0, 200).map((text) => ({
577
- kind: text.startsWith("+")
578
- ? "add"
579
- : text.startsWith("-")
580
- ? "remove"
581
- : "context",
582
- text,
583
- })),
584
- ...(lines.length > 200
585
- ? [{ kind: "context", text: "... [diff truncated]" }]
586
- : []),
587
- ];
588
- }
589
- const edits = Array.isArray(input.edits)
590
- ? input.edits.map(recordOf)
591
- : [input];
592
- const result = [];
593
- for (const edit of edits) {
594
- const oldText = stringField(edit, "old_string", "oldText");
595
- const newText = stringField(edit, "new_string", "newText");
596
- for (const text of oldText.split(/\r?\n/).filter(Boolean).slice(0, 6)) {
597
- result.push({ kind: "remove", text: `- ${text}` });
598
- }
599
- for (const text of newText.split(/\r?\n/).filter(Boolean).slice(0, 6)) {
600
- result.push({ kind: "add", text: `+ ${text}` });
601
- }
602
- }
603
- return result;
604
- }
605
- function EditDetails({ presentation }) {
606
- const { input, output } = presentation;
607
- const path = stringField(input, "file_path", "path", "filename");
608
- const edits = Array.isArray(input.edits)
609
- ? input.edits.length
610
- : stringField(input, "old_string", "oldText", "new_string", "newText")
611
- ? 1
612
- : 0;
613
- const outputRecord = recordOf(output);
614
- const replacementCount = numberField(outputRecord, "replacementCount") ??
615
- numberField(recordOf(outputRecord.details), "replacementCount") ??
616
- edits;
617
- const lines = diffLines(input, output);
618
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Path:", value: path, copyValue: path, mono: true }), _jsx(ProjectRow, { input: input }), _jsx(DetailRow, { label: "Changes:", value: `${replacementCount} replacements` }), lines.length > 0 ? (_jsx("div", { className: "mt-2 max-h-32 overflow-auto rounded bg-muted/30 p-2 font-mono text-xs", children: lines.map((line, index) => (_jsx("div", { className: cn("whitespace-pre-wrap break-words", line.kind === "add" && "text-green-600/80 dark:text-green-400/80", line.kind === "remove" && "text-red-600/80 dark:text-red-400/80", line.kind === "context" && "text-muted-foreground/60"), children: line.text }, `${line.kind}:${index}`))) })) : (_jsx(DetailRow, { label: "Diff:", value: "No diff available" }))] }));
619
- }
620
- function BashDetails({ presentation }) {
621
- const { input, output, outputText } = presentation;
622
- const command = stringField(input, "command", "cmd");
623
- const outputRecord = recordOf(output);
624
- const parsedFromText = (() => {
625
- if (!outputText.trim().startsWith("{"))
626
- return {};
627
- try {
628
- return recordOf(JSON.parse(outputText));
629
- }
630
- catch {
631
- return {};
632
- }
633
- })();
634
- const result = Object.keys(outputRecord).length > 0
635
- ? outputRecord
636
- : parsedFromText;
637
- const stdout = stringField(result, "stdout");
638
- const stderr = stringField(result, "stderr");
639
- const exitCode = numberField(result, "exit_code", "exitCode");
640
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Command:", value: command, copyValue: command, mono: true }), _jsx(ProjectRow, { input: input }), _jsx(DetailRow, { label: "Description:", value: stringField(input, "description") }), exitCode != null ? (_jsx(DetailRow, { label: "Exit code:", value: String(exitCode) })) : null, _jsx(OutputBlock, { label: "Stdout", value: stdout }), _jsx(OutputBlock, { label: "Stderr", value: stderr }), !stdout && !stderr ? (_jsx(OutputBlock, { label: presentation.failed ? "Error" : "Output", value: presentation.errorText || outputText })) : null] }));
641
- }
642
- function SearchDetails({ presentation, label, }) {
643
- const { input, outputText } = presentation;
644
- const cleanedOutput = outputText
645
- .split(/\r?\n/)
646
- .filter((line) => !/^Found\s+\d+/i.test(line.trim()))
647
- .join("\n");
648
- const count = parseCount(outputText, label === "Files" ? "files" : "matches");
649
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Pattern:", value: stringField(input, "pattern", "glob", "query"), mono: true }), _jsx(DetailRow, { label: "Path:", value: stringField(input, "path", "cwd"), mono: true }), _jsx(ProjectRow, { input: input }), _jsx(DetailRow, { label: "Mode:", value: stringField(input, "output_mode") }), count != null ? _jsx(DetailRow, { label: "Count:", value: String(count) }) : null, _jsx(OutputBlock, { label: label, value: cleanedOutput || outputText })] }));
650
- }
651
- function ListDetails({ presentation }) {
652
- const { input, outputText } = presentation;
653
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Path:", value: stringField(input, "path"), mono: true }), _jsx(ProjectRow, { input: input }), _jsx(OutputBlock, { label: "Files", value: outputText })] }));
654
- }
655
- function WebDetails({ presentation, mode, }) {
656
- const { input, outputText } = presentation;
657
- return (_jsxs("div", { className: "space-y-1", children: [mode === "fetch" ? (_jsx(DetailRow, { label: "URL:", value: stringField(input, "url"), copyValue: stringField(input, "url"), mono: true })) : (_jsx(DetailRow, { label: "Query:", value: stringField(input, "query", "q"), copyValue: stringField(input, "query", "q") })), _jsx(DetailRow, { label: "Prompt:", value: stringField(input, "prompt") }), _jsx(OutputBlock, { label: mode === "fetch" ? "Response" : "Response", value: outputText })] }));
658
- }
659
- function TaskDetails({ presentation }) {
660
- const { input, output, outputText } = presentation;
661
- const tasks = Array.isArray(input.todos)
662
- ? input.todos.map(recordOf)
663
- : Array.isArray(input.steps)
664
- ? input.steps.map(recordOf)
665
- : [];
666
- return (_jsxs("div", { className: "space-y-2", children: [tasks.length > 0 ? (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-xs text-muted-foreground/60", children: "Tasks:" }), _jsx("div", { className: "space-y-1 pl-4", children: tasks.map((task, index) => {
667
- const status = stringField(task, "status") || "pending";
668
- const text = stringField(task, "content", "text", "activeForm") ||
669
- "Untitled task";
670
- return (_jsxs("div", { className: "flex items-start gap-2 text-xs", children: [_jsx("span", { className: "font-mono text-muted-foreground/60", children: ["done", "completed"].includes(status) ? "[x]" : "[ ]" }), _jsx("span", { className: "min-w-0 flex-1 text-muted-foreground/80", children: text }), _jsx("span", { className: "text-[0.65rem] uppercase text-muted-foreground/50", children: status })] }, `${status}:${index}`));
671
- }) })] })) : (_jsx(DetailRow, { label: "Tasks:", value: "No tasks provided" })), _jsx(OutputBlock, { label: "Result", value: typeof output === "object"
672
- ? summarizeResult(recordOf(output))
673
- : outputText })] }));
674
- }
675
- function summarizeResult(output) {
676
- const preferred = [
677
- "message",
678
- "summary",
679
- "status",
680
- "result",
681
- ].flatMap((key) => {
682
- const value = output[key];
683
- return value == null ? [] : [`${humanize(key)}: ${normalizeOutputText(value)}`];
684
- });
685
- if (preferred.length > 0)
686
- return preferred.join("\n");
687
- const counts = ["done", "total", "success", "ok"].flatMap((key) => output[key] == null ? [] : [`${humanize(key)}: ${String(output[key])}`]);
688
- return counts.join("\n");
689
- }
690
- function AgentDetails({ presentation }) {
691
- const { input, output, outputText, running, failed } = presentation;
692
- const outputRecord = recordOf(output);
693
- const activities = Array.isArray(outputRecord.activities)
694
- ? outputRecord.activities
695
- : Array.isArray(recordOf(outputRecord.details).activities)
696
- ? recordOf(outputRecord.details).activities
697
- : [];
698
- const request = stringField(input, "question", "prompt", "description");
699
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Task:", value: stringField(input, "description") }), _jsx(DetailRow, { label: "Agent:", value: stringField(input, "agent", "name") }), _jsx(DetailRow, { label: "Model:", value: stringField(input, "model") }), _jsx(DetailRow, { label: "Request:", value: request ? (_jsx("span", { className: "whitespace-pre-wrap break-words text-foreground/80", children: request })) : undefined }), _jsxs("div", { className: "mt-2", children: [_jsx("div", { className: "mb-1 text-[0.7rem] text-muted-foreground/60", children: "Activity" }), activities.length > 0 ? (_jsx("div", { className: "space-y-1", children: activities.slice(-10).map((activity, index) => (_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: cn("size-1.5 shrink-0 rounded-full bg-muted-foreground/40", running && index === activities.slice(-10).length - 1 &&
700
- "animate-pulse bg-blue-500", failed && "bg-red-500") }), _jsx("span", { className: "min-w-0 truncate text-muted-foreground/70", children: normalizeOutputText(activity) })] }, `${normalizeOutputText(activity)}:${index}`))) })) : (_jsx("div", { className: "text-muted-foreground/60", children: running
701
- ? "Waiting for the first tool call..."
702
- : failed
703
- ? "No tool activity was recorded before this run stopped."
704
- : "Detailed tool activity was not recorded." }))] }), outputText ? (_jsxs("div", { className: "mt-2", children: [_jsx("div", { className: "mb-1 text-[0.7rem] text-muted-foreground/60", children: "Agent result" }), _jsx("div", { className: "max-h-72 overflow-auto rounded bg-muted/30 p-2 text-sm text-foreground/90", children: _jsx(MessageResponse, { children: outputText }) })] })) : null, numberField(recordOf(outputRecord.details), "durationMs") != null ? (_jsx(DetailRow, { label: "Duration:", value: `${Math.max(1, Math.round(numberField(recordOf(outputRecord.details), "durationMs") / 1000))}s` })) : null] }));
705
- }
706
- function SkillDetails({ presentation, resource = false, }) {
707
- const { input, outputText } = presentation;
708
- const skill = stringField(input, "skill", "name");
709
- const path = stringField(input, "path");
710
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Skill:", value: skill, mono: true }), resource ? (_jsx(DetailRow, { label: "Resource:", value: path, copyValue: path, mono: true })) : null, _jsx(OutputBlock, { label: resource ? "Preview" : "Status", value: resource ? previewLines(outputText, 10) : outputText })] }));
711
- }
712
- function AskUserDetails({ presentation }) {
713
- const questions = questionsOf(presentation.input);
714
- const answer = presentation.outputText;
715
- return (_jsx("div", { className: "space-y-2", children: questions.length > 0 ? (questions.map((question, index) => (_jsxs("div", { children: [_jsx("p", { className: "font-medium text-foreground/85", children: stringField(question, "question", "prompt") || "Question" }), _jsx("p", { className: "text-muted-foreground/60", children: answer || "—" })] }, `${stringField(question, "question", "prompt")}:${index}`)))) : (_jsx(GenericDetails, { presentation: presentation })) }));
716
- }
717
- function JavaScriptDetails({ presentation }) {
718
- const { input, outputText } = presentation;
719
- const timeout = numberField(input, "timeoutMs", "timeout_ms");
720
- const outputLimit = numberField(input, "maxOutputCharacters", "max_output_characters");
721
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Runtime:", value: "JavaScript code mode" }), _jsx(DetailRow, { label: "Description:", value: stringField(input, "description") }), timeout != null ? (_jsx(DetailRow, { label: "Timeout:", value: `${timeout}ms` })) : null, outputLimit != null ? (_jsx(DetailRow, { label: "Output limit:", value: `${outputLimit} chars` })) : null, _jsx(OutputBlock, { label: "Code", value: stringField(input, "code") }), _jsx(OutputBlock, { label: "Output", value: outputText })] }));
722
- }
723
- function McpDetails({ presentation }) {
724
- const parsed = parseMcpName(presentation.name);
725
- return (_jsxs("div", { className: "space-y-1", children: [parsed ? (_jsxs(_Fragment, { children: [_jsx(DetailRow, { label: "MCP Server:", value: parsed.server }), _jsx(DetailRow, { label: "Tool:", value: parsed.tool })] })) : null, _jsx(GenericDetails, { presentation: presentation, hideProject: true })] }));
726
- }
727
- function GenericDetails({ presentation, hideProject = false, }) {
728
- const { input, outputText, errorText } = presentation;
729
- const inputText = Object.keys(input).length > 0 ? safeStringify(input) : "";
730
- return (_jsxs("div", { className: "space-y-1", children: [!hideProject ? _jsx(ProjectRow, { input: input }) : null, _jsx(OutputBlock, { label: "Input", value: inputText }), _jsx(OutputBlock, { label: presentation.failed ? "Error" : "Output", value: errorText || outputText }), !inputText && !outputText && !errorText ? (_jsx(DetailRow, { label: "Details:", value: "No additional data" })) : null] }));
731
- }
732
- export function CamelToolDetails({ presentation, }) {
733
- switch (presentation.kind) {
734
- case "read":
735
- return _jsx(ReadDetails, { presentation: presentation });
736
- case "write":
737
- return _jsx(WriteDetails, { presentation: presentation });
738
- case "edit":
739
- return _jsx(EditDetails, { presentation: presentation });
740
- case "bash":
741
- return _jsx(BashDetails, { presentation: presentation });
742
- case "glob":
743
- return _jsx(SearchDetails, { presentation: presentation, label: "Files" });
744
- case "grep":
745
- return _jsx(SearchDetails, { presentation: presentation, label: "Matches" });
746
- case "list":
747
- case "find":
748
- return _jsx(ListDetails, { presentation: presentation });
749
- case "web-search":
750
- return _jsx(WebDetails, { presentation: presentation, mode: "search" });
751
- case "web-fetch":
752
- return _jsx(WebDetails, { presentation: presentation, mode: "fetch" });
753
- case "javascript":
754
- return _jsx(JavaScriptDetails, { presentation: presentation });
755
- case "tasks":
756
- return _jsx(TaskDetails, { presentation: presentation });
757
- case "agent":
758
- return _jsx(AgentDetails, { presentation: presentation });
759
- case "skill":
760
- return _jsx(SkillDetails, { presentation: presentation });
761
- case "skill-resource":
762
- return _jsx(SkillDetails, { presentation: presentation, resource: true });
763
- case "ask-user":
764
- return _jsx(AskUserDetails, { presentation: presentation });
765
- case "suggestions":
766
- return (_jsx(OutputBlock, { label: "Suggestions", value: presentation.outputText || safeStringify(presentation.input) }));
767
- case "notebook":
768
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(DetailRow, { label: "Notebook:", value: stringField(presentation.input, "notebook_path", "path"), mono: true }), _jsx(DetailRow, { label: "Cell:", value: stringField(presentation.input, "cell_id", "cell_number") }), _jsx(OutputBlock, { label: "Code", value: stringField(presentation.input, "code", "new_source") })] }));
769
- case "mcp":
770
- return _jsx(McpDetails, { presentation: presentation });
771
- case "generic":
772
- return _jsx(GenericDetails, { presentation: presentation });
773
- }
774
- }
775
- function formatBytes(bytes) {
776
- if (!Number.isFinite(bytes))
777
- return "";
778
- if (bytes < 1024)
779
- return `${bytes} B`;
780
- const kilobytes = bytes / 1024;
781
- if (kilobytes < 1024)
782
- return `${kilobytes.toFixed(1)} KB`;
783
- return `${(kilobytes / 1024).toFixed(1)} MB`;
784
- }