@tangle-network/ui 8.0.0 → 8.1.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/CHANGELOG.md +6 -0
- package/dist/chat.d.ts +10 -1
- package/dist/chat.js +2 -2
- package/dist/{chunk-QIRVZMQY.js → chunk-C3BIVG72.js} +143 -107
- package/dist/{chunk-UOLL2YHG.js → chunk-QUAU6ZNC.js} +90 -42
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -3
- package/dist/run.d.ts +29 -1
- package/dist/run.js +4 -2
- package/package.json +2 -2
- package/src/chat/agent-timeline.tsx +139 -45
- package/src/run/assistant-run-shell.tsx +115 -0
- package/src/run/index.ts +1 -0
- package/src/run/run-group.tsx +12 -76
- /package/dist/{chunk-47XH56SV.js → chunk-LQS34IGP.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 8.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 79b55f5: Converge the two transcripts on one collapsible run. New `AssistantRunShell` primitive (the header · summary · status pill · chevron · Radix collapse extracted from `RunGroup`) is now used by both `RunGroup` and `AgentTimeline`, so there is one implementation of "an assistant run" instead of two divergent ones. `AgentTimeline` folds consecutive tool / tool-group items into that shell (`collapsibleToolRuns`, default on; `defaultToolRunsOpen`, default open) so a burst of tool activity reads as one toggleable step on the timeline spine instead of a long ladder of rows — matching `RunGroup`. Additive: `AgentTimeline`'s `items[]` API is unchanged and folding happens internally; consumers building their own item arrays keep working.
|
|
8
|
+
|
|
3
9
|
## 8.0.0
|
|
4
10
|
|
|
5
11
|
### Major Changes
|
package/dist/chat.d.ts
CHANGED
|
@@ -157,12 +157,21 @@ interface AgentTimelineProps {
|
|
|
157
157
|
isThinking?: boolean;
|
|
158
158
|
emptyState?: ReactNode;
|
|
159
159
|
className?: string;
|
|
160
|
+
/**
|
|
161
|
+
* Fold consecutive tool / tool-group items into one collapsible run shell
|
|
162
|
+
* (the same `AssistantRunShell` `RunGroup` uses), so a burst of tool activity
|
|
163
|
+
* reads as a single toggleable step instead of a long ladder of rows.
|
|
164
|
+
* Default true; pass false for the flat one-row-per-tool timeline.
|
|
165
|
+
*/
|
|
166
|
+
collapsibleToolRuns?: boolean;
|
|
167
|
+
/** Start collapsed tool runs open (true) or collapsed (false). Default open. */
|
|
168
|
+
defaultToolRunsOpen?: boolean;
|
|
160
169
|
}
|
|
161
170
|
/**
|
|
162
171
|
* AgentTimeline — unified mixed-content timeline for agent-backed sandbox
|
|
163
172
|
* sessions. Renders messages, tool steps, status cards, and artifact handoffs in
|
|
164
173
|
* a single execution narrative.
|
|
165
174
|
*/
|
|
166
|
-
declare function AgentTimeline({ items, isThinking, emptyState, className, }: AgentTimelineProps): react_jsx_runtime.JSX.Element | null;
|
|
175
|
+
declare function AgentTimeline({ items, isThinking, emptyState, className, collapsibleToolRuns, defaultToolRunsOpen, }: AgentTimelineProps): react_jsx_runtime.JSX.Element | null;
|
|
167
176
|
|
|
168
177
|
export { AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, MessageList, type MessageListProps, type MessageRole, ThinkingIndicator, type ThinkingIndicatorProps, UserMessage, type UserMessageProps };
|
package/dist/chat.js
CHANGED
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
MessageList,
|
|
6
6
|
ThinkingIndicator,
|
|
7
7
|
UserMessage
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QUAU6ZNC.js";
|
|
9
9
|
import "./chunk-AZWDI2JG.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-C3BIVG72.js";
|
|
11
11
|
import "./chunk-RKQDBRTC.js";
|
|
12
12
|
import "./chunk-ULDNFLIM.js";
|
|
13
13
|
import "./chunk-AAUNOHVL.js";
|
|
@@ -108,14 +108,68 @@ var InlineThinkingItem = memo(
|
|
|
108
108
|
);
|
|
109
109
|
InlineThinkingItem.displayName = "InlineThinkingItem";
|
|
110
110
|
|
|
111
|
+
// src/run/assistant-run-shell.tsx
|
|
112
|
+
import * as Collapsible2 from "@radix-ui/react-collapsible";
|
|
113
|
+
import { ChevronDown, ChevronRight as ChevronRight2, Loader2, Sparkles } from "lucide-react";
|
|
114
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
115
|
+
function AssistantRunShell({
|
|
116
|
+
label,
|
|
117
|
+
summary,
|
|
118
|
+
collapsedPreview,
|
|
119
|
+
badges,
|
|
120
|
+
isStreaming,
|
|
121
|
+
collapsed,
|
|
122
|
+
onToggle,
|
|
123
|
+
headerActions,
|
|
124
|
+
children,
|
|
125
|
+
className
|
|
126
|
+
}) {
|
|
127
|
+
return /* @__PURE__ */ jsx2(Collapsible2.Root, { open: !collapsed, onOpenChange: () => onToggle(), children: /* @__PURE__ */ jsxs2(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
className: cn(
|
|
131
|
+
"rounded-[28px] border border-[var(--border-subtle)] bg-[var(--bg-card)] shadow-none",
|
|
132
|
+
className
|
|
133
|
+
),
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-start gap-3 px-3 py-2.5", children: [
|
|
136
|
+
/* @__PURE__ */ jsx2(Collapsible2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx2(
|
|
137
|
+
"button",
|
|
138
|
+
{
|
|
139
|
+
type: "button",
|
|
140
|
+
className: "w-full rounded-[20px] bg-transparent px-0 py-0 text-left transition-colors hover:bg-transparent",
|
|
141
|
+
children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
142
|
+
/* @__PURE__ */ jsx2("span", { className: "font-semibold text-foreground text-sm", children: label }),
|
|
143
|
+
summary ? /* @__PURE__ */ jsx2("span", { className: "text-[11px] text-muted-foreground", children: summary }) : null,
|
|
144
|
+
collapsed && collapsedPreview ? /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate text-[11px] text-foreground/70", children: collapsedPreview }) : null,
|
|
145
|
+
/* @__PURE__ */ jsxs2("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
|
|
146
|
+
badges,
|
|
147
|
+
isStreaming ? /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-1 rounded-full border border-[var(--border-accent)] bg-[var(--accent-surface-soft)] px-2 py-px text-[10px] font-semibold uppercase text-[var(--accent-text)]", children: [
|
|
148
|
+
/* @__PURE__ */ jsx2(Loader2, { className: "h-2.5 w-2.5 animate-spin" }),
|
|
149
|
+
"Running"
|
|
150
|
+
] }) : /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-1 rounded-full border border-border px-2 py-px text-[10px] font-semibold uppercase text-muted-foreground", children: [
|
|
151
|
+
/* @__PURE__ */ jsx2(Sparkles, { className: "h-2.5 w-2.5" }),
|
|
152
|
+
"Done"
|
|
153
|
+
] }),
|
|
154
|
+
collapsed ? /* @__PURE__ */ jsx2(ChevronRight2, { className: "h-3.5 w-3.5 text-muted-foreground" }) : /* @__PURE__ */ jsx2(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
155
|
+
] })
|
|
156
|
+
] })
|
|
157
|
+
}
|
|
158
|
+
) }),
|
|
159
|
+
headerActions ? /* @__PURE__ */ jsx2("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1", children: headerActions }) : null
|
|
160
|
+
] }),
|
|
161
|
+
collapsed && collapsedPreview ? /* @__PURE__ */ jsx2("div", { className: "line-clamp-2 px-4 pb-4 text-sm leading-6 text-muted-foreground", children: collapsedPreview }) : null,
|
|
162
|
+
/* @__PURE__ */ jsx2(Collapsible2.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx2("div", { className: "border-t border-[var(--border-subtle)] px-4 pb-4 pt-3", children }) })
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
) });
|
|
166
|
+
}
|
|
167
|
+
|
|
111
168
|
// src/run/run-group.tsx
|
|
112
169
|
import { memo as memo2, useMemo } from "react";
|
|
113
|
-
import * as Collapsible2 from "@radix-ui/react-collapsible";
|
|
114
170
|
import {
|
|
115
171
|
Bot,
|
|
116
|
-
Loader2,
|
|
117
|
-
ChevronDown,
|
|
118
|
-
ChevronRight as ChevronRight2,
|
|
172
|
+
Loader2 as Loader22,
|
|
119
173
|
Terminal,
|
|
120
174
|
FileEdit,
|
|
121
175
|
FileSearch,
|
|
@@ -123,10 +177,9 @@ import {
|
|
|
123
177
|
PencilLine,
|
|
124
178
|
Globe,
|
|
125
179
|
ClipboardList,
|
|
126
|
-
Settings
|
|
127
|
-
Sparkles
|
|
180
|
+
Settings
|
|
128
181
|
} from "lucide-react";
|
|
129
|
-
import { jsx as
|
|
182
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
130
183
|
var DEFAULT_BRANDING = {
|
|
131
184
|
label: "Agent",
|
|
132
185
|
accentClass: "text-primary",
|
|
@@ -142,15 +195,15 @@ function AssistantShell({
|
|
|
142
195
|
isStreaming,
|
|
143
196
|
children
|
|
144
197
|
}) {
|
|
145
|
-
return /* @__PURE__ */
|
|
146
|
-
/* @__PURE__ */
|
|
147
|
-
/* @__PURE__ */
|
|
148
|
-
isStreaming ? /* @__PURE__ */
|
|
149
|
-
/* @__PURE__ */
|
|
198
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [
|
|
199
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 px-1 font-medium text-muted-foreground text-xs", children: [
|
|
200
|
+
/* @__PURE__ */ jsx3("span", { children: branding.label }),
|
|
201
|
+
isStreaming ? /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
202
|
+
/* @__PURE__ */ jsx3(Loader22, { className: "h-3 w-3 animate-spin" }),
|
|
150
203
|
"Thinking"
|
|
151
204
|
] }) : null
|
|
152
205
|
] }),
|
|
153
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ jsx3("div", { className: ASSISTANT_SHELL, children })
|
|
154
207
|
] });
|
|
155
208
|
}
|
|
156
209
|
var CATEGORY_ICON_MAP = {
|
|
@@ -242,14 +295,14 @@ function CategoryBadges({ categories }) {
|
|
|
242
295
|
[categories]
|
|
243
296
|
);
|
|
244
297
|
if (sorted.length === 0) return null;
|
|
245
|
-
return /* @__PURE__ */
|
|
298
|
+
return /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-1", children: sorted.map((cat) => {
|
|
246
299
|
const Icon = CATEGORY_ICON_MAP[cat] ?? Settings;
|
|
247
|
-
return /* @__PURE__ */
|
|
300
|
+
return /* @__PURE__ */ jsx3(
|
|
248
301
|
"span",
|
|
249
302
|
{
|
|
250
303
|
title: cat,
|
|
251
304
|
className: "flex h-5 w-5 items-center justify-center rounded border border-border text-muted-foreground",
|
|
252
|
-
children: /* @__PURE__ */
|
|
305
|
+
children: /* @__PURE__ */ jsx3(Icon, { className: "h-3 w-3" })
|
|
253
306
|
},
|
|
254
307
|
cat
|
|
255
308
|
);
|
|
@@ -310,10 +363,10 @@ var RunGroup = memo2(
|
|
|
310
363
|
if (!isStreaming) {
|
|
311
364
|
return null;
|
|
312
365
|
}
|
|
313
|
-
return /* @__PURE__ */
|
|
314
|
-
/* @__PURE__ */
|
|
315
|
-
/* @__PURE__ */
|
|
316
|
-
/* @__PURE__ */
|
|
366
|
+
return /* @__PURE__ */ jsx3(AssistantShell, { branding, isStreaming: true, children: /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-2 px-0.5 py-0.5 text-sm text-[var(--text-muted)]", children: /* @__PURE__ */ jsxs3("span", { className: "flex gap-[5px]", children: [
|
|
367
|
+
/* @__PURE__ */ jsx3("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "0ms" } }),
|
|
368
|
+
/* @__PURE__ */ jsx3("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "150ms" } }),
|
|
369
|
+
/* @__PURE__ */ jsx3("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "300ms" } })
|
|
317
370
|
] }) }) });
|
|
318
371
|
}
|
|
319
372
|
const showTraceChrome = allParts.some(({ part }) => {
|
|
@@ -326,32 +379,32 @@ var RunGroup = memo2(
|
|
|
326
379
|
return false;
|
|
327
380
|
});
|
|
328
381
|
if (!showTraceChrome) {
|
|
329
|
-
return /* @__PURE__ */
|
|
382
|
+
return /* @__PURE__ */ jsx3(AssistantShell, { branding, isStreaming, children: allParts.map(({ part, msgId, index }) => {
|
|
330
383
|
const key = `${msgId}-${index}`;
|
|
331
384
|
if (part.type === "tool" && isOpenUITool(part)) {
|
|
332
385
|
const toolPart = part;
|
|
333
386
|
const schema = extractOpenUISchema(toolPart.state.output);
|
|
334
387
|
const summary = getOpenUISummary(toolPart.state.output);
|
|
335
388
|
if (toolPart.state.status === "completed" && schema) {
|
|
336
|
-
return /* @__PURE__ */
|
|
389
|
+
return /* @__PURE__ */ jsxs3(
|
|
337
390
|
"div",
|
|
338
391
|
{
|
|
339
392
|
className: "overflow-hidden rounded-[22px] border border-[var(--border-subtle)] bg-[var(--bg-root)]",
|
|
340
393
|
children: [
|
|
341
|
-
summary ? /* @__PURE__ */
|
|
342
|
-
/* @__PURE__ */
|
|
394
|
+
summary ? /* @__PURE__ */ jsx3("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3 text-sm leading-6 text-foreground", children: summary }) : null,
|
|
395
|
+
/* @__PURE__ */ jsx3("div", { className: "p-4", children: /* @__PURE__ */ jsx3(OpenUIArtifactRenderer, { schema }) })
|
|
343
396
|
]
|
|
344
397
|
},
|
|
345
398
|
key
|
|
346
399
|
);
|
|
347
400
|
}
|
|
348
401
|
if (toolPart.state.status === "running") {
|
|
349
|
-
return /* @__PURE__ */
|
|
402
|
+
return /* @__PURE__ */ jsxs3(
|
|
350
403
|
"div",
|
|
351
404
|
{
|
|
352
405
|
className: "flex items-center gap-2 rounded-[18px] border border-[var(--border-subtle)] bg-[var(--bg-root)] px-4 py-3 text-sm text-muted-foreground",
|
|
353
406
|
children: [
|
|
354
|
-
/* @__PURE__ */
|
|
407
|
+
/* @__PURE__ */ jsx3(Loader22, { className: "h-4 w-4 animate-spin text-primary" }),
|
|
355
408
|
"Building view\u2026"
|
|
356
409
|
]
|
|
357
410
|
},
|
|
@@ -360,102 +413,85 @@ var RunGroup = memo2(
|
|
|
360
413
|
}
|
|
361
414
|
}
|
|
362
415
|
if (part.type === "text" && !part.synthetic && part.text.trim()) {
|
|
363
|
-
return /* @__PURE__ */
|
|
416
|
+
return /* @__PURE__ */ jsx3("div", { className: "px-0.5", children: /* @__PURE__ */ jsx3(Markdown, { className: "tangle-prose text-[15px] leading-7 text-[var(--text-primary)]", children: part.text }) }, key);
|
|
364
417
|
}
|
|
365
418
|
return null;
|
|
366
419
|
}) });
|
|
367
420
|
}
|
|
368
|
-
return /* @__PURE__ */
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if (part.type === "tool") {
|
|
405
|
-
if (isOpenUITool(part)) {
|
|
406
|
-
const toolPart = part;
|
|
407
|
-
const schema = extractOpenUISchema(toolPart.state.output);
|
|
408
|
-
const summary = getOpenUISummary(toolPart.state.output);
|
|
409
|
-
if (toolPart.state.status === "completed" && schema) {
|
|
410
|
-
node = /* @__PURE__ */ jsxs2("div", { className: "overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]", children: [
|
|
411
|
-
summary ? /* @__PURE__ */ jsxs2("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3", children: [
|
|
412
|
-
/* @__PURE__ */ jsx2("div", { className: "text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "View" }),
|
|
413
|
-
/* @__PURE__ */ jsx2("div", { className: "mt-1 text-sm leading-6 text-foreground", children: summary })
|
|
414
|
-
] }) : null,
|
|
415
|
-
/* @__PURE__ */ jsx2("div", { className: "p-4", children: /* @__PURE__ */ jsx2(OpenUIArtifactRenderer, { schema }) })
|
|
416
|
-
] });
|
|
417
|
-
} else if (toolPart.state.status === "running") {
|
|
418
|
-
node = /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 rounded-[20px] border border-[var(--border-subtle)] bg-[var(--bg-card)] px-4 py-3 text-sm text-muted-foreground", children: [
|
|
419
|
-
/* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin text-primary" }),
|
|
420
|
-
"Building view\u2026"
|
|
421
|
-
] });
|
|
421
|
+
return /* @__PURE__ */ jsx3(
|
|
422
|
+
AssistantRunShell,
|
|
423
|
+
{
|
|
424
|
+
label: branding.label,
|
|
425
|
+
summary: renderSummary(run) || void 0,
|
|
426
|
+
collapsedPreview: run.summaryText ?? void 0,
|
|
427
|
+
badges: /* @__PURE__ */ jsx3(CategoryBadges, { categories: stats.toolCategories }),
|
|
428
|
+
isStreaming,
|
|
429
|
+
collapsed,
|
|
430
|
+
onToggle,
|
|
431
|
+
headerActions,
|
|
432
|
+
children: allParts.map(({ part, msgId, index }, partIndex) => {
|
|
433
|
+
const key = `${msgId}-${index}`;
|
|
434
|
+
const prev = allParts[partIndex - 1]?.part;
|
|
435
|
+
const connectedTool = part.type === "tool" && prev?.type === "tool" && !isOpenUITool(part) && !isOpenUITool(prev);
|
|
436
|
+
const gapClass = partIndex === 0 ? "" : connectedTool ? "mt-px" : "mt-3";
|
|
437
|
+
let node = null;
|
|
438
|
+
if (part.type === "tool") {
|
|
439
|
+
if (isOpenUITool(part)) {
|
|
440
|
+
const toolPart = part;
|
|
441
|
+
const schema = extractOpenUISchema(toolPart.state.output);
|
|
442
|
+
const summary = getOpenUISummary(toolPart.state.output);
|
|
443
|
+
if (toolPart.state.status === "completed" && schema) {
|
|
444
|
+
node = /* @__PURE__ */ jsxs3("div", { className: "overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]", children: [
|
|
445
|
+
summary ? /* @__PURE__ */ jsxs3("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3", children: [
|
|
446
|
+
/* @__PURE__ */ jsx3("div", { className: "text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "View" }),
|
|
447
|
+
/* @__PURE__ */ jsx3("div", { className: "mt-1 text-sm leading-6 text-foreground", children: summary })
|
|
448
|
+
] }) : null,
|
|
449
|
+
/* @__PURE__ */ jsx3("div", { className: "p-4", children: /* @__PURE__ */ jsx3(OpenUIArtifactRenderer, { schema }) })
|
|
450
|
+
] });
|
|
451
|
+
} else if (toolPart.state.status === "running") {
|
|
452
|
+
node = /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 rounded-[20px] border border-[var(--border-subtle)] bg-[var(--bg-card)] px-4 py-3 text-sm text-muted-foreground", children: [
|
|
453
|
+
/* @__PURE__ */ jsx3(Loader22, { className: "h-4 w-4 animate-spin text-primary" }),
|
|
454
|
+
"Building view\u2026"
|
|
455
|
+
] });
|
|
456
|
+
}
|
|
422
457
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
458
|
+
if (node === null) {
|
|
459
|
+
node = /* @__PURE__ */ jsx3(
|
|
460
|
+
InlineToolItem,
|
|
461
|
+
{
|
|
462
|
+
part,
|
|
463
|
+
renderToolDetail,
|
|
464
|
+
groupPosition: getToolGroupPosition(partIndex, allParts),
|
|
465
|
+
actions: renderToolActions?.(part, {
|
|
466
|
+
run,
|
|
467
|
+
messageId: msgId,
|
|
468
|
+
partIndex: index
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
} else if (part.type === "reasoning") {
|
|
474
|
+
node = /* @__PURE__ */ jsx3(
|
|
475
|
+
InlineThinkingItem,
|
|
427
476
|
{
|
|
428
477
|
part,
|
|
429
|
-
|
|
430
|
-
groupPosition: getToolGroupPosition(partIndex, allParts),
|
|
431
|
-
actions: renderToolActions?.(part, {
|
|
432
|
-
run,
|
|
433
|
-
messageId: msgId,
|
|
434
|
-
partIndex: index
|
|
435
|
-
})
|
|
478
|
+
defaultOpen: isStreaming
|
|
436
479
|
}
|
|
437
480
|
);
|
|
481
|
+
} else if (part.type === "text" && !part.synthetic && part.text.trim()) {
|
|
482
|
+
node = /* @__PURE__ */ jsx3("div", { className: "px-1 py-1", children: /* @__PURE__ */ jsx3(Markdown, { className: "tangle-prose text-[15px] leading-7", children: part.text }) });
|
|
438
483
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
defaultOpen: isStreaming
|
|
445
|
-
}
|
|
446
|
-
);
|
|
447
|
-
} else if (part.type === "text" && !part.synthetic && part.text.trim()) {
|
|
448
|
-
node = /* @__PURE__ */ jsx2("div", { className: "px-1 py-1", children: /* @__PURE__ */ jsx2(Markdown, { className: "tangle-prose text-[15px] leading-7", children: part.text }) });
|
|
449
|
-
}
|
|
450
|
-
if (!node) return null;
|
|
451
|
-
return /* @__PURE__ */ jsx2("div", { className: gapClass, children: node }, key);
|
|
452
|
-
}) }) })
|
|
453
|
-
] }) });
|
|
484
|
+
if (!node) return null;
|
|
485
|
+
return /* @__PURE__ */ jsx3("div", { className: gapClass, children: node }, key);
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
);
|
|
454
489
|
}
|
|
455
490
|
);
|
|
456
491
|
RunGroup.displayName = "RunGroup";
|
|
457
492
|
|
|
458
493
|
export {
|
|
459
494
|
InlineThinkingItem,
|
|
495
|
+
AssistantRunShell,
|
|
460
496
|
RunGroup
|
|
461
497
|
};
|
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
useRunGroups
|
|
5
5
|
} from "./chunk-AZWDI2JG.js";
|
|
6
6
|
import {
|
|
7
|
+
AssistantRunShell,
|
|
7
8
|
InlineThinkingItem,
|
|
8
9
|
RunGroup
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-C3BIVG72.js";
|
|
10
11
|
import {
|
|
11
12
|
ToolCallGroup,
|
|
12
13
|
ToolCallStep
|
|
@@ -98,6 +99,7 @@ var MessageList = memo2(
|
|
|
98
99
|
MessageList.displayName = "MessageList";
|
|
99
100
|
|
|
100
101
|
// src/chat/agent-timeline.tsx
|
|
102
|
+
import { useState as useState2 } from "react";
|
|
101
103
|
import {
|
|
102
104
|
AlertTriangle,
|
|
103
105
|
CheckCircle2,
|
|
@@ -130,6 +132,48 @@ function ThinkingIndicator({ className }) {
|
|
|
130
132
|
|
|
131
133
|
// src/chat/agent-timeline.tsx
|
|
132
134
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
135
|
+
function foldToolRuns(items) {
|
|
136
|
+
const nodes = [];
|
|
137
|
+
let run = [];
|
|
138
|
+
const flush = () => {
|
|
139
|
+
if (run.length === 0) return;
|
|
140
|
+
if (run.length === 1) {
|
|
141
|
+
nodes.push(run[0]);
|
|
142
|
+
} else {
|
|
143
|
+
nodes.push({ id: `tool-run-${run[0].id}`, kind: "tool_run", items: run });
|
|
144
|
+
}
|
|
145
|
+
run = [];
|
|
146
|
+
};
|
|
147
|
+
for (const item of items) {
|
|
148
|
+
if (item.kind === "tool" || item.kind === "tool_group") {
|
|
149
|
+
run.push(item);
|
|
150
|
+
} else {
|
|
151
|
+
flush();
|
|
152
|
+
nodes.push(item);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
flush();
|
|
156
|
+
return nodes;
|
|
157
|
+
}
|
|
158
|
+
function countTools(group) {
|
|
159
|
+
return group.items.reduce(
|
|
160
|
+
(n, item) => n + (item.kind === "tool_group" ? item.calls.length : 1),
|
|
161
|
+
0
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function ToolCallRow({ call }) {
|
|
165
|
+
return /* @__PURE__ */ jsx4(
|
|
166
|
+
ToolCallStep,
|
|
167
|
+
{
|
|
168
|
+
type: call.type,
|
|
169
|
+
label: call.label,
|
|
170
|
+
status: call.status,
|
|
171
|
+
detail: call.detail,
|
|
172
|
+
output: call.output,
|
|
173
|
+
duration: call.duration
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
}
|
|
133
177
|
var TONE_STYLES = {
|
|
134
178
|
default: {
|
|
135
179
|
dot: "bg-[var(--border-hover)]",
|
|
@@ -233,72 +277,76 @@ function AgentTimeline({
|
|
|
233
277
|
items,
|
|
234
278
|
isThinking,
|
|
235
279
|
emptyState,
|
|
236
|
-
className
|
|
280
|
+
className,
|
|
281
|
+
collapsibleToolRuns = true,
|
|
282
|
+
defaultToolRunsOpen = true
|
|
237
283
|
}) {
|
|
284
|
+
const [collapsedRuns, setCollapsedRuns] = useState2({});
|
|
285
|
+
const toggleRun = (id) => setCollapsedRuns((prev) => ({
|
|
286
|
+
...prev,
|
|
287
|
+
[id]: prev[id] === void 0 ? defaultToolRunsOpen : !prev[id]
|
|
288
|
+
}));
|
|
238
289
|
if (items.length === 0 && !isThinking) {
|
|
239
290
|
return emptyState ? /* @__PURE__ */ jsx4("div", { className: cn("flex h-full items-center justify-center p-4", className), children: emptyState }) : null;
|
|
240
291
|
}
|
|
241
292
|
const renderedItems = isThinking ? [...items, { id: "__thinking__", kind: "custom", content: /* @__PURE__ */ jsx4(ThinkingIndicator, {}) }] : items;
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (item.kind === "message") {
|
|
250
|
-
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--brand-glow)]", children: /* @__PURE__ */ jsx4(AssistantMessage, { item }) }, item.id);
|
|
293
|
+
const nodes = collapsibleToolRuns ? foldToolRuns(renderedItems) : renderedItems;
|
|
294
|
+
const timelineNodes = nodes.filter(
|
|
295
|
+
(node) => !(node.kind === "message" && node.role === "user")
|
|
296
|
+
);
|
|
297
|
+
return /* @__PURE__ */ jsx4("div", { className: cn("mx-auto w-full max-w-5xl px-4 py-4", className), children: nodes.map((node) => {
|
|
298
|
+
if (node.kind === "message" && node.role === "user") {
|
|
299
|
+
return /* @__PURE__ */ jsx4(UserMessage2, { item: node }, node.id);
|
|
251
300
|
}
|
|
252
|
-
|
|
301
|
+
const isLast = timelineNodes.indexOf(node) === timelineNodes.length - 1;
|
|
302
|
+
if (node.kind === "tool_run") {
|
|
303
|
+
const collapsed = collapsedRuns[node.id] ?? !defaultToolRunsOpen;
|
|
304
|
+
const total = countTools(node);
|
|
253
305
|
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: /* @__PURE__ */ jsx4(
|
|
254
|
-
|
|
306
|
+
AssistantRunShell,
|
|
255
307
|
{
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
308
|
+
label: "Tools",
|
|
309
|
+
summary: `${total} ${total === 1 ? "tool" : "tools"}`,
|
|
310
|
+
collapsed,
|
|
311
|
+
onToggle: () => toggleRun(node.id),
|
|
312
|
+
children: /* @__PURE__ */ jsx4("div", { className: "space-y-px", children: node.items.map(
|
|
313
|
+
(item) => item.kind === "tool_group" ? /* @__PURE__ */ jsx4(ToolCallGroup, { title: item.title, children: item.calls.map((call) => /* @__PURE__ */ jsx4(ToolCallRow, { call }, call.id)) }, item.id) : /* @__PURE__ */ jsx4(ToolCallRow, { call: item.call }, item.id)
|
|
314
|
+
) })
|
|
262
315
|
}
|
|
263
|
-
) },
|
|
316
|
+
) }, node.id);
|
|
264
317
|
}
|
|
265
|
-
if (
|
|
266
|
-
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
output: call.output,
|
|
274
|
-
duration: call.duration
|
|
275
|
-
},
|
|
276
|
-
call.id
|
|
277
|
-
)) }) }, item.id);
|
|
318
|
+
if (node.kind === "message") {
|
|
319
|
+
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--brand-glow)]", children: /* @__PURE__ */ jsx4(AssistantMessage, { item: node }) }, node.id);
|
|
320
|
+
}
|
|
321
|
+
if (node.kind === "tool") {
|
|
322
|
+
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: /* @__PURE__ */ jsx4(ToolCallRow, { call: node.call }) }, node.id);
|
|
323
|
+
}
|
|
324
|
+
if (node.kind === "tool_group") {
|
|
325
|
+
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: /* @__PURE__ */ jsx4(ToolCallGroup, { title: node.title, children: node.calls.map((call) => /* @__PURE__ */ jsx4(ToolCallRow, { call }, call.id)) }) }, node.id);
|
|
278
326
|
}
|
|
279
|
-
if (
|
|
327
|
+
if (node.kind === "status") {
|
|
280
328
|
return /* @__PURE__ */ jsx4(
|
|
281
329
|
AgentTimelineRow,
|
|
282
330
|
{
|
|
283
331
|
isLast,
|
|
284
|
-
accentClassName: TONE_STYLES[
|
|
285
|
-
children: /* @__PURE__ */ jsx4(StatusCard, { item })
|
|
332
|
+
accentClassName: TONE_STYLES[node.tone ?? "default"].dot,
|
|
333
|
+
children: /* @__PURE__ */ jsx4(StatusCard, { item: node })
|
|
286
334
|
},
|
|
287
|
-
|
|
335
|
+
node.id
|
|
288
336
|
);
|
|
289
337
|
}
|
|
290
|
-
if (
|
|
338
|
+
if (node.kind === "artifact") {
|
|
291
339
|
return /* @__PURE__ */ jsx4(
|
|
292
340
|
AgentTimelineRow,
|
|
293
341
|
{
|
|
294
342
|
isLast,
|
|
295
|
-
accentClassName: TONE_STYLES[
|
|
296
|
-
children: /* @__PURE__ */ jsx4(ArtifactCard, { item })
|
|
343
|
+
accentClassName: TONE_STYLES[node.tone ?? "default"].dot,
|
|
344
|
+
children: /* @__PURE__ */ jsx4(ArtifactCard, { item: node })
|
|
297
345
|
},
|
|
298
|
-
|
|
346
|
+
node.id
|
|
299
347
|
);
|
|
300
348
|
}
|
|
301
|
-
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children:
|
|
349
|
+
return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: node.content }, node.id);
|
|
302
350
|
}) });
|
|
303
351
|
}
|
|
304
352
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, Card, CardConte
|
|
|
3
3
|
export { Logo, LogoProps, TangleKnot } from '@tangle-network/brand';
|
|
4
4
|
export { A as ArtifactPane, a as ArtifactPaneProps } from './artifact-pane-DvJyPWV4.js';
|
|
5
5
|
export { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem, ChatContainer, ChatContainerProps, ChatMessage, ChatMessageProps, MessageList, MessageListProps, MessageRole, ThinkingIndicator, ThinkingIndicatorProps, UserMessage, UserMessageProps } from './chat.js';
|
|
6
|
-
export { ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from './run.js';
|
|
6
|
+
export { AssistantRunShell, AssistantRunShellProps, ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from './run.js';
|
|
7
7
|
export { F as FeedSegment, T as ToolCallData, a as ToolCallFeed, b as ToolCallFeedProps, c as ToolCallStatus, d as ToolCallType, p as parseToolEvent } from './tool-call-feed-D9iofJgW.js';
|
|
8
8
|
export { OpenUIAction, OpenUIActionsNode, OpenUIArtifactRenderer, OpenUIArtifactRendererProps, OpenUIBadgeNode, OpenUICardNode, OpenUICodeNode, OpenUIComponentNode, OpenUIGridNode, OpenUIHeadingNode, OpenUIKeyValueNode, OpenUIMarkdownNode, OpenUIPrimitive, OpenUISeparatorNode, OpenUIStackNode, OpenUIStatNode, OpenUITableNode, OpenUITextNode } from './openui.js';
|
|
9
9
|
export { FileArtifactPane, FileArtifactPaneProps, FileFormat, FileNode, FilePreview, FilePreviewProps, FileTabData, FileTabs, FileTabsProps, FileTree, FileTreeProps, FileTreeVisibilityOptions, RichFileTree, RichFileTreeGitEntry, RichFileTreeGitStatus, RichFileTreeProps, RichFileTreeThemeVars, detectFileFormat, fileExtension, filterFileTree, getCodeLanguage, getFormatLabel, getSyntaxLanguage } from './files.js';
|
package/dist/index.js
CHANGED
|
@@ -137,21 +137,22 @@ import {
|
|
|
137
137
|
MessageList,
|
|
138
138
|
ThinkingIndicator,
|
|
139
139
|
UserMessage
|
|
140
|
-
} from "./chunk-
|
|
140
|
+
} from "./chunk-QUAU6ZNC.js";
|
|
141
141
|
import {
|
|
142
142
|
useAutoScroll,
|
|
143
143
|
useRunCollapseState,
|
|
144
144
|
useRunGroups
|
|
145
145
|
} from "./chunk-AZWDI2JG.js";
|
|
146
|
-
import "./chunk-
|
|
146
|
+
import "./chunk-LQS34IGP.js";
|
|
147
147
|
import {
|
|
148
148
|
ToolCallFeed,
|
|
149
149
|
parseToolEvent
|
|
150
150
|
} from "./chunk-IWQZXL6A.js";
|
|
151
151
|
import {
|
|
152
|
+
AssistantRunShell,
|
|
152
153
|
InlineThinkingItem,
|
|
153
154
|
RunGroup
|
|
154
|
-
} from "./chunk-
|
|
155
|
+
} from "./chunk-C3BIVG72.js";
|
|
155
156
|
import {
|
|
156
157
|
ExpandedToolDetail,
|
|
157
158
|
InlineToolItem,
|
|
@@ -338,6 +339,7 @@ function RedactedDocument({
|
|
|
338
339
|
export {
|
|
339
340
|
AgentTimeline,
|
|
340
341
|
ArtifactPane,
|
|
342
|
+
AssistantRunShell,
|
|
341
343
|
AuthHeader,
|
|
342
344
|
Avatar,
|
|
343
345
|
AvatarFallback,
|
package/dist/run.d.ts
CHANGED
|
@@ -28,6 +28,34 @@ interface RunGroupProps {
|
|
|
28
28
|
*/
|
|
29
29
|
declare const RunGroup: React.MemoExoticComponent<({ run, partMap, collapsed, onToggle, branding, renderToolDetail, headerActions, renderToolActions, }: RunGroupProps) => react_jsx_runtime.JSX.Element | null>;
|
|
30
30
|
|
|
31
|
+
interface AssistantRunShellProps {
|
|
32
|
+
/** Header label, e.g. the agent name or "Tools". */
|
|
33
|
+
label: string;
|
|
34
|
+
/** Terse stat line beside the label, e.g. "3 tools, 2s thinking". */
|
|
35
|
+
summary?: string;
|
|
36
|
+
/** One-line preview shown next to the label AND below the header when collapsed. */
|
|
37
|
+
collapsedPreview?: string;
|
|
38
|
+
/** Small trailing glyphs before the status pill (e.g. category badges). */
|
|
39
|
+
badges?: ReactNode;
|
|
40
|
+
/** Drives the status pill and header spinner. */
|
|
41
|
+
isStreaming?: boolean;
|
|
42
|
+
collapsed: boolean;
|
|
43
|
+
onToggle: () => void;
|
|
44
|
+
/** Actions rendered outside the collapse trigger, right of the header. */
|
|
45
|
+
headerActions?: ReactNode;
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
className?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The collapsible "assistant run" container shared by `RunGroup` (session-model
|
|
51
|
+
* driven) and `AgentTimeline` (declarative item list). Owns the header
|
|
52
|
+
* (label · summary · badges · status pill · chevron), the collapsed preview, and
|
|
53
|
+
* the Radix collapse — so both transcripts fold agent activity the same way and
|
|
54
|
+
* there is one implementation of a run, not two. It renders only chrome; callers
|
|
55
|
+
* pass the run body (tool rows, reasoning, text) as `children`.
|
|
56
|
+
*/
|
|
57
|
+
declare function AssistantRunShell({ label, summary, collapsedPreview, badges, isStreaming, collapsed, onToggle, headerActions, children, className, }: AssistantRunShellProps): react_jsx_runtime.JSX.Element;
|
|
58
|
+
|
|
31
59
|
interface InlineToolItemProps {
|
|
32
60
|
part: ToolPart;
|
|
33
61
|
renderToolDetail?: CustomToolRenderer;
|
|
@@ -70,4 +98,4 @@ declare function LiveDuration({ startTime }: {
|
|
|
70
98
|
startTime: number;
|
|
71
99
|
}): react_jsx_runtime.JSX.Element;
|
|
72
100
|
|
|
73
|
-
export { ExpandedToolDetail, type ExpandedToolDetailProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps };
|
|
101
|
+
export { AssistantRunShell, type AssistantRunShellProps, ExpandedToolDetail, type ExpandedToolDetailProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps };
|
package/dist/run.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-LQS34IGP.js";
|
|
2
2
|
import {
|
|
3
3
|
ToolCallFeed,
|
|
4
4
|
parseToolEvent
|
|
5
5
|
} from "./chunk-IWQZXL6A.js";
|
|
6
6
|
import {
|
|
7
|
+
AssistantRunShell,
|
|
7
8
|
InlineThinkingItem,
|
|
8
9
|
RunGroup
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-C3BIVG72.js";
|
|
10
11
|
import {
|
|
11
12
|
ExpandedToolDetail,
|
|
12
13
|
InlineToolItem,
|
|
@@ -22,6 +23,7 @@ import "./chunk-FJBTCTZM.js";
|
|
|
22
23
|
import "./chunk-WUQDUBJG.js";
|
|
23
24
|
import "./chunk-RQHJBTEU.js";
|
|
24
25
|
export {
|
|
26
|
+
AssistantRunShell,
|
|
25
27
|
ExpandedToolDetail,
|
|
26
28
|
InlineThinkingItem,
|
|
27
29
|
InlineToolItem,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"react": "^18 || ^19",
|
|
133
133
|
"react-dom": "^18 || ^19",
|
|
134
134
|
"react-router": "^7",
|
|
135
|
-
"@tangle-network/brand": "^0.8.
|
|
135
|
+
"@tangle-network/brand": "^0.8.1"
|
|
136
136
|
},
|
|
137
137
|
"peerDependenciesMeta": {
|
|
138
138
|
"@nanostores/react": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type KeyboardEvent, type ReactNode } from "react";
|
|
1
|
+
import { type KeyboardEvent, type ReactNode, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
AlertTriangle,
|
|
4
4
|
CheckCircle2,
|
|
@@ -12,6 +12,7 @@ import { Markdown } from "../markdown/markdown";
|
|
|
12
12
|
import { ThinkingIndicator } from "./thinking-indicator";
|
|
13
13
|
import { type ToolCallData } from "../run/tool-call-feed";
|
|
14
14
|
import { ToolCallGroup, ToolCallStep } from "../run/tool-call-step";
|
|
15
|
+
import { AssistantRunShell } from "../run/assistant-run-shell";
|
|
15
16
|
|
|
16
17
|
export type AgentTimelineTone = "default" | "info" | "success" | "warning" | "error";
|
|
17
18
|
|
|
@@ -78,6 +79,71 @@ export interface AgentTimelineProps {
|
|
|
78
79
|
isThinking?: boolean;
|
|
79
80
|
emptyState?: ReactNode;
|
|
80
81
|
className?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Fold consecutive tool / tool-group items into one collapsible run shell
|
|
84
|
+
* (the same `AssistantRunShell` `RunGroup` uses), so a burst of tool activity
|
|
85
|
+
* reads as a single toggleable step instead of a long ladder of rows.
|
|
86
|
+
* Default true; pass false for the flat one-row-per-tool timeline.
|
|
87
|
+
*/
|
|
88
|
+
collapsibleToolRuns?: boolean;
|
|
89
|
+
/** Start collapsed tool runs open (true) or collapsed (false). Default open. */
|
|
90
|
+
defaultToolRunsOpen?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A run of consecutive tool / tool-group items folded into one shell. */
|
|
94
|
+
interface ToolRunGroup {
|
|
95
|
+
id: string;
|
|
96
|
+
kind: "tool_run";
|
|
97
|
+
items: (AgentTimelineToolItem | AgentTimelineToolGroupItem)[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
type TimelineNode = AgentTimelineItem | ToolRunGroup;
|
|
101
|
+
|
|
102
|
+
function foldToolRuns(items: AgentTimelineItem[]): TimelineNode[] {
|
|
103
|
+
const nodes: TimelineNode[] = [];
|
|
104
|
+
let run: (AgentTimelineToolItem | AgentTimelineToolGroupItem)[] = [];
|
|
105
|
+
|
|
106
|
+
const flush = () => {
|
|
107
|
+
if (run.length === 0) return;
|
|
108
|
+
// A single tool stays a plain row; two or more fold into a collapsible run.
|
|
109
|
+
if (run.length === 1) {
|
|
110
|
+
nodes.push(run[0]);
|
|
111
|
+
} else {
|
|
112
|
+
nodes.push({ id: `tool-run-${run[0].id}`, kind: "tool_run", items: run });
|
|
113
|
+
}
|
|
114
|
+
run = [];
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
for (const item of items) {
|
|
118
|
+
if (item.kind === "tool" || item.kind === "tool_group") {
|
|
119
|
+
run.push(item);
|
|
120
|
+
} else {
|
|
121
|
+
flush();
|
|
122
|
+
nodes.push(item);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
flush();
|
|
126
|
+
return nodes;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function countTools(group: ToolRunGroup): number {
|
|
130
|
+
return group.items.reduce(
|
|
131
|
+
(n, item) => n + (item.kind === "tool_group" ? item.calls.length : 1),
|
|
132
|
+
0,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function ToolCallRow({ call }: { call: ToolCallData }) {
|
|
137
|
+
return (
|
|
138
|
+
<ToolCallStep
|
|
139
|
+
type={call.type}
|
|
140
|
+
label={call.label}
|
|
141
|
+
status={call.status}
|
|
142
|
+
detail={call.detail}
|
|
143
|
+
output={call.output}
|
|
144
|
+
duration={call.duration}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
81
147
|
}
|
|
82
148
|
|
|
83
149
|
const TONE_STYLES: Record<AgentTimelineTone, { dot: string; card: string; text: string; icon: typeof Info }> = {
|
|
@@ -253,7 +319,17 @@ export function AgentTimeline({
|
|
|
253
319
|
isThinking,
|
|
254
320
|
emptyState,
|
|
255
321
|
className,
|
|
322
|
+
collapsibleToolRuns = true,
|
|
323
|
+
defaultToolRunsOpen = true,
|
|
256
324
|
}: AgentTimelineProps) {
|
|
325
|
+
// Collapse state for folded tool runs, keyed by run id. Absent → default.
|
|
326
|
+
const [collapsedRuns, setCollapsedRuns] = useState<Record<string, boolean>>({});
|
|
327
|
+
const toggleRun = (id: string) =>
|
|
328
|
+
setCollapsedRuns((prev) => ({
|
|
329
|
+
...prev,
|
|
330
|
+
[id]: prev[id] === undefined ? defaultToolRunsOpen : !prev[id],
|
|
331
|
+
}));
|
|
332
|
+
|
|
257
333
|
if (items.length === 0 && !isThinking) {
|
|
258
334
|
return emptyState ? (
|
|
259
335
|
<div className={cn("flex h-full items-center justify-center p-4", className)}>
|
|
@@ -266,92 +342,110 @@ export function AgentTimeline({
|
|
|
266
342
|
? [...items, { id: "__thinking__", kind: "custom", content: <ThinkingIndicator /> }]
|
|
267
343
|
: items;
|
|
268
344
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
345
|
+
const nodes: TimelineNode[] = collapsibleToolRuns
|
|
346
|
+
? foldToolRuns(renderedItems)
|
|
347
|
+
: renderedItems;
|
|
348
|
+
|
|
349
|
+
// Non-user rows participate in the connector spine.
|
|
350
|
+
const timelineNodes = nodes.filter(
|
|
351
|
+
(node) => !(node.kind === "message" && node.role === "user"),
|
|
352
|
+
);
|
|
272
353
|
|
|
273
354
|
return (
|
|
274
355
|
<div className={cn("mx-auto w-full max-w-5xl px-4 py-4", className)}>
|
|
275
|
-
{
|
|
356
|
+
{nodes.map((node) => {
|
|
276
357
|
// User messages: right-aligned bubble, no connector
|
|
277
|
-
if (
|
|
278
|
-
return <UserMessage key={
|
|
358
|
+
if (node.kind === "message" && node.role === "user") {
|
|
359
|
+
return <UserMessage key={node.id} item={node} />;
|
|
279
360
|
}
|
|
280
361
|
|
|
281
|
-
const
|
|
282
|
-
|
|
362
|
+
const isLast = timelineNodes.indexOf(node) === timelineNodes.length - 1;
|
|
363
|
+
|
|
364
|
+
if (node.kind === "tool_run") {
|
|
365
|
+
const collapsed = collapsedRuns[node.id] ?? !defaultToolRunsOpen;
|
|
366
|
+
const total = countTools(node);
|
|
367
|
+
return (
|
|
368
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
369
|
+
<AssistantRunShell
|
|
370
|
+
label="Tools"
|
|
371
|
+
summary={`${total} ${total === 1 ? "tool" : "tools"}`}
|
|
372
|
+
collapsed={collapsed}
|
|
373
|
+
onToggle={() => toggleRun(node.id)}
|
|
374
|
+
>
|
|
375
|
+
<div className="space-y-px">
|
|
376
|
+
{node.items.map((item) =>
|
|
377
|
+
item.kind === "tool_group" ? (
|
|
378
|
+
<ToolCallGroup key={item.id} title={item.title}>
|
|
379
|
+
{item.calls.map((call) => (
|
|
380
|
+
<ToolCallRow key={call.id} call={call} />
|
|
381
|
+
))}
|
|
382
|
+
</ToolCallGroup>
|
|
383
|
+
) : (
|
|
384
|
+
<ToolCallRow key={item.id} call={item.call} />
|
|
385
|
+
),
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
</AssistantRunShell>
|
|
389
|
+
</AgentTimelineRow>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
283
392
|
|
|
284
|
-
if (
|
|
393
|
+
if (node.kind === "message") {
|
|
285
394
|
return (
|
|
286
|
-
<AgentTimelineRow key={
|
|
287
|
-
<AssistantMessage item={
|
|
395
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--brand-glow)]">
|
|
396
|
+
<AssistantMessage item={node} />
|
|
288
397
|
</AgentTimelineRow>
|
|
289
398
|
);
|
|
290
399
|
}
|
|
291
400
|
|
|
292
|
-
if (
|
|
401
|
+
if (node.kind === "tool") {
|
|
293
402
|
return (
|
|
294
|
-
<AgentTimelineRow key={
|
|
295
|
-
<
|
|
296
|
-
type={item.call.type}
|
|
297
|
-
label={item.call.label}
|
|
298
|
-
status={item.call.status}
|
|
299
|
-
detail={item.call.detail}
|
|
300
|
-
output={item.call.output}
|
|
301
|
-
duration={item.call.duration}
|
|
302
|
-
/>
|
|
403
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
404
|
+
<ToolCallRow call={node.call} />
|
|
303
405
|
</AgentTimelineRow>
|
|
304
406
|
);
|
|
305
407
|
}
|
|
306
408
|
|
|
307
|
-
if (
|
|
409
|
+
if (node.kind === "tool_group") {
|
|
308
410
|
return (
|
|
309
|
-
<AgentTimelineRow key={
|
|
310
|
-
<ToolCallGroup title={
|
|
311
|
-
{
|
|
312
|
-
<
|
|
313
|
-
key={call.id}
|
|
314
|
-
type={call.type}
|
|
315
|
-
label={call.label}
|
|
316
|
-
status={call.status}
|
|
317
|
-
detail={call.detail}
|
|
318
|
-
output={call.output}
|
|
319
|
-
duration={call.duration}
|
|
320
|
-
/>
|
|
411
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
412
|
+
<ToolCallGroup title={node.title}>
|
|
413
|
+
{node.calls.map((call) => (
|
|
414
|
+
<ToolCallRow key={call.id} call={call} />
|
|
321
415
|
))}
|
|
322
416
|
</ToolCallGroup>
|
|
323
417
|
</AgentTimelineRow>
|
|
324
418
|
);
|
|
325
419
|
}
|
|
326
420
|
|
|
327
|
-
if (
|
|
421
|
+
if (node.kind === "status") {
|
|
328
422
|
return (
|
|
329
423
|
<AgentTimelineRow
|
|
330
|
-
key={
|
|
424
|
+
key={node.id}
|
|
331
425
|
isLast={isLast}
|
|
332
|
-
accentClassName={TONE_STYLES[
|
|
426
|
+
accentClassName={TONE_STYLES[node.tone ?? "default"].dot}
|
|
333
427
|
>
|
|
334
|
-
<StatusCard item={
|
|
428
|
+
<StatusCard item={node} />
|
|
335
429
|
</AgentTimelineRow>
|
|
336
430
|
);
|
|
337
431
|
}
|
|
338
432
|
|
|
339
|
-
if (
|
|
433
|
+
if (node.kind === "artifact") {
|
|
340
434
|
return (
|
|
341
435
|
<AgentTimelineRow
|
|
342
|
-
key={
|
|
436
|
+
key={node.id}
|
|
343
437
|
isLast={isLast}
|
|
344
|
-
accentClassName={TONE_STYLES[
|
|
438
|
+
accentClassName={TONE_STYLES[node.tone ?? "default"].dot}
|
|
345
439
|
>
|
|
346
|
-
<ArtifactCard item={
|
|
440
|
+
<ArtifactCard item={node} />
|
|
347
441
|
</AgentTimelineRow>
|
|
348
442
|
);
|
|
349
443
|
}
|
|
350
444
|
|
|
351
445
|
// custom
|
|
352
446
|
return (
|
|
353
|
-
<AgentTimelineRow key={
|
|
354
|
-
{(
|
|
447
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
448
|
+
{(node as AgentTimelineCustomItem).content}
|
|
355
449
|
</AgentTimelineRow>
|
|
356
450
|
);
|
|
357
451
|
})}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
3
|
+
import { ChevronDown, ChevronRight, Loader2, Sparkles } from "lucide-react";
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
export interface AssistantRunShellProps {
|
|
7
|
+
/** Header label, e.g. the agent name or "Tools". */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Terse stat line beside the label, e.g. "3 tools, 2s thinking". */
|
|
10
|
+
summary?: string;
|
|
11
|
+
/** One-line preview shown next to the label AND below the header when collapsed. */
|
|
12
|
+
collapsedPreview?: string;
|
|
13
|
+
/** Small trailing glyphs before the status pill (e.g. category badges). */
|
|
14
|
+
badges?: ReactNode;
|
|
15
|
+
/** Drives the status pill and header spinner. */
|
|
16
|
+
isStreaming?: boolean;
|
|
17
|
+
collapsed: boolean;
|
|
18
|
+
onToggle: () => void;
|
|
19
|
+
/** Actions rendered outside the collapse trigger, right of the header. */
|
|
20
|
+
headerActions?: ReactNode;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The collapsible "assistant run" container shared by `RunGroup` (session-model
|
|
27
|
+
* driven) and `AgentTimeline` (declarative item list). Owns the header
|
|
28
|
+
* (label · summary · badges · status pill · chevron), the collapsed preview, and
|
|
29
|
+
* the Radix collapse — so both transcripts fold agent activity the same way and
|
|
30
|
+
* there is one implementation of a run, not two. It renders only chrome; callers
|
|
31
|
+
* pass the run body (tool rows, reasoning, text) as `children`.
|
|
32
|
+
*/
|
|
33
|
+
export function AssistantRunShell({
|
|
34
|
+
label,
|
|
35
|
+
summary,
|
|
36
|
+
collapsedPreview,
|
|
37
|
+
badges,
|
|
38
|
+
isStreaming,
|
|
39
|
+
collapsed,
|
|
40
|
+
onToggle,
|
|
41
|
+
headerActions,
|
|
42
|
+
children,
|
|
43
|
+
className,
|
|
44
|
+
}: AssistantRunShellProps) {
|
|
45
|
+
return (
|
|
46
|
+
<Collapsible.Root open={!collapsed} onOpenChange={() => onToggle()}>
|
|
47
|
+
<div
|
|
48
|
+
className={cn(
|
|
49
|
+
"rounded-[28px] border border-[var(--border-subtle)] bg-[var(--bg-card)] shadow-none",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
>
|
|
53
|
+
<div className="flex items-start gap-3 px-3 py-2.5">
|
|
54
|
+
<Collapsible.Trigger asChild>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
className="w-full rounded-[20px] bg-transparent px-0 py-0 text-left transition-colors hover:bg-transparent"
|
|
58
|
+
>
|
|
59
|
+
<div className="flex items-center gap-2">
|
|
60
|
+
<span className="font-semibold text-foreground text-sm">{label}</span>
|
|
61
|
+
|
|
62
|
+
{summary ? (
|
|
63
|
+
<span className="text-[11px] text-muted-foreground">{summary}</span>
|
|
64
|
+
) : null}
|
|
65
|
+
{collapsed && collapsedPreview ? (
|
|
66
|
+
<span className="min-w-0 truncate text-[11px] text-foreground/70">
|
|
67
|
+
{collapsedPreview}
|
|
68
|
+
</span>
|
|
69
|
+
) : null}
|
|
70
|
+
|
|
71
|
+
<div className="ml-auto flex shrink-0 items-center gap-1.5">
|
|
72
|
+
{badges}
|
|
73
|
+
|
|
74
|
+
{isStreaming ? (
|
|
75
|
+
<span className="inline-flex items-center gap-1 rounded-full border border-[var(--border-accent)] bg-[var(--accent-surface-soft)] px-2 py-px text-[10px] font-semibold uppercase text-[var(--accent-text)]">
|
|
76
|
+
<Loader2 className="h-2.5 w-2.5 animate-spin" />
|
|
77
|
+
Running
|
|
78
|
+
</span>
|
|
79
|
+
) : (
|
|
80
|
+
<span className="inline-flex items-center gap-1 rounded-full border border-border px-2 py-px text-[10px] font-semibold uppercase text-muted-foreground">
|
|
81
|
+
<Sparkles className="h-2.5 w-2.5" />
|
|
82
|
+
Done
|
|
83
|
+
</span>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{collapsed ? (
|
|
87
|
+
<ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />
|
|
88
|
+
) : (
|
|
89
|
+
<ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</button>
|
|
94
|
+
</Collapsible.Trigger>
|
|
95
|
+
|
|
96
|
+
{headerActions ? (
|
|
97
|
+
<div className="flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1">
|
|
98
|
+
{headerActions}
|
|
99
|
+
</div>
|
|
100
|
+
) : null}
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{collapsed && collapsedPreview ? (
|
|
104
|
+
<div className="line-clamp-2 px-4 pb-4 text-sm leading-6 text-muted-foreground">
|
|
105
|
+
{collapsedPreview}
|
|
106
|
+
</div>
|
|
107
|
+
) : null}
|
|
108
|
+
|
|
109
|
+
<Collapsible.Content className="overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp">
|
|
110
|
+
<div className="border-t border-[var(--border-subtle)] px-4 pb-4 pt-3">{children}</div>
|
|
111
|
+
</Collapsible.Content>
|
|
112
|
+
</div>
|
|
113
|
+
</Collapsible.Root>
|
|
114
|
+
);
|
|
115
|
+
}
|
package/src/run/index.ts
CHANGED
package/src/run/run-group.tsx
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { memo, useMemo, type ComponentType, type ReactNode } from "react";
|
|
2
|
-
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
3
2
|
import {
|
|
4
3
|
Bot,
|
|
5
4
|
Loader2,
|
|
6
|
-
ChevronDown,
|
|
7
|
-
ChevronRight,
|
|
8
5
|
Terminal,
|
|
9
6
|
FileEdit,
|
|
10
7
|
FileSearch,
|
|
@@ -13,10 +10,8 @@ import {
|
|
|
13
10
|
Globe,
|
|
14
11
|
ClipboardList,
|
|
15
12
|
Settings,
|
|
16
|
-
Sparkles,
|
|
17
13
|
type LucideProps,
|
|
18
14
|
} from "lucide-react";
|
|
19
|
-
import { cn } from "../lib/utils";
|
|
20
15
|
import { formatDuration } from "../utils/format";
|
|
21
16
|
import type { Run, ToolCategory } from "../types/run";
|
|
22
17
|
import type { SessionPart, ToolPart, ReasoningPart } from "../types/parts";
|
|
@@ -24,6 +19,7 @@ import type { AgentBranding } from "../types/branding";
|
|
|
24
19
|
import type { CustomToolRenderer } from "../types/tool-display";
|
|
25
20
|
import { InlineToolItem } from "./inline-tool-item";
|
|
26
21
|
import { InlineThinkingItem } from "./inline-thinking-item";
|
|
22
|
+
import { AssistantRunShell } from "./assistant-run-shell";
|
|
27
23
|
import { Markdown } from "../markdown/markdown";
|
|
28
24
|
import {
|
|
29
25
|
OpenUIArtifactRenderer,
|
|
@@ -393,73 +389,16 @@ export const RunGroup = memo(
|
|
|
393
389
|
}
|
|
394
390
|
|
|
395
391
|
return (
|
|
396
|
-
<
|
|
397
|
-
|
|
398
|
-
{
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
>
|
|
407
|
-
<div className="flex items-center gap-2">
|
|
408
|
-
<span className={cn("font-semibold text-sm", branding.textClass)}>
|
|
409
|
-
{branding.label}
|
|
410
|
-
</span>
|
|
411
|
-
|
|
412
|
-
{renderSummary(run) ? (
|
|
413
|
-
<span className="text-[11px] text-muted-foreground">{renderSummary(run)}</span>
|
|
414
|
-
) : null}
|
|
415
|
-
{collapsed && run.summaryText ? (
|
|
416
|
-
<span className="min-w-0 truncate text-[11px] text-foreground/70">
|
|
417
|
-
{run.summaryText}
|
|
418
|
-
</span>
|
|
419
|
-
) : null}
|
|
420
|
-
|
|
421
|
-
<div className="ml-auto flex shrink-0 items-center gap-1.5">
|
|
422
|
-
<CategoryBadges categories={stats.toolCategories} />
|
|
423
|
-
|
|
424
|
-
{isStreaming ? (
|
|
425
|
-
<span className="inline-flex items-center gap-1 rounded-full border border-[var(--border-accent)] bg-[var(--accent-surface-soft)] px-2 py-px text-[10px] font-semibold uppercase text-[var(--accent-text)]">
|
|
426
|
-
<Loader2 className="h-2.5 w-2.5 animate-spin" />
|
|
427
|
-
Running
|
|
428
|
-
</span>
|
|
429
|
-
) : (
|
|
430
|
-
<span className="inline-flex items-center gap-1 rounded-full border border-border px-2 py-px text-[10px] font-semibold uppercase text-muted-foreground">
|
|
431
|
-
<Sparkles className="h-2.5 w-2.5" />
|
|
432
|
-
Done
|
|
433
|
-
</span>
|
|
434
|
-
)}
|
|
435
|
-
|
|
436
|
-
{!collapsed ? (
|
|
437
|
-
<ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
|
|
438
|
-
) : (
|
|
439
|
-
<ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />
|
|
440
|
-
)}
|
|
441
|
-
</div>
|
|
442
|
-
</div>
|
|
443
|
-
</button>
|
|
444
|
-
</Collapsible.Trigger>
|
|
445
|
-
|
|
446
|
-
{headerActions ? (
|
|
447
|
-
<div className="flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1">
|
|
448
|
-
{headerActions}
|
|
449
|
-
</div>
|
|
450
|
-
) : null}
|
|
451
|
-
</div>
|
|
452
|
-
|
|
453
|
-
{/* Summary text when collapsed */}
|
|
454
|
-
{collapsed && run.summaryText && (
|
|
455
|
-
<div className="px-4 pb-4 text-sm leading-6 text-muted-foreground line-clamp-2">
|
|
456
|
-
{run.summaryText}
|
|
457
|
-
</div>
|
|
458
|
-
)}
|
|
459
|
-
|
|
460
|
-
{/* Expanded content */}
|
|
461
|
-
<Collapsible.Content className="overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp">
|
|
462
|
-
<div className={cn("border-t border-[var(--border-subtle)] px-4 pb-4 pt-3")}>
|
|
392
|
+
<AssistantRunShell
|
|
393
|
+
label={branding.label}
|
|
394
|
+
summary={renderSummary(run) || undefined}
|
|
395
|
+
collapsedPreview={run.summaryText ?? undefined}
|
|
396
|
+
badges={<CategoryBadges categories={stats.toolCategories} />}
|
|
397
|
+
isStreaming={isStreaming}
|
|
398
|
+
collapsed={collapsed}
|
|
399
|
+
onToggle={onToggle}
|
|
400
|
+
headerActions={headerActions}
|
|
401
|
+
>
|
|
463
402
|
{allParts.map(({ part, msgId, index }, partIndex) => {
|
|
464
403
|
const key = `${msgId}-${index}`;
|
|
465
404
|
|
|
@@ -549,10 +488,7 @@ export const RunGroup = memo(
|
|
|
549
488
|
</div>
|
|
550
489
|
);
|
|
551
490
|
})}
|
|
552
|
-
|
|
553
|
-
</Collapsible.Content>
|
|
554
|
-
</div>
|
|
555
|
-
</Collapsible.Root>
|
|
491
|
+
</AssistantRunShell>
|
|
556
492
|
);
|
|
557
493
|
},
|
|
558
494
|
);
|
|
File without changes
|