@tangle-network/ui 5.0.0 → 5.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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 5.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6db1ce3: Calmer, unified agent transcript. The tool-call rows read as harsh black-and-white outlines on dark surfaces; soften the whole transcript to one calm design language:
8
+
9
+ - `ToolCallStep` (used by `AgentTimeline`): subtle `--border-subtle` row border instead of full-strength `border-border`, a borderless tinted status badge, and a quiet status glyph (green check / red alert) in place of the loud bordered uppercase `SUCCESS`/`ERROR` pill.
10
+ - `InlineToolItem` (used by `RunGroup`): same subtle border, and a blueprint-style accent left-border indent on the expanded detail so expanding reads cleanly.
11
+
12
+ No API or capability changes — purely the visual treatment, applied consistently so `RunGroup` and `AgentTimeline` share one calm look.
13
+
3
14
  ## 5.0.0
4
15
 
5
16
  ### Patch Changes
package/dist/chat.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  MessageList,
7
7
  ThinkingIndicator,
8
8
  UserMessage
9
- } from "./chunk-KT5RNO7N.js";
9
+ } from "./chunk-FKDZOA4S.js";
10
10
  import "./chunk-54SQQMMM.js";
11
- import "./chunk-JBPWIYTQ.js";
12
- import "./chunk-DGW77LD7.js";
11
+ import "./chunk-TAWY3KOH.js";
12
+ import "./chunk-3O4XJCOE.js";
13
13
  import "./chunk-4CLN43XT.js";
14
14
  import "./chunk-BX6AQMUS.js";
15
15
  import "./chunk-AAUNOHVL.js";
@@ -13,6 +13,7 @@ import {
13
13
  FileCode,
14
14
  Search,
15
15
  CheckCircle,
16
+ AlertCircle,
16
17
  ChevronRight,
17
18
  Loader2,
18
19
  FolderOpen,
@@ -103,10 +104,9 @@ function ToolCallStep({
103
104
  "div",
104
105
  {
105
106
  className: cn(
106
- "group overflow-hidden rounded-[var(--radius-lg)] border bg-card transition-colors",
107
- status === "running" && "border-border",
108
- status === "success" && "border-border hover:border-primary/20",
109
- status === "error" && "border-[var(--surface-danger-border)]",
107
+ "group overflow-hidden rounded-[var(--radius-lg)] border bg-card/40 transition-colors",
108
+ "border-[var(--border-subtle)] hover:border-border",
109
+ status === "error" && "border-[var(--surface-danger-border)]/60",
110
110
  className
111
111
  ),
112
112
  children: [
@@ -124,28 +124,18 @@ function ToolCallStep({
124
124
  "div",
125
125
  {
126
126
  className: cn(
127
- "flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)] border",
128
- status === "running" && "border-[var(--border-accent)] bg-[var(--accent-surface-soft)] text-primary",
129
- status === "success" && "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
130
- status === "error" && "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]"
127
+ "flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)]",
128
+ status === "running" && "bg-[var(--accent-surface-soft)] text-primary",
129
+ status === "success" && "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
130
+ status === "error" && "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]"
131
131
  ),
132
132
  children: status === "running" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin shrink-0" }) : /* @__PURE__ */ jsx(Icon, { className: cn("h-3 w-3 shrink-0", STATUS_COLORS[status]) })
133
133
  }
134
134
  ),
135
135
  /* @__PURE__ */ jsx("span", { className: "truncate flex-1 font-sans text-foreground", children: label }),
136
- /* @__PURE__ */ jsx(
137
- "span",
138
- {
139
- className: cn(
140
- "rounded-full border px-2 py-0.5 text-[11px] font-semibold uppercase tracking-[0.06em]",
141
- status === "running" && "border-border bg-[var(--accent-surface-soft)] text-primary",
142
- status === "success" && "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
143
- status === "error" && "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]"
144
- ),
145
- children: status
146
- }
147
- ),
148
- duration !== void 0 && status !== "running" && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: duration < 1e3 ? `${duration}ms` : `${(duration / 1e3).toFixed(1)}s` }),
136
+ duration !== void 0 && status !== "running" && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[11px] tabular-nums text-muted-foreground", children: duration < 1e3 ? `${duration}ms` : `${(duration / 1e3).toFixed(1)}s` }),
137
+ status === "success" && /* @__PURE__ */ jsx(CheckCircle, { className: "h-3.5 w-3.5 shrink-0 text-[var(--surface-success-text)]" }),
138
+ status === "error" && /* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5 shrink-0 text-[var(--surface-danger-text)]" }),
149
139
  hasExpandable && /* @__PURE__ */ jsx(
150
140
  ChevronRight,
151
141
  {
@@ -158,7 +148,7 @@ function ToolCallStep({
158
148
  ]
159
149
  }
160
150
  ),
161
- expanded && (detail || output) && /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-border bg-muted px-3 py-2.5", children: [
151
+ expanded && (detail || output) && /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-[var(--border-subtle)] bg-muted/40 px-3 py-2.5", children: [
162
152
  detail && (isFilePath(detail) ? /* @__PURE__ */ jsx(FilePathChip, { path: detail }) : /* @__PURE__ */ jsx("div", { className: "text-xs font-mono text-muted-foreground", children: detail })),
163
153
  output && /* @__PURE__ */ jsx(
164
154
  CodeBlock,
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-OEX7NZE3.js";
12
12
  import {
13
13
  parseToolEvent
14
- } from "./chunk-5VPTNXX7.js";
14
+ } from "./chunk-U4YRT6MT.js";
15
15
 
16
16
  // src/hooks/use-dropdown-menu.ts
17
17
  import { useEffect, useRef, useState } from "react";
@@ -6,11 +6,11 @@ import {
6
6
  import {
7
7
  InlineThinkingItem,
8
8
  RunGroup
9
- } from "./chunk-JBPWIYTQ.js";
9
+ } from "./chunk-TAWY3KOH.js";
10
10
  import {
11
11
  ToolCallGroup,
12
12
  ToolCallStep
13
- } from "./chunk-DGW77LD7.js";
13
+ } from "./chunk-3O4XJCOE.js";
14
14
  import {
15
15
  getToolDisplayMetadata
16
16
  } from "./chunk-BX6AQMUS.js";
@@ -232,8 +232,8 @@ var InlineToolItem = memo2(
232
232
  {
233
233
  className: cn(
234
234
  "w-full border text-left transition-colors",
235
- "border-border bg-card hover:border-[var(--border-accent-hover)] hover:bg-accent/35",
236
- open && "border-border bg-accent/30",
235
+ "border-[var(--border-subtle)] bg-card/40 hover:border-border hover:bg-accent/25",
236
+ open && "border-border bg-accent/20",
237
237
  shapeClass,
238
238
  className
239
239
  ),
@@ -269,7 +269,7 @@ var InlineToolItem = memo2(
269
269
  }
270
270
  ) : null
271
271
  ] }),
272
- /* @__PURE__ */ jsx3(Collapsible.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx3("div", { className: cn("mt-2 pl-4", contentClassName), children: renderToolDetail?.(part) ?? /* @__PURE__ */ jsx3(ExpandedToolDetail, { part }) }) })
272
+ /* @__PURE__ */ jsx3(Collapsible.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx3("div", { className: cn("mt-1.5 ml-2.5 border-l-2 border-primary/15 pl-3.5", contentClassName), children: renderToolDetail?.(part) ?? /* @__PURE__ */ jsx3(ExpandedToolDetail, { part }) }) })
273
273
  ] });
274
274
  }
275
275
  );
@@ -401,8 +401,8 @@ function AssistantShell({
401
401
  isStreaming,
402
402
  children
403
403
  }) {
404
- return /* @__PURE__ */ jsxs4("div", { className: "flex gap-3", children: [
405
- /* @__PURE__ */ jsx5("div", { className: "mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white", children: /* @__PURE__ */ jsx5(Bot2, { className: "h-4 w-4" }) }),
404
+ return /* @__PURE__ */ jsxs4("div", { className: "flex gap-2.5", children: [
405
+ /* @__PURE__ */ jsx5("div", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white", children: /* @__PURE__ */ jsx5(Bot2, { className: "h-3.5 w-3.5" }) }),
406
406
  /* @__PURE__ */ jsxs4("div", { className: ASSISTANT_SHELL, children: [
407
407
  /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.14em] text-[var(--text-muted)]", children: [
408
408
  /* @__PURE__ */ jsx5("span", { children: branding.label }),
@@ -628,7 +628,7 @@ var RunGroup = memo4(
628
628
  }) });
629
629
  }
630
630
  return /* @__PURE__ */ jsx5(Collapsible3.Root, { open: !collapsed, onOpenChange: () => onToggle(), children: /* @__PURE__ */ jsxs4("div", { className: "rounded-[28px] border border-[var(--border-subtle)] bg-[var(--bg-card)] shadow-none", children: [
631
- /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-3 px-4 py-3.5", children: [
631
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-3 px-3 py-2.5", children: [
632
632
  /* @__PURE__ */ jsx5(Collapsible3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx5(
633
633
  "button",
634
634
  {
@@ -636,14 +636,14 @@ var RunGroup = memo4(
636
636
  "w-full rounded-[20px] px-0 py-0 text-left transition-colors",
637
637
  "bg-transparent hover:bg-transparent"
638
638
  ),
639
- children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2.5", children: [
639
+ children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
640
640
  /* @__PURE__ */ jsx5(
641
641
  "div",
642
642
  {
643
643
  className: cn(
644
- "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white"
644
+ "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white"
645
645
  ),
646
- children: /* @__PURE__ */ jsx5(Bot2, { className: "h-4 w-4" })
646
+ children: /* @__PURE__ */ jsx5(Bot2, { className: "h-3.5 w-3.5" })
647
647
  }
648
648
  ),
649
649
  /* @__PURE__ */ jsx5("span", { className: cn("text-sm font-semibold", branding.textClass), children: branding.label }),
@@ -666,79 +666,60 @@ var RunGroup = memo4(
666
666
  headerActions ? /* @__PURE__ */ jsx5("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1", children: headerActions }) : null
667
667
  ] }),
668
668
  collapsed && run.summaryText && /* @__PURE__ */ jsx5("div", { className: "px-4 pb-4 text-sm leading-6 text-muted-foreground line-clamp-2", children: run.summaryText }),
669
- /* @__PURE__ */ jsx5(Collapsible3.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx5("div", { className: cn("space-y-3 border-t border-[var(--border-subtle)] px-4 pb-4 pt-3"), children: allParts.map(({ part, msgId, index }, partIndex) => {
669
+ /* @__PURE__ */ jsx5(Collapsible3.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx5("div", { className: cn("border-t border-[var(--border-subtle)] px-4 pb-4 pt-3"), children: allParts.map(({ part, msgId, index }, partIndex) => {
670
670
  const key = `${msgId}-${index}`;
671
+ const prev = allParts[partIndex - 1]?.part;
672
+ const connectedTool = part.type === "tool" && prev?.type === "tool" && !isOpenUITool(part) && !isOpenUITool(prev);
673
+ const gapClass = partIndex === 0 ? "" : connectedTool ? "mt-px" : "mt-3";
674
+ let node = null;
671
675
  if (part.type === "tool") {
672
676
  if (isOpenUITool(part)) {
673
677
  const toolPart = part;
674
678
  const schema = extractOpenUISchema(toolPart.state.output);
675
679
  const summary = getOpenUISummary(toolPart.state.output);
676
680
  if (toolPart.state.status === "completed" && schema) {
677
- return /* @__PURE__ */ jsxs4(
678
- "div",
679
- {
680
- className: "overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]",
681
- children: [
682
- summary ? /* @__PURE__ */ jsxs4("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3", children: [
683
- /* @__PURE__ */ jsx5("div", { className: "text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "View" }),
684
- /* @__PURE__ */ jsx5("div", { className: "mt-1 text-sm leading-6 text-foreground", children: summary })
685
- ] }) : null,
686
- /* @__PURE__ */ jsx5("div", { className: "p-4", children: /* @__PURE__ */ jsx5(OpenUIArtifactRenderer, { schema }) })
687
- ]
688
- },
689
- key
690
- );
691
- }
692
- if (toolPart.state.status === "running") {
693
- return /* @__PURE__ */ jsxs4(
694
- "div",
695
- {
696
- 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",
697
- children: [
698
- /* @__PURE__ */ jsx5(Loader23, { className: "h-4 w-4 animate-spin text-primary" }),
699
- "Building view\u2026"
700
- ]
701
- },
702
- key
703
- );
681
+ node = /* @__PURE__ */ jsxs4("div", { className: "overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]", children: [
682
+ summary ? /* @__PURE__ */ jsxs4("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3", children: [
683
+ /* @__PURE__ */ jsx5("div", { className: "text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "View" }),
684
+ /* @__PURE__ */ jsx5("div", { className: "mt-1 text-sm leading-6 text-foreground", children: summary })
685
+ ] }) : null,
686
+ /* @__PURE__ */ jsx5("div", { className: "p-4", children: /* @__PURE__ */ jsx5(OpenUIArtifactRenderer, { schema }) })
687
+ ] });
688
+ } else if (toolPart.state.status === "running") {
689
+ node = /* @__PURE__ */ jsxs4("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: [
690
+ /* @__PURE__ */ jsx5(Loader23, { className: "h-4 w-4 animate-spin text-primary" }),
691
+ "Building view\u2026"
692
+ ] });
704
693
  }
705
694
  }
706
- return /* @__PURE__ */ jsx5(
707
- InlineToolItem,
708
- {
709
- part,
710
- renderToolDetail,
711
- groupPosition: getToolGroupPosition(partIndex, allParts),
712
- actions: renderToolActions?.(part, {
713
- run,
714
- messageId: msgId,
715
- partIndex: index
716
- })
717
- },
718
- key
719
- );
720
- }
721
- if (part.type === "reasoning") {
722
- return /* @__PURE__ */ jsx5(
695
+ if (node === null) {
696
+ node = /* @__PURE__ */ jsx5(
697
+ InlineToolItem,
698
+ {
699
+ part,
700
+ renderToolDetail,
701
+ groupPosition: getToolGroupPosition(partIndex, allParts),
702
+ actions: renderToolActions?.(part, {
703
+ run,
704
+ messageId: msgId,
705
+ partIndex: index
706
+ })
707
+ }
708
+ );
709
+ }
710
+ } else if (part.type === "reasoning") {
711
+ node = /* @__PURE__ */ jsx5(
723
712
  InlineThinkingItem,
724
713
  {
725
714
  part,
726
715
  defaultOpen: isStreaming
727
- },
728
- key
729
- );
730
- }
731
- if (part.type === "text" && !part.synthetic && part.text.trim()) {
732
- return /* @__PURE__ */ jsx5(
733
- "div",
734
- {
735
- className: "px-1 py-1",
736
- children: /* @__PURE__ */ jsx5(Markdown, { className: "tangle-prose text-[15px] leading-7", children: part.text })
737
- },
738
- key
716
+ }
739
717
  );
718
+ } else if (part.type === "text" && !part.synthetic && part.text.trim()) {
719
+ node = /* @__PURE__ */ jsx5("div", { className: "px-1 py-1", children: /* @__PURE__ */ jsx5(Markdown, { className: "tangle-prose text-[15px] leading-7", children: part.text }) });
740
720
  }
741
- return null;
721
+ if (!node) return null;
722
+ return /* @__PURE__ */ jsx5("div", { className: gapClass, children: node }, key);
742
723
  }) }) })
743
724
  ] }) });
744
725
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ToolCallGroup,
3
3
  ToolCallStep
4
- } from "./chunk-DGW77LD7.js";
4
+ } from "./chunk-3O4XJCOE.js";
5
5
  import {
6
6
  Markdown
7
7
  } from "./chunk-FJBTCTZM.js";
package/dist/hooks.js CHANGED
@@ -11,15 +11,15 @@ import {
11
11
  useSSEStream,
12
12
  useSdkSession,
13
13
  useToolCallStream
14
- } from "./chunk-CMX2I43A.js";
14
+ } from "./chunk-CJJMKDNN.js";
15
15
  import "./chunk-OEX7NZE3.js";
16
16
  import {
17
17
  useAutoScroll,
18
18
  useRunCollapseState,
19
19
  useRunGroups
20
20
  } from "./chunk-54SQQMMM.js";
21
- import "./chunk-5VPTNXX7.js";
22
- import "./chunk-DGW77LD7.js";
21
+ import "./chunk-U4YRT6MT.js";
22
+ import "./chunk-3O4XJCOE.js";
23
23
  import "./chunk-BX6AQMUS.js";
24
24
  import "./chunk-FJBTCTZM.js";
25
25
  import "./chunk-WUQDUBJG.js";
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  useSSEStream,
18
18
  useSdkSession,
19
19
  useToolCallStream
20
- } from "./chunk-CMX2I43A.js";
20
+ } from "./chunk-CJJMKDNN.js";
21
21
  import {
22
22
  addMessage,
23
23
  addParts,
@@ -138,7 +138,7 @@ import {
138
138
  MessageList,
139
139
  ThinkingIndicator,
140
140
  UserMessage
141
- } from "./chunk-KT5RNO7N.js";
141
+ } from "./chunk-FKDZOA4S.js";
142
142
  import {
143
143
  useAutoScroll,
144
144
  useRunCollapseState,
@@ -148,18 +148,18 @@ import "./chunk-LQS34IGP.js";
148
148
  import {
149
149
  ToolCallFeed,
150
150
  parseToolEvent
151
- } from "./chunk-5VPTNXX7.js";
151
+ } from "./chunk-U4YRT6MT.js";
152
152
  import {
153
153
  ExpandedToolDetail,
154
154
  InlineThinkingItem,
155
155
  InlineToolItem,
156
156
  LiveDuration,
157
157
  RunGroup
158
- } from "./chunk-JBPWIYTQ.js";
158
+ } from "./chunk-TAWY3KOH.js";
159
159
  import {
160
160
  ToolCallGroup,
161
161
  ToolCallStep
162
- } from "./chunk-DGW77LD7.js";
162
+ } from "./chunk-3O4XJCOE.js";
163
163
  import {
164
164
  formatBytes,
165
165
  formatDuration,
package/dist/run.js CHANGED
@@ -2,18 +2,18 @@ import "./chunk-LQS34IGP.js";
2
2
  import {
3
3
  ToolCallFeed,
4
4
  parseToolEvent
5
- } from "./chunk-5VPTNXX7.js";
5
+ } from "./chunk-U4YRT6MT.js";
6
6
  import {
7
7
  ExpandedToolDetail,
8
8
  InlineThinkingItem,
9
9
  InlineToolItem,
10
10
  LiveDuration,
11
11
  RunGroup
12
- } from "./chunk-JBPWIYTQ.js";
12
+ } from "./chunk-TAWY3KOH.js";
13
13
  import {
14
14
  ToolCallGroup,
15
15
  ToolCallStep
16
- } from "./chunk-DGW77LD7.js";
16
+ } from "./chunk-3O4XJCOE.js";
17
17
  import "./chunk-4CLN43XT.js";
18
18
  import "./chunk-BX6AQMUS.js";
19
19
  import "./chunk-AAUNOHVL.js";
package/dist/sdk-hooks.js CHANGED
@@ -5,15 +5,15 @@ import {
5
5
  useSSEStream,
6
6
  useSdkSession,
7
7
  useToolCallStream
8
- } from "./chunk-CMX2I43A.js";
8
+ } from "./chunk-CJJMKDNN.js";
9
9
  import "./chunk-OEX7NZE3.js";
10
10
  import {
11
11
  useAutoScroll,
12
12
  useRunCollapseState,
13
13
  useRunGroups
14
14
  } from "./chunk-54SQQMMM.js";
15
- import "./chunk-5VPTNXX7.js";
16
- import "./chunk-DGW77LD7.js";
15
+ import "./chunk-U4YRT6MT.js";
16
+ import "./chunk-3O4XJCOE.js";
17
17
  import "./chunk-BX6AQMUS.js";
18
18
  import "./chunk-FJBTCTZM.js";
19
19
  import "./chunk-WUQDUBJG.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "5.0.0",
3
+ "version": "5.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",
@@ -101,8 +101,8 @@ export const InlineToolItem = memo(
101
101
  <button
102
102
  className={cn(
103
103
  "w-full border text-left transition-colors",
104
- "border-border bg-card hover:border-[var(--border-accent-hover)] hover:bg-accent/35",
105
- open && "border-border bg-accent/30",
104
+ "border-[var(--border-subtle)] bg-card/40 hover:border-border hover:bg-accent/25",
105
+ open && "border-border bg-accent/20",
106
106
  shapeClass,
107
107
  className,
108
108
  )}
@@ -179,7 +179,7 @@ export const InlineToolItem = memo(
179
179
  </div>
180
180
 
181
181
  <Collapsible.Content className="overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp">
182
- <div className={cn("mt-2 pl-4", contentClassName)}>
182
+ <div className={cn("mt-1.5 ml-2.5 border-l-2 border-primary/15 pl-3.5", contentClassName)}>
183
183
  {renderToolDetail?.(part) ?? <ExpandedToolDetail part={part} />}
184
184
  </div>
185
185
  </Collapsible.Content>
@@ -58,9 +58,9 @@ function AssistantShell({
58
58
  children: ReactNode;
59
59
  }) {
60
60
  return (
61
- <div className="flex gap-3">
62
- <div className="mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white">
63
- <Bot className="h-4 w-4" />
61
+ <div className="flex gap-2.5">
62
+ <div className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white">
63
+ <Bot className="h-3.5 w-3.5" />
64
64
  </div>
65
65
 
66
66
  <div className={ASSISTANT_SHELL}>
@@ -402,7 +402,7 @@ export const RunGroup = memo(
402
402
  <Collapsible.Root open={!collapsed} onOpenChange={() => onToggle()}>
403
403
  <div className="rounded-[28px] border border-[var(--border-subtle)] bg-[var(--bg-card)] shadow-none">
404
404
  {/* Header */}
405
- <div className="flex items-start gap-3 px-4 py-3.5">
405
+ <div className="flex items-start gap-3 px-3 py-2.5">
406
406
  <Collapsible.Trigger asChild>
407
407
  <button
408
408
  className={cn(
@@ -410,13 +410,13 @@ export const RunGroup = memo(
410
410
  "bg-transparent hover:bg-transparent",
411
411
  )}
412
412
  >
413
- <div className="flex items-center gap-2.5">
413
+ <div className="flex items-center gap-2">
414
414
  <div
415
415
  className={cn(
416
- "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white",
416
+ "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white",
417
417
  )}
418
418
  >
419
- <Bot className="h-4 w-4" />
419
+ <Bot className="h-3.5 w-3.5" />
420
420
  </div>
421
421
 
422
422
  <span className={cn("text-sm font-semibold", branding.textClass)}>
@@ -473,10 +473,24 @@ export const RunGroup = memo(
473
473
 
474
474
  {/* Expanded content */}
475
475
  <Collapsible.Content className="overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp">
476
- <div className={cn("space-y-3 border-t border-[var(--border-subtle)] px-4 pb-4 pt-3")}>
476
+ <div className={cn("border-t border-[var(--border-subtle)] px-4 pb-4 pt-3")}>
477
477
  {allParts.map(({ part, msgId, index }, partIndex) => {
478
478
  const key = `${msgId}-${index}`;
479
479
 
480
+ // Consecutive (non-OpenUI) tool calls connect into one block —
481
+ // `getToolGroupPosition` already gives them joined radii, so they
482
+ // get no vertical gap; every other transition keeps a normal gap.
483
+ const prev = allParts[partIndex - 1]?.part;
484
+ const connectedTool =
485
+ part.type === "tool" &&
486
+ prev?.type === "tool" &&
487
+ !isOpenUITool(part as ToolPart) &&
488
+ !isOpenUITool(prev as ToolPart);
489
+ const gapClass =
490
+ partIndex === 0 ? "" : connectedTool ? "mt-px" : "mt-3";
491
+
492
+ let node: ReactNode = null;
493
+
480
494
  if (part.type === "tool") {
481
495
  if (isOpenUITool(part as ToolPart)) {
482
496
  const toolPart = part as ToolPart;
@@ -484,11 +498,8 @@ export const RunGroup = memo(
484
498
  const summary = getOpenUISummary(toolPart.state.output);
485
499
 
486
500
  if (toolPart.state.status === "completed" && schema) {
487
- return (
488
- <div
489
- key={key}
490
- className="overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]"
491
- >
501
+ node = (
502
+ <div className="overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]">
492
503
  {summary ? (
493
504
  <div className="border-b border-[var(--border-subtle)] px-4 py-3">
494
505
  <div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground">
@@ -502,14 +513,9 @@ export const RunGroup = memo(
502
513
  </div>
503
514
  </div>
504
515
  );
505
- }
506
-
507
- if (toolPart.state.status === "running") {
508
- return (
509
- <div
510
- key={key}
511
- 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"
512
- >
516
+ } else if (toolPart.state.status === "running") {
517
+ node = (
518
+ <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">
513
519
  <Loader2 className="h-4 w-4 animate-spin text-primary" />
514
520
  Building view…
515
521
  </div>
@@ -517,47 +523,45 @@ export const RunGroup = memo(
517
523
  }
518
524
  }
519
525
 
520
- return (
521
- <InlineToolItem
522
- key={key}
523
- part={part as ToolPart}
524
- renderToolDetail={renderToolDetail}
525
- groupPosition={getToolGroupPosition(partIndex, allParts)}
526
- actions={renderToolActions?.(part as ToolPart, {
527
- run,
528
- messageId: msgId,
529
- partIndex: index,
530
- })}
531
- />
532
- );
533
- }
534
-
535
- if (part.type === "reasoning") {
536
- return (
526
+ if (node === null) {
527
+ node = (
528
+ <InlineToolItem
529
+ part={part as ToolPart}
530
+ renderToolDetail={renderToolDetail}
531
+ groupPosition={getToolGroupPosition(partIndex, allParts)}
532
+ actions={renderToolActions?.(part as ToolPart, {
533
+ run,
534
+ messageId: msgId,
535
+ partIndex: index,
536
+ })}
537
+ />
538
+ );
539
+ }
540
+ } else if (part.type === "reasoning") {
541
+ node = (
537
542
  <InlineThinkingItem
538
- key={key}
539
543
  part={part as ReasoningPart}
540
544
  defaultOpen={isStreaming}
541
545
  />
542
546
  );
543
- }
544
-
545
- if (
547
+ } else if (
546
548
  part.type === "text" &&
547
549
  !part.synthetic &&
548
550
  part.text.trim()
549
551
  ) {
550
- return (
551
- <div
552
- key={key}
553
- className="px-1 py-1"
554
- >
552
+ node = (
553
+ <div className="px-1 py-1">
555
554
  <Markdown className="tangle-prose text-[15px] leading-7">{part.text}</Markdown>
556
555
  </div>
557
556
  );
558
557
  }
559
558
 
560
- return null;
559
+ if (!node) return null;
560
+ return (
561
+ <div key={key} className={gapClass}>
562
+ {node}
563
+ </div>
564
+ );
561
565
  })}
562
566
  </div>
563
567
  </Collapsible.Content>
@@ -12,6 +12,7 @@ import {
12
12
  FileCode,
13
13
  Search,
14
14
  CheckCircle,
15
+ AlertCircle,
15
16
  ChevronRight,
16
17
  Loader2,
17
18
  FolderOpen,
@@ -130,10 +131,9 @@ export function ToolCallStep({
130
131
  return (
131
132
  <div
132
133
  className={cn(
133
- "group overflow-hidden rounded-[var(--radius-lg)] border bg-card transition-colors",
134
- status === "running" && "border-border",
135
- status === "success" && "border-border hover:border-primary/20",
136
- status === "error" && "border-[var(--surface-danger-border)]",
134
+ "group overflow-hidden rounded-[var(--radius-lg)] border bg-card/40 transition-colors",
135
+ "border-[var(--border-subtle)] hover:border-border",
136
+ status === "error" && "border-[var(--surface-danger-border)]/60",
137
137
  className,
138
138
  )}
139
139
  >
@@ -147,10 +147,10 @@ export function ToolCallStep({
147
147
  >
148
148
  <div
149
149
  className={cn(
150
- "flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)] border",
151
- status === "running" && "border-[var(--border-accent)] bg-[var(--accent-surface-soft)] text-primary",
152
- status === "success" && "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
153
- status === "error" && "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]",
150
+ "flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)]",
151
+ status === "running" && "bg-[var(--accent-surface-soft)] text-primary",
152
+ status === "success" && "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
153
+ status === "error" && "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]",
154
154
  )}
155
155
  >
156
156
  {status === "running" ? (
@@ -165,26 +165,22 @@ export function ToolCallStep({
165
165
  {label}
166
166
  </span>
167
167
 
168
- <span
169
- className={cn(
170
- "rounded-full border px-2 py-0.5 text-[11px] font-semibold uppercase tracking-[0.06em]",
171
- status === "running" &&
172
- "border-border bg-[var(--accent-surface-soft)] text-primary",
173
- status === "success" &&
174
- "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
175
- status === "error" && "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]",
176
- )}
177
- >
178
- {status}
179
- </span>
180
-
181
- {/* Duration */}
168
+ {/* Duration — quiet, before the status glyph */}
182
169
  {duration !== undefined && status !== "running" && (
183
- <span className="shrink-0 text-xs tabular-nums text-muted-foreground">
170
+ <span className="shrink-0 text-[11px] tabular-nums text-muted-foreground">
184
171
  {duration < 1000 ? `${duration}ms` : `${(duration / 1000).toFixed(1)}s`}
185
172
  </span>
186
173
  )}
187
174
 
175
+ {/* Quiet status glyph — the loud uppercase pill read as harsh; the colored
176
+ icon carries the same signal calmly (running spins in the left badge). */}
177
+ {status === "success" && (
178
+ <CheckCircle className="h-3.5 w-3.5 shrink-0 text-[var(--surface-success-text)]" />
179
+ )}
180
+ {status === "error" && (
181
+ <AlertCircle className="h-3.5 w-3.5 shrink-0 text-[var(--surface-danger-text)]" />
182
+ )}
183
+
188
184
  {/* Expand chevron */}
189
185
  {hasExpandable && (
190
186
  <ChevronRight
@@ -198,7 +194,7 @@ export function ToolCallStep({
198
194
 
199
195
  {/* Expandable content */}
200
196
  {expanded && (detail || output) && (
201
- <div className="space-y-2 border-t border-border bg-muted px-3 py-2.5">
197
+ <div className="space-y-2 border-t border-[var(--border-subtle)] bg-muted/40 px-3 py-2.5">
202
198
  {detail && (
203
199
  isFilePath(detail)
204
200
  ? <FilePathChip path={detail} />