@tangle-network/ui 8.0.0 → 9.0.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 +18 -0
- package/dist/chat.js +2 -2
- package/dist/{chunk-QIRVZMQY.js → chunk-2TRMNB6L.js} +49 -43
- package/dist/{chunk-UOLL2YHG.js → chunk-LHOGIUGY.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/run.js +1 -1
- package/package.json +2 -2
- package/src/run/run-group.tsx +131 -134
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 9.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 87252cf: Flip the transcript convergence: RunGroup adopts AgentTimeline's look, not the reverse. 8.1 made AgentTimeline fold tool activity into RunGroup's single filled box (`AssistantRunShell`); that boxed all steps into one card and lost the timeline's separated, distinct rows. Reverted.
|
|
8
|
+
|
|
9
|
+
- **`RunGroup`** now renders as separated steps on a timeline spine (connector line + accent dots, one row per tool/reasoning/text part) with a quiet collapsible header (chevron · label · summary · status) — no wrapping `bg-card` box, and consecutive tools are no longer joined into one block. It reads like `AgentTimeline`, plus collapse.
|
|
10
|
+
- **`AgentTimeline`** is restored to its prior flat, separated rendering (no tool-run folding). The `collapsibleToolRuns` / `defaultToolRunsOpen` props added in 8.1 are removed.
|
|
11
|
+
- **`AssistantRunShell`** (added in 8.1) is removed — the boxed shell is gone.
|
|
12
|
+
|
|
13
|
+
BREAKING: `AssistantRunShell` / `AssistantRunShellProps` are no longer exported, and `AgentTimeline` drops the `collapsibleToolRuns` / `defaultToolRunsOpen` props.
|
|
14
|
+
|
|
15
|
+
## 8.1.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 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.
|
|
20
|
+
|
|
3
21
|
## 8.0.0
|
|
4
22
|
|
|
5
23
|
### Major Changes
|
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-LHOGIUGY.js";
|
|
9
9
|
import "./chunk-AZWDI2JG.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-2TRMNB6L.js";
|
|
11
11
|
import "./chunk-RKQDBRTC.js";
|
|
12
12
|
import "./chunk-ULDNFLIM.js";
|
|
13
13
|
import "./chunk-AAUNOHVL.js";
|
|
@@ -127,6 +127,27 @@ import {
|
|
|
127
127
|
Sparkles
|
|
128
128
|
} from "lucide-react";
|
|
129
129
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
130
|
+
function SpineRow({
|
|
131
|
+
accentClassName,
|
|
132
|
+
isLast,
|
|
133
|
+
children
|
|
134
|
+
}) {
|
|
135
|
+
return /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-[1.25rem_minmax(0,1fr)] gap-x-3", children: [
|
|
136
|
+
/* @__PURE__ */ jsxs2("div", { className: "relative flex justify-center", children: [
|
|
137
|
+
!isLast && /* @__PURE__ */ jsx2("span", { className: "absolute top-3.5 bottom-[-0.75rem] left-1/2 w-px -translate-x-1/2 bg-[var(--border-subtle)]" }),
|
|
138
|
+
/* @__PURE__ */ jsx2(
|
|
139
|
+
"span",
|
|
140
|
+
{
|
|
141
|
+
className: cn(
|
|
142
|
+
"relative mt-1.5 h-[var(--timeline-dot-size,0.5rem)] w-[var(--timeline-dot-size,0.5rem)] rounded-full ring-4 ring-[var(--bg-root)]",
|
|
143
|
+
accentClassName
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
] }),
|
|
148
|
+
/* @__PURE__ */ jsx2("div", { className: "min-w-0 pb-3", children })
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
130
151
|
var DEFAULT_BRANDING = {
|
|
131
152
|
label: "Agent",
|
|
132
153
|
accentClass: "text-primary",
|
|
@@ -268,16 +289,6 @@ function renderSummary(run) {
|
|
|
268
289
|
}
|
|
269
290
|
return parts.join(", ");
|
|
270
291
|
}
|
|
271
|
-
function getToolGroupPosition(currentIndex, parts) {
|
|
272
|
-
const previous = parts[currentIndex - 1]?.part;
|
|
273
|
-
const next = parts[currentIndex + 1]?.part;
|
|
274
|
-
const previousIsTool = previous?.type === "tool";
|
|
275
|
-
const nextIsTool = next?.type === "tool";
|
|
276
|
-
if (previousIsTool && nextIsTool) return "middle";
|
|
277
|
-
if (previousIsTool) return "last";
|
|
278
|
-
if (nextIsTool) return "first";
|
|
279
|
-
return "single";
|
|
280
|
-
}
|
|
281
292
|
var RunGroup = memo2(
|
|
282
293
|
({
|
|
283
294
|
run,
|
|
@@ -365,20 +376,28 @@ var RunGroup = memo2(
|
|
|
365
376
|
return null;
|
|
366
377
|
}) });
|
|
367
378
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
379
|
+
const rows = allParts.filter(({ part }) => {
|
|
380
|
+
if (part.type === "tool" || part.type === "reasoning") return true;
|
|
381
|
+
return part.type === "text" && !part.synthetic && part.text.trim().length > 0;
|
|
382
|
+
});
|
|
383
|
+
const dotAccent = (part) => {
|
|
384
|
+
if (part.type === "reasoning") return "bg-[var(--brand-glow)]";
|
|
385
|
+
if (part.type === "text") return "bg-primary";
|
|
386
|
+
return "bg-[var(--border-hover)]";
|
|
387
|
+
};
|
|
388
|
+
return /* @__PURE__ */ jsx2(Collapsible2.Root, { open: !collapsed, onOpenChange: () => onToggle(), children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-1", children: [
|
|
389
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-start gap-2", children: [
|
|
390
|
+
/* @__PURE__ */ jsx2(Collapsible2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
|
|
371
391
|
"button",
|
|
372
392
|
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
393
|
+
type: "button",
|
|
394
|
+
className: "group flex min-w-0 flex-1 items-center gap-2 rounded-md px-1 py-0.5 text-left transition-colors hover:bg-[var(--surface-container-high)]/40",
|
|
395
|
+
children: [
|
|
396
|
+
collapsed ? /* @__PURE__ */ jsx2(ChevronRight2, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ jsx2(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
378
397
|
/* @__PURE__ */ jsx2("span", { className: cn("font-semibold text-sm", branding.textClass), children: branding.label }),
|
|
379
398
|
renderSummary(run) ? /* @__PURE__ */ jsx2("span", { className: "text-[11px] text-muted-foreground", children: renderSummary(run) }) : null,
|
|
380
399
|
collapsed && run.summaryText ? /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate text-[11px] text-foreground/70", children: run.summaryText }) : null,
|
|
381
|
-
/* @__PURE__ */ jsxs2("
|
|
400
|
+
/* @__PURE__ */ jsxs2("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
|
|
382
401
|
/* @__PURE__ */ jsx2(CategoryBadges, { categories: stats.toolCategories }),
|
|
383
402
|
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: [
|
|
384
403
|
/* @__PURE__ */ jsx2(Loader2, { className: "h-2.5 w-2.5 animate-spin" }),
|
|
@@ -386,20 +405,17 @@ var RunGroup = memo2(
|
|
|
386
405
|
] }) : /* @__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: [
|
|
387
406
|
/* @__PURE__ */ jsx2(Sparkles, { className: "h-2.5 w-2.5" }),
|
|
388
407
|
"Done"
|
|
389
|
-
] })
|
|
390
|
-
!collapsed ? /* @__PURE__ */ jsx2(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" }) : /* @__PURE__ */ jsx2(ChevronRight2, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
408
|
+
] })
|
|
391
409
|
] })
|
|
392
|
-
]
|
|
410
|
+
]
|
|
393
411
|
}
|
|
394
412
|
) }),
|
|
395
413
|
headerActions ? /* @__PURE__ */ jsx2("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1", children: headerActions }) : null
|
|
396
414
|
] }),
|
|
397
|
-
collapsed && run.summaryText
|
|
398
|
-
/* @__PURE__ */ jsx2(Collapsible2.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx2("div", { className:
|
|
415
|
+
collapsed && run.summaryText ? /* @__PURE__ */ jsx2("div", { className: "line-clamp-2 pl-6 text-sm leading-6 text-muted-foreground", children: run.summaryText }) : null,
|
|
416
|
+
/* @__PURE__ */ jsx2(Collapsible2.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx2("div", { className: "pt-1.5", children: rows.map(({ part, msgId, index }, rowIndex) => {
|
|
399
417
|
const key = `${msgId}-${index}`;
|
|
400
|
-
const
|
|
401
|
-
const connectedTool = part.type === "tool" && prev?.type === "tool" && !isOpenUITool(part) && !isOpenUITool(prev);
|
|
402
|
-
const gapClass = partIndex === 0 ? "" : connectedTool ? "mt-px" : "mt-3";
|
|
418
|
+
const isLast = rowIndex === rows.length - 1;
|
|
403
419
|
let node = null;
|
|
404
420
|
if (part.type === "tool") {
|
|
405
421
|
if (isOpenUITool(part)) {
|
|
@@ -407,15 +423,12 @@ var RunGroup = memo2(
|
|
|
407
423
|
const schema = extractOpenUISchema(toolPart.state.output);
|
|
408
424
|
const summary = getOpenUISummary(toolPart.state.output);
|
|
409
425
|
if (toolPart.state.status === "completed" && schema) {
|
|
410
|
-
node = /* @__PURE__ */ jsxs2("div", { className: "overflow-hidden rounded-[
|
|
411
|
-
summary ? /* @__PURE__ */
|
|
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,
|
|
426
|
+
node = /* @__PURE__ */ jsxs2("div", { className: "overflow-hidden rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--bg-card)]", children: [
|
|
427
|
+
summary ? /* @__PURE__ */ jsx2("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3 text-sm leading-6 text-foreground", children: summary }) : null,
|
|
415
428
|
/* @__PURE__ */ jsx2("div", { className: "p-4", children: /* @__PURE__ */ jsx2(OpenUIArtifactRenderer, { schema }) })
|
|
416
429
|
] });
|
|
417
430
|
} else if (toolPart.state.status === "running") {
|
|
418
|
-
node = /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 rounded-[
|
|
431
|
+
node = /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--bg-card)] px-4 py-3 text-sm text-muted-foreground", children: [
|
|
419
432
|
/* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin text-primary" }),
|
|
420
433
|
"Building view\u2026"
|
|
421
434
|
] });
|
|
@@ -427,7 +440,6 @@ var RunGroup = memo2(
|
|
|
427
440
|
{
|
|
428
441
|
part,
|
|
429
442
|
renderToolDetail,
|
|
430
|
-
groupPosition: getToolGroupPosition(partIndex, allParts),
|
|
431
443
|
actions: renderToolActions?.(part, {
|
|
432
444
|
run,
|
|
433
445
|
messageId: msgId,
|
|
@@ -437,18 +449,12 @@ var RunGroup = memo2(
|
|
|
437
449
|
);
|
|
438
450
|
}
|
|
439
451
|
} else if (part.type === "reasoning") {
|
|
440
|
-
node = /* @__PURE__ */ jsx2(
|
|
441
|
-
InlineThinkingItem,
|
|
442
|
-
{
|
|
443
|
-
part,
|
|
444
|
-
defaultOpen: isStreaming
|
|
445
|
-
}
|
|
446
|
-
);
|
|
452
|
+
node = /* @__PURE__ */ jsx2(InlineThinkingItem, { part, defaultOpen: isStreaming });
|
|
447
453
|
} else if (part.type === "text" && !part.synthetic && part.text.trim()) {
|
|
448
|
-
node = /* @__PURE__ */ jsx2("div", { className: "px-1 py-
|
|
454
|
+
node = /* @__PURE__ */ jsx2("div", { className: "px-1 py-0.5", children: /* @__PURE__ */ jsx2(Markdown, { className: "tangle-prose text-[15px] leading-7", children: part.text }) });
|
|
449
455
|
}
|
|
450
456
|
if (!node) return null;
|
|
451
|
-
return /* @__PURE__ */ jsx2(
|
|
457
|
+
return /* @__PURE__ */ jsx2(SpineRow, { accentClassName: dotAccent(part), isLast, children: node }, key);
|
|
452
458
|
}) }) })
|
|
453
459
|
] }) });
|
|
454
460
|
}
|
package/dist/index.js
CHANGED
|
@@ -137,7 +137,7 @@ import {
|
|
|
137
137
|
MessageList,
|
|
138
138
|
ThinkingIndicator,
|
|
139
139
|
UserMessage
|
|
140
|
-
} from "./chunk-
|
|
140
|
+
} from "./chunk-LHOGIUGY.js";
|
|
141
141
|
import {
|
|
142
142
|
useAutoScroll,
|
|
143
143
|
useRunCollapseState,
|
|
@@ -151,7 +151,7 @@ import {
|
|
|
151
151
|
import {
|
|
152
152
|
InlineThinkingItem,
|
|
153
153
|
RunGroup
|
|
154
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-2TRMNB6L.js";
|
|
155
155
|
import {
|
|
156
156
|
ExpandedToolDetail,
|
|
157
157
|
InlineToolItem,
|
package/dist/run.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.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": {
|
package/src/run/run-group.tsx
CHANGED
|
@@ -25,6 +25,38 @@ import type { CustomToolRenderer } from "../types/tool-display";
|
|
|
25
25
|
import { InlineToolItem } from "./inline-tool-item";
|
|
26
26
|
import { InlineThinkingItem } from "./inline-thinking-item";
|
|
27
27
|
import { Markdown } from "../markdown/markdown";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* One row on the run's timeline spine: a connector line + accent dot in a
|
|
31
|
+
* narrow gutter, content to the right. Mirrors AgentTimeline's row so a run
|
|
32
|
+
* reads as separated, distinct steps — not one filled box.
|
|
33
|
+
*/
|
|
34
|
+
function SpineRow({
|
|
35
|
+
accentClassName,
|
|
36
|
+
isLast,
|
|
37
|
+
children,
|
|
38
|
+
}: {
|
|
39
|
+
accentClassName: string;
|
|
40
|
+
isLast: boolean;
|
|
41
|
+
children: ReactNode;
|
|
42
|
+
}) {
|
|
43
|
+
return (
|
|
44
|
+
<div className="grid grid-cols-[1.25rem_minmax(0,1fr)] gap-x-3">
|
|
45
|
+
<div className="relative flex justify-center">
|
|
46
|
+
{!isLast && (
|
|
47
|
+
<span className="absolute top-3.5 bottom-[-0.75rem] left-1/2 w-px -translate-x-1/2 bg-[var(--border-subtle)]" />
|
|
48
|
+
)}
|
|
49
|
+
<span
|
|
50
|
+
className={cn(
|
|
51
|
+
"relative mt-1.5 h-[var(--timeline-dot-size,0.5rem)] w-[var(--timeline-dot-size,0.5rem)] rounded-full ring-4 ring-[var(--bg-root)]",
|
|
52
|
+
accentClassName,
|
|
53
|
+
)}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="min-w-0 pb-3">{children}</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
28
60
|
import {
|
|
29
61
|
OpenUIArtifactRenderer,
|
|
30
62
|
type OpenUIAction,
|
|
@@ -245,21 +277,6 @@ function renderSummary(run: Run) {
|
|
|
245
277
|
return parts.join(", ");
|
|
246
278
|
}
|
|
247
279
|
|
|
248
|
-
function getToolGroupPosition(
|
|
249
|
-
currentIndex: number,
|
|
250
|
-
parts: Array<{ part: SessionPart; msgId: string; index: number }>,
|
|
251
|
-
) {
|
|
252
|
-
const previous = parts[currentIndex - 1]?.part;
|
|
253
|
-
const next = parts[currentIndex + 1]?.part;
|
|
254
|
-
const previousIsTool = previous?.type === "tool";
|
|
255
|
-
const nextIsTool = next?.type === "tool";
|
|
256
|
-
|
|
257
|
-
if (previousIsTool && nextIsTool) return "middle" as const;
|
|
258
|
-
if (previousIsTool) return "last" as const;
|
|
259
|
-
if (nextIsTool) return "first" as const;
|
|
260
|
-
return "single" as const;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
280
|
// ---------------------------------------------------------------------------
|
|
264
281
|
// Component
|
|
265
282
|
// ---------------------------------------------------------------------------
|
|
@@ -392,23 +409,36 @@ export const RunGroup = memo(
|
|
|
392
409
|
);
|
|
393
410
|
}
|
|
394
411
|
|
|
412
|
+
// Renderable rows: skip empty/synthetic text so spine dots map to real steps.
|
|
413
|
+
const rows = allParts.filter(({ part }) => {
|
|
414
|
+
if (part.type === "tool" || part.type === "reasoning") return true;
|
|
415
|
+
return part.type === "text" && !part.synthetic && part.text.trim().length > 0;
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const dotAccent = (part: SessionPart): string => {
|
|
419
|
+
if (part.type === "reasoning") return "bg-[var(--brand-glow)]";
|
|
420
|
+
if (part.type === "text") return "bg-primary";
|
|
421
|
+
return "bg-[var(--border-hover)]";
|
|
422
|
+
};
|
|
423
|
+
|
|
395
424
|
return (
|
|
396
425
|
<Collapsible.Root open={!collapsed} onOpenChange={() => onToggle()}>
|
|
397
|
-
<div className="
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
"w-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
426
|
+
<div className="flex flex-col gap-1">
|
|
427
|
+
{/* Header — a quiet row, not a filled box */}
|
|
428
|
+
<div className="flex items-start gap-2">
|
|
429
|
+
<Collapsible.Trigger asChild>
|
|
430
|
+
<button
|
|
431
|
+
type="button"
|
|
432
|
+
className="group flex min-w-0 flex-1 items-center gap-2 rounded-md px-1 py-0.5 text-left transition-colors hover:bg-[var(--surface-container-high)]/40"
|
|
433
|
+
>
|
|
434
|
+
{collapsed ? (
|
|
435
|
+
<ChevronRight className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
|
436
|
+
) : (
|
|
437
|
+
<ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
|
438
|
+
)}
|
|
408
439
|
<span className={cn("font-semibold text-sm", branding.textClass)}>
|
|
409
440
|
{branding.label}
|
|
410
441
|
</span>
|
|
411
|
-
|
|
412
442
|
{renderSummary(run) ? (
|
|
413
443
|
<span className="text-[11px] text-muted-foreground">{renderSummary(run)}</span>
|
|
414
444
|
) : null}
|
|
@@ -417,10 +447,8 @@ export const RunGroup = memo(
|
|
|
417
447
|
{run.summaryText}
|
|
418
448
|
</span>
|
|
419
449
|
) : null}
|
|
420
|
-
|
|
421
|
-
<div className="ml-auto flex shrink-0 items-center gap-1.5">
|
|
450
|
+
<span className="ml-auto flex shrink-0 items-center gap-1.5">
|
|
422
451
|
<CategoryBadges categories={stats.toolCategories} />
|
|
423
|
-
|
|
424
452
|
{isStreaming ? (
|
|
425
453
|
<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
454
|
<Loader2 className="h-2.5 w-2.5 animate-spin" />
|
|
@@ -432,125 +460,94 @@ export const RunGroup = memo(
|
|
|
432
460
|
Done
|
|
433
461
|
</span>
|
|
434
462
|
)}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
</div>
|
|
463
|
+
</span>
|
|
464
|
+
</button>
|
|
465
|
+
</Collapsible.Trigger>
|
|
466
|
+
{headerActions ? (
|
|
467
|
+
<div className="flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1">
|
|
468
|
+
{headerActions}
|
|
442
469
|
</div>
|
|
443
|
-
|
|
444
|
-
</
|
|
470
|
+
) : null}
|
|
471
|
+
</div>
|
|
445
472
|
|
|
446
|
-
{
|
|
447
|
-
|
|
448
|
-
|
|
473
|
+
{/* Collapsed preview */}
|
|
474
|
+
{collapsed && run.summaryText ? (
|
|
475
|
+
<div className="line-clamp-2 pl-6 text-sm leading-6 text-muted-foreground">
|
|
476
|
+
{run.summaryText}
|
|
449
477
|
</div>
|
|
450
478
|
) : 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
479
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (part.type === "tool") {
|
|
481
|
-
if (isOpenUITool(part as ToolPart)) {
|
|
482
|
-
const toolPart = part as ToolPart;
|
|
483
|
-
const schema = extractOpenUISchema(toolPart.state.output);
|
|
484
|
-
const summary = getOpenUISummary(toolPart.state.output);
|
|
485
|
-
|
|
486
|
-
if (toolPart.state.status === "completed" && schema) {
|
|
487
|
-
node = (
|
|
488
|
-
<div className="overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]">
|
|
489
|
-
{summary ? (
|
|
490
|
-
<div className="border-b border-[var(--border-subtle)] px-4 py-3">
|
|
491
|
-
<div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground">
|
|
492
|
-
View
|
|
480
|
+
{/* Expanded — separated steps on a timeline spine, no wrapping box */}
|
|
481
|
+
<Collapsible.Content className="overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp">
|
|
482
|
+
<div className="pt-1.5">
|
|
483
|
+
{rows.map(({ part, msgId, index }, rowIndex) => {
|
|
484
|
+
const key = `${msgId}-${index}`;
|
|
485
|
+
const isLast = rowIndex === rows.length - 1;
|
|
486
|
+
let node: ReactNode = null;
|
|
487
|
+
|
|
488
|
+
if (part.type === "tool") {
|
|
489
|
+
if (isOpenUITool(part as ToolPart)) {
|
|
490
|
+
const toolPart = part as ToolPart;
|
|
491
|
+
const schema = extractOpenUISchema(toolPart.state.output);
|
|
492
|
+
const summary = getOpenUISummary(toolPart.state.output);
|
|
493
|
+
|
|
494
|
+
if (toolPart.state.status === "completed" && schema) {
|
|
495
|
+
node = (
|
|
496
|
+
<div className="overflow-hidden rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--bg-card)]">
|
|
497
|
+
{summary ? (
|
|
498
|
+
<div className="border-b border-[var(--border-subtle)] px-4 py-3 text-sm leading-6 text-foreground">
|
|
499
|
+
{summary}
|
|
493
500
|
</div>
|
|
494
|
-
|
|
501
|
+
) : null}
|
|
502
|
+
<div className="p-4">
|
|
503
|
+
<OpenUIArtifactRenderer schema={schema} />
|
|
495
504
|
</div>
|
|
496
|
-
) : null}
|
|
497
|
-
<div className="p-4">
|
|
498
|
-
<OpenUIArtifactRenderer schema={schema} />
|
|
499
505
|
</div>
|
|
500
|
-
|
|
501
|
-
)
|
|
502
|
-
|
|
506
|
+
);
|
|
507
|
+
} else if (toolPart.state.status === "running") {
|
|
508
|
+
node = (
|
|
509
|
+
<div className="flex items-center gap-3 rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--bg-card)] px-4 py-3 text-sm text-muted-foreground">
|
|
510
|
+
<Loader2 className="h-4 w-4 animate-spin text-primary" />
|
|
511
|
+
Building view…
|
|
512
|
+
</div>
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (node === null) {
|
|
503
518
|
node = (
|
|
504
|
-
<
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
519
|
+
<InlineToolItem
|
|
520
|
+
part={part as ToolPart}
|
|
521
|
+
renderToolDetail={renderToolDetail}
|
|
522
|
+
actions={renderToolActions?.(part as ToolPart, {
|
|
523
|
+
run,
|
|
524
|
+
messageId: msgId,
|
|
525
|
+
partIndex: index,
|
|
526
|
+
})}
|
|
527
|
+
/>
|
|
508
528
|
);
|
|
509
529
|
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
530
|
+
} else if (part.type === "reasoning") {
|
|
531
|
+
node = (
|
|
532
|
+
<InlineThinkingItem part={part as ReasoningPart} defaultOpen={isStreaming} />
|
|
533
|
+
);
|
|
534
|
+
} else if (part.type === "text" && !part.synthetic && part.text.trim()) {
|
|
513
535
|
node = (
|
|
514
|
-
<
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
groupPosition={getToolGroupPosition(partIndex, allParts)}
|
|
518
|
-
actions={renderToolActions?.(part as ToolPart, {
|
|
519
|
-
run,
|
|
520
|
-
messageId: msgId,
|
|
521
|
-
partIndex: index,
|
|
522
|
-
})}
|
|
523
|
-
/>
|
|
536
|
+
<div className="px-1 py-0.5">
|
|
537
|
+
<Markdown className="tangle-prose text-[15px] leading-7">{part.text}</Markdown>
|
|
538
|
+
</div>
|
|
524
539
|
);
|
|
525
540
|
}
|
|
526
|
-
} else if (part.type === "reasoning") {
|
|
527
|
-
node = (
|
|
528
|
-
<InlineThinkingItem
|
|
529
|
-
part={part as ReasoningPart}
|
|
530
|
-
defaultOpen={isStreaming}
|
|
531
|
-
/>
|
|
532
|
-
);
|
|
533
|
-
} else if (
|
|
534
|
-
part.type === "text" &&
|
|
535
|
-
!part.synthetic &&
|
|
536
|
-
part.text.trim()
|
|
537
|
-
) {
|
|
538
|
-
node = (
|
|
539
|
-
<div className="px-1 py-1">
|
|
540
|
-
<Markdown className="tangle-prose text-[15px] leading-7">{part.text}</Markdown>
|
|
541
|
-
</div>
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
541
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
542
|
+
if (!node) return null;
|
|
543
|
+
return (
|
|
544
|
+
<SpineRow key={key} accentClassName={dotAccent(part)} isLast={isLast}>
|
|
545
|
+
{node}
|
|
546
|
+
</SpineRow>
|
|
547
|
+
);
|
|
548
|
+
})}
|
|
549
|
+
</div>
|
|
550
|
+
</Collapsible.Content>
|
|
554
551
|
</div>
|
|
555
552
|
</Collapsible.Root>
|
|
556
553
|
);
|