@tangle-network/ui 8.1.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 CHANGED
@@ -1,5 +1,17 @@
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
+
3
15
  ## 8.1.0
4
16
 
5
17
  ### Minor Changes
package/dist/chat.d.ts CHANGED
@@ -157,21 +157,12 @@ 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;
169
160
  }
170
161
  /**
171
162
  * AgentTimeline — unified mixed-content timeline for agent-backed sandbox
172
163
  * sessions. Renders messages, tool steps, status cards, and artifact handoffs in
173
164
  * a single execution narrative.
174
165
  */
175
- declare function AgentTimeline({ items, isThinking, emptyState, className, collapsibleToolRuns, defaultToolRunsOpen, }: AgentTimelineProps): react_jsx_runtime.JSX.Element | null;
166
+ declare function AgentTimeline({ items, isThinking, emptyState, className, }: AgentTimelineProps): react_jsx_runtime.JSX.Element | null;
176
167
 
177
168
  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-QUAU6ZNC.js";
8
+ } from "./chunk-LHOGIUGY.js";
9
9
  import "./chunk-AZWDI2JG.js";
10
- import "./chunk-C3BIVG72.js";
10
+ import "./chunk-2TRMNB6L.js";
11
11
  import "./chunk-RKQDBRTC.js";
12
12
  import "./chunk-ULDNFLIM.js";
13
13
  import "./chunk-AAUNOHVL.js";
@@ -108,68 +108,14 @@ 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
-
168
111
  // src/run/run-group.tsx
169
112
  import { memo as memo2, useMemo } from "react";
113
+ import * as Collapsible2 from "@radix-ui/react-collapsible";
170
114
  import {
171
115
  Bot,
172
- Loader2 as Loader22,
116
+ Loader2,
117
+ ChevronDown,
118
+ ChevronRight as ChevronRight2,
173
119
  Terminal,
174
120
  FileEdit,
175
121
  FileSearch,
@@ -177,9 +123,31 @@ import {
177
123
  PencilLine,
178
124
  Globe,
179
125
  ClipboardList,
180
- Settings
126
+ Settings,
127
+ Sparkles
181
128
  } from "lucide-react";
182
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
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
+ }
183
151
  var DEFAULT_BRANDING = {
184
152
  label: "Agent",
185
153
  accentClass: "text-primary",
@@ -195,15 +163,15 @@ function AssistantShell({
195
163
  isStreaming,
196
164
  children
197
165
  }) {
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" }),
166
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-1", children: [
167
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-1 font-medium text-muted-foreground text-xs", children: [
168
+ /* @__PURE__ */ jsx2("span", { children: branding.label }),
169
+ isStreaming ? /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-1.5", children: [
170
+ /* @__PURE__ */ jsx2(Loader2, { className: "h-3 w-3 animate-spin" }),
203
171
  "Thinking"
204
172
  ] }) : null
205
173
  ] }),
206
- /* @__PURE__ */ jsx3("div", { className: ASSISTANT_SHELL, children })
174
+ /* @__PURE__ */ jsx2("div", { className: ASSISTANT_SHELL, children })
207
175
  ] });
208
176
  }
209
177
  var CATEGORY_ICON_MAP = {
@@ -295,14 +263,14 @@ function CategoryBadges({ categories }) {
295
263
  [categories]
296
264
  );
297
265
  if (sorted.length === 0) return null;
298
- return /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-1", children: sorted.map((cat) => {
266
+ return /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-1", children: sorted.map((cat) => {
299
267
  const Icon = CATEGORY_ICON_MAP[cat] ?? Settings;
300
- return /* @__PURE__ */ jsx3(
268
+ return /* @__PURE__ */ jsx2(
301
269
  "span",
302
270
  {
303
271
  title: cat,
304
272
  className: "flex h-5 w-5 items-center justify-center rounded border border-border text-muted-foreground",
305
- children: /* @__PURE__ */ jsx3(Icon, { className: "h-3 w-3" })
273
+ children: /* @__PURE__ */ jsx2(Icon, { className: "h-3 w-3" })
306
274
  },
307
275
  cat
308
276
  );
@@ -321,16 +289,6 @@ function renderSummary(run) {
321
289
  }
322
290
  return parts.join(", ");
323
291
  }
324
- function getToolGroupPosition(currentIndex, parts) {
325
- const previous = parts[currentIndex - 1]?.part;
326
- const next = parts[currentIndex + 1]?.part;
327
- const previousIsTool = previous?.type === "tool";
328
- const nextIsTool = next?.type === "tool";
329
- if (previousIsTool && nextIsTool) return "middle";
330
- if (previousIsTool) return "last";
331
- if (nextIsTool) return "first";
332
- return "single";
333
- }
334
292
  var RunGroup = memo2(
335
293
  ({
336
294
  run,
@@ -363,10 +321,10 @@ var RunGroup = memo2(
363
321
  if (!isStreaming) {
364
322
  return null;
365
323
  }
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" } })
324
+ return /* @__PURE__ */ jsx2(AssistantShell, { branding, isStreaming: true, children: /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-2 px-0.5 py-0.5 text-sm text-[var(--text-muted)]", children: /* @__PURE__ */ jsxs2("span", { className: "flex gap-[5px]", children: [
325
+ /* @__PURE__ */ jsx2("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "0ms" } }),
326
+ /* @__PURE__ */ jsx2("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "150ms" } }),
327
+ /* @__PURE__ */ jsx2("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "300ms" } })
370
328
  ] }) }) });
371
329
  }
372
330
  const showTraceChrome = allParts.some(({ part }) => {
@@ -379,32 +337,32 @@ var RunGroup = memo2(
379
337
  return false;
380
338
  });
381
339
  if (!showTraceChrome) {
382
- return /* @__PURE__ */ jsx3(AssistantShell, { branding, isStreaming, children: allParts.map(({ part, msgId, index }) => {
340
+ return /* @__PURE__ */ jsx2(AssistantShell, { branding, isStreaming, children: allParts.map(({ part, msgId, index }) => {
383
341
  const key = `${msgId}-${index}`;
384
342
  if (part.type === "tool" && isOpenUITool(part)) {
385
343
  const toolPart = part;
386
344
  const schema = extractOpenUISchema(toolPart.state.output);
387
345
  const summary = getOpenUISummary(toolPart.state.output);
388
346
  if (toolPart.state.status === "completed" && schema) {
389
- return /* @__PURE__ */ jsxs3(
347
+ return /* @__PURE__ */ jsxs2(
390
348
  "div",
391
349
  {
392
350
  className: "overflow-hidden rounded-[22px] border border-[var(--border-subtle)] bg-[var(--bg-root)]",
393
351
  children: [
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 }) })
352
+ summary ? /* @__PURE__ */ jsx2("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3 text-sm leading-6 text-foreground", children: summary }) : null,
353
+ /* @__PURE__ */ jsx2("div", { className: "p-4", children: /* @__PURE__ */ jsx2(OpenUIArtifactRenderer, { schema }) })
396
354
  ]
397
355
  },
398
356
  key
399
357
  );
400
358
  }
401
359
  if (toolPart.state.status === "running") {
402
- return /* @__PURE__ */ jsxs3(
360
+ return /* @__PURE__ */ jsxs2(
403
361
  "div",
404
362
  {
405
363
  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",
406
364
  children: [
407
- /* @__PURE__ */ jsx3(Loader22, { className: "h-4 w-4 animate-spin text-primary" }),
365
+ /* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin text-primary" }),
408
366
  "Building view\u2026"
409
367
  ]
410
368
  },
@@ -413,85 +371,97 @@ var RunGroup = memo2(
413
371
  }
414
372
  }
415
373
  if (part.type === "text" && !part.synthetic && part.text.trim()) {
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);
374
+ return /* @__PURE__ */ jsx2("div", { className: "px-0.5", children: /* @__PURE__ */ jsx2(Markdown, { className: "tangle-prose text-[15px] leading-7 text-[var(--text-primary)]", children: part.text }) }, key);
417
375
  }
418
376
  return null;
419
377
  }) });
420
378
  }
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
- }
457
- }
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
- );
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(
391
+ "button",
392
+ {
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" }),
397
+ /* @__PURE__ */ jsx2("span", { className: cn("font-semibold text-sm", branding.textClass), children: branding.label }),
398
+ renderSummary(run) ? /* @__PURE__ */ jsx2("span", { className: "text-[11px] text-muted-foreground", children: renderSummary(run) }) : null,
399
+ collapsed && run.summaryText ? /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate text-[11px] text-foreground/70", children: run.summaryText }) : null,
400
+ /* @__PURE__ */ jsxs2("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
401
+ /* @__PURE__ */ jsx2(CategoryBadges, { categories: stats.toolCategories }),
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: [
403
+ /* @__PURE__ */ jsx2(Loader2, { className: "h-2.5 w-2.5 animate-spin" }),
404
+ "Running"
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: [
406
+ /* @__PURE__ */ jsx2(Sparkles, { className: "h-2.5 w-2.5" }),
407
+ "Done"
408
+ ] })
409
+ ] })
410
+ ]
411
+ }
412
+ ) }),
413
+ headerActions ? /* @__PURE__ */ jsx2("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1", children: headerActions }) : null
414
+ ] }),
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) => {
417
+ const key = `${msgId}-${index}`;
418
+ const isLast = rowIndex === rows.length - 1;
419
+ let node = null;
420
+ if (part.type === "tool") {
421
+ if (isOpenUITool(part)) {
422
+ const toolPart = part;
423
+ const schema = extractOpenUISchema(toolPart.state.output);
424
+ const summary = getOpenUISummary(toolPart.state.output);
425
+ if (toolPart.state.status === "completed" && schema) {
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,
428
+ /* @__PURE__ */ jsx2("div", { className: "p-4", children: /* @__PURE__ */ jsx2(OpenUIArtifactRenderer, { schema }) })
429
+ ] });
430
+ } else if (toolPart.state.status === "running") {
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: [
432
+ /* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin text-primary" }),
433
+ "Building view\u2026"
434
+ ] });
472
435
  }
473
- } else if (part.type === "reasoning") {
474
- node = /* @__PURE__ */ jsx3(
475
- InlineThinkingItem,
436
+ }
437
+ if (node === null) {
438
+ node = /* @__PURE__ */ jsx2(
439
+ InlineToolItem,
476
440
  {
477
441
  part,
478
- defaultOpen: isStreaming
442
+ renderToolDetail,
443
+ actions: renderToolActions?.(part, {
444
+ run,
445
+ messageId: msgId,
446
+ partIndex: index
447
+ })
479
448
  }
480
449
  );
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 }) });
483
450
  }
484
- if (!node) return null;
485
- return /* @__PURE__ */ jsx3("div", { className: gapClass, children: node }, key);
486
- })
487
- }
488
- );
451
+ } else if (part.type === "reasoning") {
452
+ node = /* @__PURE__ */ jsx2(InlineThinkingItem, { part, defaultOpen: isStreaming });
453
+ } else if (part.type === "text" && !part.synthetic && part.text.trim()) {
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 }) });
455
+ }
456
+ if (!node) return null;
457
+ return /* @__PURE__ */ jsx2(SpineRow, { accentClassName: dotAccent(part), isLast, children: node }, key);
458
+ }) }) })
459
+ ] }) });
489
460
  }
490
461
  );
491
462
  RunGroup.displayName = "RunGroup";
492
463
 
493
464
  export {
494
465
  InlineThinkingItem,
495
- AssistantRunShell,
496
466
  RunGroup
497
467
  };
@@ -4,10 +4,9 @@ import {
4
4
  useRunGroups
5
5
  } from "./chunk-AZWDI2JG.js";
6
6
  import {
7
- AssistantRunShell,
8
7
  InlineThinkingItem,
9
8
  RunGroup
10
- } from "./chunk-C3BIVG72.js";
9
+ } from "./chunk-2TRMNB6L.js";
11
10
  import {
12
11
  ToolCallGroup,
13
12
  ToolCallStep
@@ -99,7 +98,6 @@ var MessageList = memo2(
99
98
  MessageList.displayName = "MessageList";
100
99
 
101
100
  // src/chat/agent-timeline.tsx
102
- import { useState as useState2 } from "react";
103
101
  import {
104
102
  AlertTriangle,
105
103
  CheckCircle2,
@@ -132,48 +130,6 @@ function ThinkingIndicator({ className }) {
132
130
 
133
131
  // src/chat/agent-timeline.tsx
134
132
  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
- }
177
133
  var TONE_STYLES = {
178
134
  default: {
179
135
  dot: "bg-[var(--border-hover)]",
@@ -277,76 +233,72 @@ function AgentTimeline({
277
233
  items,
278
234
  isThinking,
279
235
  emptyState,
280
- className,
281
- collapsibleToolRuns = true,
282
- defaultToolRunsOpen = true
236
+ className
283
237
  }) {
284
- const [collapsedRuns, setCollapsedRuns] = useState2({});
285
- const toggleRun = (id) => setCollapsedRuns((prev) => ({
286
- ...prev,
287
- [id]: prev[id] === void 0 ? defaultToolRunsOpen : !prev[id]
288
- }));
289
238
  if (items.length === 0 && !isThinking) {
290
239
  return emptyState ? /* @__PURE__ */ jsx4("div", { className: cn("flex h-full items-center justify-center p-4", className), children: emptyState }) : null;
291
240
  }
292
241
  const renderedItems = isThinking ? [...items, { id: "__thinking__", kind: "custom", content: /* @__PURE__ */ jsx4(ThinkingIndicator, {}) }] : items;
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);
242
+ const timelineItems = renderedItems.filter((item) => !(item.kind === "message" && item.role === "user"));
243
+ return /* @__PURE__ */ jsx4("div", { className: cn("mx-auto w-full max-w-5xl px-4 py-4", className), children: renderedItems.map((item, index) => {
244
+ if (item.kind === "message" && item.role === "user") {
245
+ return /* @__PURE__ */ jsx4(UserMessage2, { item }, item.id);
246
+ }
247
+ const timelineIndex = timelineItems.indexOf(item);
248
+ const isLast = timelineIndex === timelineItems.length - 1;
249
+ if (item.kind === "message") {
250
+ return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--brand-glow)]", children: /* @__PURE__ */ jsx4(AssistantMessage, { item }) }, item.id);
300
251
  }
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);
252
+ if (item.kind === "tool") {
305
253
  return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: /* @__PURE__ */ jsx4(
306
- AssistantRunShell,
254
+ ToolCallStep,
307
255
  {
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
- ) })
256
+ type: item.call.type,
257
+ label: item.call.label,
258
+ status: item.call.status,
259
+ detail: item.call.detail,
260
+ output: item.call.output,
261
+ duration: item.call.duration
315
262
  }
316
- ) }, node.id);
317
- }
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);
263
+ ) }, item.id);
323
264
  }
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);
265
+ if (item.kind === "tool_group") {
266
+ return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: /* @__PURE__ */ jsx4(ToolCallGroup, { title: item.title, children: item.calls.map((call) => /* @__PURE__ */ jsx4(
267
+ ToolCallStep,
268
+ {
269
+ type: call.type,
270
+ label: call.label,
271
+ status: call.status,
272
+ detail: call.detail,
273
+ output: call.output,
274
+ duration: call.duration
275
+ },
276
+ call.id
277
+ )) }) }, item.id);
326
278
  }
327
- if (node.kind === "status") {
279
+ if (item.kind === "status") {
328
280
  return /* @__PURE__ */ jsx4(
329
281
  AgentTimelineRow,
330
282
  {
331
283
  isLast,
332
- accentClassName: TONE_STYLES[node.tone ?? "default"].dot,
333
- children: /* @__PURE__ */ jsx4(StatusCard, { item: node })
284
+ accentClassName: TONE_STYLES[item.tone ?? "default"].dot,
285
+ children: /* @__PURE__ */ jsx4(StatusCard, { item })
334
286
  },
335
- node.id
287
+ item.id
336
288
  );
337
289
  }
338
- if (node.kind === "artifact") {
290
+ if (item.kind === "artifact") {
339
291
  return /* @__PURE__ */ jsx4(
340
292
  AgentTimelineRow,
341
293
  {
342
294
  isLast,
343
- accentClassName: TONE_STYLES[node.tone ?? "default"].dot,
344
- children: /* @__PURE__ */ jsx4(ArtifactCard, { item: node })
295
+ accentClassName: TONE_STYLES[item.tone ?? "default"].dot,
296
+ children: /* @__PURE__ */ jsx4(ArtifactCard, { item })
345
297
  },
346
- node.id
298
+ item.id
347
299
  );
348
300
  }
349
- return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: node.content }, node.id);
301
+ return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: item.content }, item.id);
350
302
  }) });
351
303
  }
352
304
 
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 { AssistantRunShell, AssistantRunShellProps, ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from './run.js';
6
+ export { 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';