@tangle-network/ui 11.48.0 → 11.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/chat/agent-timeline.js +4 -3
  3. package/dist/chat/speaker.d.ts +3 -3
  4. package/dist/chat/speaker.js +12 -18
  5. package/dist/index.d.ts +4 -3
  6. package/dist/index.js +4 -3
  7. package/dist/markdown/code-block.js +1 -1
  8. package/dist/presentation.d.ts +12 -0
  9. package/dist/presentation.js +85 -1
  10. package/dist/primitives/metric-strip.d.ts +2 -2
  11. package/dist/primitives/page-shell.d.ts +6 -3
  12. package/dist/primitives/page-shell.js +3 -60
  13. package/dist/primitives/progress.d.ts +8 -0
  14. package/dist/primitives/progress.js +8 -2
  15. package/dist/primitives/settings.d.ts +28 -1
  16. package/dist/primitives/settings.js +78 -29
  17. package/dist/primitives/status-pill.js +11 -2
  18. package/dist/primitives.d.ts +3 -3
  19. package/dist/primitives.js +3 -3
  20. package/dist/run/expanded-tool-detail.js +12 -4
  21. package/dist/run/run-group.js +21 -9
  22. package/dist/run/run-phase-list.js +5 -8
  23. package/dist/run/status-meta.d.ts +25 -0
  24. package/dist/run/status-meta.js +105 -0
  25. package/dist/run.d.ts +2 -1
  26. package/dist/run.js +2 -1
  27. package/package.json +2 -2
  28. package/src/chat/agent-timeline.test.tsx +9 -0
  29. package/src/chat/agent-timeline.tsx +4 -3
  30. package/src/chat/speaker.test.tsx +3 -1
  31. package/src/chat/speaker.tsx +13 -14
  32. package/src/markdown/code-block.tsx +2 -1
  33. package/src/markdown/markdown.stories.tsx +11 -0
  34. package/src/presentation.ts +68 -0
  35. package/src/primitives/index.ts +2 -1
  36. package/src/primitives/metric-strip.tsx +2 -2
  37. package/src/primitives/page-shell.tsx +9 -58
  38. package/src/primitives/progress.stories.tsx +10 -0
  39. package/src/primitives/progress.tsx +13 -2
  40. package/src/primitives/settings.stories.tsx +94 -3
  41. package/src/primitives/settings.tsx +80 -19
  42. package/src/primitives/status-pill.tsx +8 -1
  43. package/src/run/expanded-tool-detail.tsx +4 -10
  44. package/src/run/index.ts +8 -0
  45. package/src/run/run-group.tsx +8 -16
  46. package/src/run/run-phase-list.stories.tsx +19 -0
  47. package/src/run/run-phase-list.test.tsx +9 -1
  48. package/src/run/run-phase-list.tsx +3 -3
  49. package/src/run/status-meta.tsx +92 -0
  50. package/src/styles/presentation.css +3 -3
@@ -6,19 +6,20 @@ import { Label } from "./label.js";
6
6
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "./dropdown-menu.js";
7
7
  import { InfoTip } from "./info-tip.js";
8
8
  import { fieldMessageId } from "./field.js";
9
+ import { StatusGlyph } from "./status-pill.js";
9
10
  import * as React from "react";
10
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
- import { CircleAlert, Ellipsis, Lock } from "lucide-react";
12
+ import { CircleAlert, Ellipsis, Lock, TriangleAlert } from "lucide-react";
12
13
  // src/primitives/settings.tsx
13
14
  const SCROLL_OFFSET = "scroll-mt-[calc(var(--shell-header-height)_+_1rem)]";
15
+ const GUTTER = "px-5 py-4 @max-[34rem]/settings-section:px-4";
14
16
  const DIVIDED = "not-first:border-t not-first:border-[color:var(--border-subtle)]";
15
17
  const ERROR_TEXT = "text-[color:var(--surface-danger-text)]";
16
- function ErrorLine({ id, className, children }) {
18
+ function GlyphLine({ icon: Icon, className, children, ...props }) {
17
19
  return /* @__PURE__ */ jsxs("div", {
18
- id,
19
- role: "alert",
20
- className: cn("flex items-start gap-1.5 text-sm leading-5 [overflow-wrap:anywhere]", ERROR_TEXT, className),
21
- children: [/* @__PURE__ */ jsx(CircleAlert, {
20
+ ...props,
21
+ className: cn("flex items-start gap-1.5 text-sm leading-5 [overflow-wrap:anywhere]", className),
22
+ children: [/* @__PURE__ */ jsx(Icon, {
22
23
  "aria-hidden": "true",
23
24
  className: "mt-0.5 size-4 shrink-0"
24
25
  }), /* @__PURE__ */ jsx("div", {
@@ -27,18 +28,32 @@ function ErrorLine({ id, className, children }) {
27
28
  })]
28
29
  });
29
30
  }
30
- function LockLine({ id, className, children }) {
31
- return /* @__PURE__ */ jsxs("div", {
32
- id,
33
- className: cn("flex items-start gap-1.5 text-sm leading-5 text-muted-foreground [overflow-wrap:anywhere]", className),
34
- children: [/* @__PURE__ */ jsx(Lock, {
35
- "aria-hidden": "true",
36
- className: "mt-0.5 size-4 shrink-0"
37
- }), /* @__PURE__ */ jsx("div", {
38
- className: "min-w-0",
39
- children
40
- })]
41
- });
31
+ const ErrorLine = ({ className, ...props }) => /* @__PURE__ */ jsx(GlyphLine, {
32
+ icon: CircleAlert,
33
+ role: "alert",
34
+ className: cn(ERROR_TEXT, className),
35
+ ...props
36
+ });
37
+ const LockLine = ({ className, ...props }) => /* @__PURE__ */ jsx(GlyphLine, {
38
+ icon: Lock,
39
+ className: cn("text-muted-foreground", className),
40
+ ...props
41
+ });
42
+ const TOKEN_BREAK = /(?<=[/@])(?=[^/])|(?<=[\p{L}\p{N}])(?=[.\-_?#&=][\p{L}\p{N}])/u;
43
+ function breakTokens(node) {
44
+ if (typeof node === "string") {
45
+ if (!TOKEN_BREAK.test(node)) return node;
46
+ return node.split(/(\s+)/).map((word, index) => {
47
+ const parts = word.split(TOKEN_BREAK);
48
+ return parts.length < 2 ? word : /* @__PURE__ */ jsx("span", {
49
+ className: "inline-block max-w-full [text-decoration:inherit]",
50
+ children: parts.map((part, at) => /* @__PURE__ */ jsxs(React.Fragment, { children: [at > 0 && /* @__PURE__ */ jsx("wbr", {}), part] }, at))
51
+ }, index);
52
+ });
53
+ }
54
+ if (Array.isArray(node)) return React.Children.map(node, breakTokens);
55
+ if (React.isValidElement(node) && node.props.children != null && typeof node.props.children !== "function") return React.cloneElement(node, void 0, breakTokens(node.props.children));
56
+ return node;
42
57
  }
43
58
  const ROW_COLUMNS = {
44
59
  inline: "grid-cols-[minmax(0,1fr)_minmax(13rem,20rem)] @max-[34rem]/settings-section:grid-cols-1",
@@ -73,7 +88,7 @@ function SettingsRow(props) {
73
88
  return /* @__PURE__ */ jsxs("div", {
74
89
  id,
75
90
  "data-layout": layout,
76
- className: cn("grid min-w-0 items-center gap-x-8 gap-y-3 px-5 py-4 @max-[34rem]/settings-section:px-4", SCROLL_OFFSET, DIVIDED, ROW_COLUMNS[hasValue && layout === "compact" ? "inline" : layout]),
91
+ className: cn("grid min-w-0 items-center gap-x-8 gap-y-3", GUTTER, SCROLL_OFFSET, DIVIDED, ROW_COLUMNS[hasValue && layout === "compact" ? "inline" : layout]),
77
92
  children: [/* @__PURE__ */ jsxs("div", {
78
93
  className: "min-w-0",
79
94
  children: [/* @__PURE__ */ jsxs("div", {
@@ -121,7 +136,7 @@ function SettingsRow(props) {
121
136
  children: [hasValue && /* @__PURE__ */ jsx("span", {
122
137
  "data-slot": "settings-value",
123
138
  className: cn("min-w-0 text-foreground text-sm [overflow-wrap:anywhere] @min-[34rem]/settings-section:text-right", mono && "font-mono"),
124
- children: value
139
+ children: breakTokens(value)
125
140
  }), control]
126
141
  })]
127
142
  });
@@ -142,7 +157,7 @@ function SettingsSection({ id, title, description, info, actions, tone = "defaul
142
157
  className: cn("@container/settings-section min-w-0", SCROLL_OFFSET, danger && "border-[color:var(--surface-danger-border)]"),
143
158
  children: [
144
159
  /* @__PURE__ */ jsxs("div", {
145
- className: "flex items-start justify-between gap-4 px-5 py-4 @max-[34rem]/settings-section:flex-wrap @max-[34rem]/settings-section:px-4",
160
+ className: cn("flex items-start justify-between gap-4 @max-[34rem]/settings-section:flex-wrap", GUTTER),
146
161
  children: [/* @__PURE__ */ jsxs("div", {
147
162
  className: "min-w-0",
148
163
  children: [/* @__PURE__ */ jsxs("div", {
@@ -173,6 +188,35 @@ function SettingsSection({ id, title, description, info, actions, tone = "defaul
173
188
  ]
174
189
  });
175
190
  }
191
+ const NOTE_TONE = {
192
+ default: "text-foreground",
193
+ muted: "text-muted-foreground",
194
+ warning: "text-[color:var(--surface-warning-text)]",
195
+ danger: ERROR_TEXT
196
+ };
197
+ const NOTE_GLYPH = {
198
+ warning: TriangleAlert,
199
+ danger: CircleAlert
200
+ };
201
+ /**
202
+ * A line or block of a {@link SettingsSection} that is not a row: an empty list, a loading line,
203
+ * a notice, a refusal or a figure. It takes the rows' gutters and divider, so it lines up with
204
+ * the rows and lists around it.
205
+ */
206
+ function SettingsNote({ tone = "default", role, children }) {
207
+ const className = cn("min-w-0", GUTTER, DIVIDED, NOTE_TONE[tone]);
208
+ const glyph = NOTE_GLYPH[tone];
209
+ return glyph ? /* @__PURE__ */ jsx(GlyphLine, {
210
+ icon: glyph,
211
+ role,
212
+ className,
213
+ children
214
+ }) : /* @__PURE__ */ jsx("div", {
215
+ role,
216
+ className: cn("text-sm leading-5 [overflow-wrap:anywhere]", className),
217
+ children
218
+ });
219
+ }
176
220
  const LIST_SLOTS = [
177
221
  "leading",
178
222
  "main",
@@ -251,7 +295,7 @@ const NARROW_STATUS = "@max-[34rem]/settings-section:col-start-[main] @max-[34re
251
295
  * that name. Without a status column the state follows the meta, and without
252
296
  * a menu column the ⋯ menu ends the action cell.
253
297
  */
254
- function SettingsListItem({ leading, title, meta, status, error, action, menu }) {
298
+ function SettingsListItem({ leading, title, meta, status, tone, error, action, menu }) {
255
299
  const columns = React.useContext(SettingsListContext);
256
300
  const titleId = React.useId();
257
301
  const reasonId = `${titleId}-reason`;
@@ -268,6 +312,11 @@ function SettingsListItem({ leading, title, meta, status, error, action, menu })
268
312
  onClick: locked ? void 0 : action.onSelect,
269
313
  children: action.label
270
314
  });
315
+ const state = tone && status != null ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(StatusGlyph, { tone }), /* @__PURE__ */ jsx("span", {
316
+ className: "min-w-0",
317
+ children: status
318
+ })] }) : status;
319
+ const toned = tone && "flex items-center gap-1.5";
271
320
  const actionCell = (actionButton || menuNode && !columns.menu) && /* @__PURE__ */ jsxs(Fragment$1, { children: [actionButton, !columns.menu && menuNode] });
272
321
  return /* @__PURE__ */ jsxs("li", {
273
322
  className: cn("col-span-full grid grid-cols-subgrid items-center px-5 py-3 @max-[34rem]/settings-section:px-4", DIVIDED),
@@ -282,15 +331,15 @@ function SettingsListItem({ leading, title, meta, status, error, action, menu })
282
331
  /* @__PURE__ */ jsx("div", {
283
332
  id: titleId,
284
333
  className: "font-medium text-sm leading-5 text-foreground [overflow-wrap:anywhere] [&_a]:rounded-sm [&_a]:text-foreground [&_a]:underline-offset-4 [&_a:hover]:underline [&_a:focus-visible]:outline-2 [&_a:focus-visible]:outline-offset-2 [&_a:focus-visible]:outline-[var(--focus-border)]",
285
- children: title
334
+ children: breakTokens(title)
286
335
  }),
287
336
  meta != null && /* @__PURE__ */ jsx("div", {
288
337
  className: "text-sm leading-5 text-muted-foreground [overflow-wrap:anywhere]",
289
- children: meta
338
+ children: breakTokens(meta)
290
339
  }),
291
340
  status != null && !columns.status && /* @__PURE__ */ jsx("div", {
292
- className: "mt-1 text-sm leading-5 text-muted-foreground",
293
- children: status
341
+ className: cn("mt-1 text-sm leading-5 text-muted-foreground", toned),
342
+ children: state
294
343
  }),
295
344
  locked && /* @__PURE__ */ jsx(LockLine, {
296
345
  id: reasonId,
@@ -304,8 +353,8 @@ function SettingsListItem({ leading, title, meta, status, error, action, menu })
304
353
  children: error
305
354
  }),
306
355
  status != null && columns.status && /* @__PURE__ */ jsx("div", {
307
- className: cn("col-start-[status] row-start-1 min-w-0 text-sm leading-5 text-muted-foreground", NARROW_STATUS),
308
- children: status
356
+ className: cn("col-start-[status] row-start-1 min-w-0 text-sm leading-5 text-muted-foreground", toned, NARROW_STATUS),
357
+ children: state
309
358
  }),
310
359
  actionCell && /* @__PURE__ */ jsx("div", {
311
360
  className: "col-start-[action] row-start-1 flex min-w-0 items-center justify-end gap-1",
@@ -318,4 +367,4 @@ function SettingsListItem({ leading, title, meta, status, error, action, menu })
318
367
  ]
319
368
  });
320
369
  }
321
- export { SettingsList, SettingsListItem, SettingsRow, SettingsSection };
370
+ export { SettingsList, SettingsListItem, SettingsNote, SettingsRow, SettingsSection };
@@ -107,15 +107,24 @@ function ToneGlyph({ tone }) {
107
107
  });
108
108
  }
109
109
  }
110
+ const GLYPH = "inline-flex [&>svg]:size-3 [&>svg]:shrink-0";
111
+ /** The tone's glyph in the tone's ink, before a label that keeps its own colour, as `bare` draws it. */
112
+ function StatusGlyph({ tone }) {
113
+ return /* @__PURE__ */ jsx("span", {
114
+ "aria-hidden": "true",
115
+ className: cn(GLYPH, TONE_TEXT[tone]),
116
+ children: /* @__PURE__ */ jsx(ToneGlyph, { tone })
117
+ });
118
+ }
110
119
  const StatusPill = React.forwardRef(({ className, tone, children, icon, bare = false, size = "sm", ...props }, ref) => /* @__PURE__ */ jsxs("span", {
111
120
  ref,
112
121
  className: cn("inline-flex items-center gap-1.5 whitespace-nowrap rounded-full font-medium", size === "sm" ? "px-2 py-0.5 text-xs" : "px-2.5 py-1 text-sm", bare ? "border border-transparent bg-transparent px-0" : cn("border", TONE_SURFACE[tone]), className),
113
122
  ...props,
114
123
  children: [/* @__PURE__ */ jsx("span", {
115
124
  "aria-hidden": "true",
116
- className: cn("inline-flex [&>svg]:size-3 [&>svg]:shrink-0", bare && TONE_TEXT[tone]),
125
+ className: cn(GLYPH, bare && TONE_TEXT[tone]),
117
126
  children: icon ?? /* @__PURE__ */ jsx(ToneGlyph, { tone })
118
127
  }), children]
119
128
  }));
120
129
  StatusPill.displayName = "StatusPill";
121
- export { StatusPill };
130
+ export { StatusGlyph, StatusPill };
@@ -45,11 +45,11 @@ import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
45
45
  import { Heading, HeadingProps, HeadingVariant } from "./primitives/heading.js";
46
46
  import { PageShell, PageShellProps } from "./primitives/page-shell.js";
47
47
  import { Field, FieldProps, fieldMessageId } from "./primitives/field.js";
48
- import { SettingsList, SettingsListAction, SettingsListColumns, SettingsListItem, SettingsListItemProps, SettingsListMenuItem, SettingsListProps, SettingsRow, SettingsRowLayout, SettingsRowProps, SettingsSection, SettingsSectionProps } from "./primitives/settings.js";
48
+ import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
49
+ import { SettingsList, SettingsListAction, SettingsListColumns, SettingsListItem, SettingsListItemProps, SettingsListMenuItem, SettingsListProps, SettingsNote, SettingsNoteProps, SettingsRow, SettingsRowLayout, SettingsRowProps, SettingsSection, SettingsSectionProps } from "./primitives/settings.js";
49
50
  import { PageHeader, PageHeaderProps } from "./primitives/page-header.js";
50
51
  import { Alert, AlertDescription, AlertProps, AlertTitle, AlertTone } from "./primitives/alert.js";
51
52
  import { Pagination, PaginationProps } from "./primitives/pagination.js";
52
- import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
53
53
  import { ComplianceBadge, ComplianceBadgeProps, ComplianceProduct, ComplianceStatus } from "./primitives/compliance-badge.js";
54
54
  import { Tag, TagProps } from "./primitives/tag.js";
55
55
  import { Chip, ChipProps } from "./primitives/chip.js";
@@ -62,4 +62,4 @@ import { SeriesSparkline, SeriesSparklineProps } from "./primitives/series-spark
62
62
  import { ChartReferenceLine, MetricChart, MetricChartProps } from "./primitives/metric-chart.js";
63
63
  import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
64
64
  import { Logo, LogoProps, TangleKnot } from "@tangle-network/brand";
65
- export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ArtifactPane, type ArtifactPaneProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, CATEGORY_TONES, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, type ChartReferenceLine, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, type ComplianceBadgeProps, type ComplianceProduct, type ComplianceStatus, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, type DatePickerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, Field, type FieldProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, MetricChart, type MetricChartProps, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, type PeriodDelta, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeriesExtent, type SeriesPoint, type SeriesPointInput, type SeriesScale, SeriesSparkline, type SeriesSparklineProps, SettingsList, type SettingsListAction, type SettingsListColumns, SettingsListItem, type SettingsListItemProps, type SettingsListMenuItem, type SettingsListProps, SettingsRow, type SettingsRowLayout, type SettingsRowProps, SettingsSection, type SettingsSectionProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, TimeSelect, type TimeSelectProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, Wizard, type WizardProps, WizardStep, type WizardStepProps, badgeVariants, buttonVariants, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
65
+ export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ArtifactPane, type ArtifactPaneProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, CATEGORY_TONES, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, type ChartReferenceLine, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, type ComplianceBadgeProps, type ComplianceProduct, type ComplianceStatus, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, type DatePickerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, Field, type FieldProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, MetricChart, type MetricChartProps, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, type PeriodDelta, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeriesExtent, type SeriesPoint, type SeriesPointInput, type SeriesScale, SeriesSparkline, type SeriesSparklineProps, SettingsList, type SettingsListAction, type SettingsListColumns, SettingsListItem, type SettingsListItemProps, type SettingsListMenuItem, type SettingsListProps, SettingsNote, type SettingsNoteProps, SettingsRow, type SettingsRowLayout, type SettingsRowProps, SettingsSection, type SettingsSectionProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, TimeSelect, type TimeSelectProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, Wizard, type WizardProps, WizardStep, type WizardStepProps, badgeVariants, buttonVariants, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
@@ -43,11 +43,11 @@ import { InlineCode } from "./primitives/code-block.js";
43
43
  import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
44
44
  import { PageShell } from "./primitives/page-shell.js";
45
45
  import { Field, fieldMessageId } from "./primitives/field.js";
46
- import { SettingsList, SettingsListItem, SettingsRow, SettingsSection } from "./primitives/settings.js";
46
+ import { StatusPill } from "./primitives/status-pill.js";
47
+ import { SettingsList, SettingsListItem, SettingsNote, SettingsRow, SettingsSection } from "./primitives/settings.js";
47
48
  import { PageHeader } from "./primitives/page-header.js";
48
49
  import { Alert, AlertDescription, AlertTitle } from "./primitives/alert.js";
49
50
  import { Pagination } from "./primitives/pagination.js";
50
- import { StatusPill } from "./primitives/status-pill.js";
51
51
  import { ComplianceBadge } from "./primitives/compliance-badge.js";
52
52
  import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
53
53
  import { Tag } from "./primitives/tag.js";
@@ -62,4 +62,4 @@ import { SeriesSparkline } from "./primitives/series-sparkline.js";
62
62
  import { MetricChart } from "./primitives/metric-chart.js";
63
63
  import { FilterField, Toolbar } from "./primitives/toolbar.js";
64
64
  import { Logo, TangleKnot } from "@tangle-network/brand";
65
- export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CATEGORY_TONES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, Field, FilterField, Heading, HelpText, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, InlineCode, Input, Label, Logo, Metric, MetricChart, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeriesSparkline, SettingsList, SettingsListItem, SettingsRow, SettingsSection, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, TimeSelect, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, Wizard, WizardStep, badgeVariants, buttonVariants, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
65
+ export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CATEGORY_TONES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, Field, FilterField, Heading, HelpText, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, InlineCode, Input, Label, Logo, Metric, MetricChart, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeriesSparkline, SettingsList, SettingsListItem, SettingsNote, SettingsRow, SettingsSection, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, TimeSelect, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, Wizard, WizardStep, badgeVariants, buttonVariants, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
@@ -1,4 +1,3 @@
1
- import { cn } from "../lib/utils.js";
2
1
  import { CodeBlock } from "../markdown/code-block.js";
3
2
  import { getToolDisplayMetadata } from "../utils/tool-display.js";
4
3
  import { CommandPreview } from "../tool-previews/command-preview.js";
@@ -9,9 +8,10 @@ import { WebSearchPreview } from "../tool-previews/web-search-preview.js";
9
8
  import { QuestionPreview } from "../tool-previews/question-preview.js";
10
9
  import { DiffPreview } from "../tool-previews/diff-preview.js";
11
10
  import { ExpandableContent } from "./expandable-content.js";
11
+ import { RunStatusIcon } from "./status-meta.js";
12
12
  import { memo } from "react";
13
13
  import { jsx, jsxs } from "react/jsx-runtime";
14
- import { AlertCircle, ArrowLeft, ArrowRight, FileText, Loader2 } from "lucide-react";
14
+ import { ArrowLeft, ArrowRight, FileText } from "lucide-react";
15
15
  // src/run/expanded-tool-detail.tsx
16
16
  const FLUSH_TOGGLE = "mx-3 mb-2";
17
17
  const EXT_LANG = {
@@ -144,7 +144,11 @@ const ExpandedToolDetail = memo(({ part }) => {
144
144
  className: "overflow-hidden rounded-[var(--radius-lg)] border border-[var(--surface-danger-border)]",
145
145
  children: [/* @__PURE__ */ jsxs("div", {
146
146
  className: "flex items-center gap-2 border-b border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] px-3 py-2",
147
- children: [/* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 text-[var(--surface-danger-text)]" }), /* @__PURE__ */ jsx("span", {
147
+ children: [/* @__PURE__ */ jsx(RunStatusIcon, {
148
+ status: "failed",
149
+ "aria-hidden": true,
150
+ className: "size-3"
151
+ }), /* @__PURE__ */ jsx("span", {
148
152
  className: "text-xs font-medium uppercase tracking-[0.08em] text-[var(--surface-danger-text)]",
149
153
  children: "Error"
150
154
  })]
@@ -159,7 +163,11 @@ const ExpandedToolDetail = memo(({ part }) => {
159
163
  }),
160
164
  (status === "pending" || status === "running") && /* @__PURE__ */ jsxs("div", {
161
165
  className: "flex items-center gap-2 rounded-[var(--radius-md)] border border-border bg-muted px-3 py-3 text-xs text-muted-foreground",
162
- children: [/* @__PURE__ */ jsx(Loader2, { className: cn("h-3 w-3 animate-spin text-primary") }), "Running…"]
166
+ children: [/* @__PURE__ */ jsx(RunStatusIcon, {
167
+ status: "running",
168
+ "aria-hidden": true,
169
+ className: "size-3"
170
+ }), "Running…"]
163
171
  })
164
172
  ]
165
173
  });
@@ -1,5 +1,6 @@
1
1
  import { cn } from "../lib/utils.js";
2
2
  import { formatDuration } from "../utils/format.js";
3
+ import { RunStatusIcon, statusMeta } from "./status-meta.js";
3
4
  import { InlineToolItem } from "./inline-tool-item.js";
4
5
  import { Markdown } from "../markdown/markdown.js";
5
6
  import { InlineThinkingItem } from "./inline-thinking-item.js";
@@ -8,7 +9,7 @@ import { OpenUIArtifactRenderer } from "../openui/openui-artifact-renderer.js";
8
9
  import { TRANSCRIPT_TEXT } from "../chat/transcript-text.js";
9
10
  import { memo, useMemo } from "react";
10
11
  import { jsx, jsxs } from "react/jsx-runtime";
11
- import { Bot, ChevronDown, ChevronRight, ClipboardList, FileEdit, FileSearch, Globe, Loader2, PencilLine, Search, Settings, Sparkles, Terminal } from "lucide-react";
12
+ import { Bot, ChevronDown, ChevronRight, ClipboardList, FileEdit, FileSearch, Globe, PencilLine, Search, Settings, Terminal } from "lucide-react";
12
13
  import * as Collapsible from "@radix-ui/react-collapsible";
13
14
  // src/run/run-group.tsx
14
15
  /**
@@ -46,7 +47,11 @@ function AssistantShell({ branding, isStreaming, children }) {
46
47
  className: "flex items-center gap-2 px-1 font-medium text-muted-foreground text-xs",
47
48
  children: [/* @__PURE__ */ jsx("span", { children: branding.label }), isStreaming ? /* @__PURE__ */ jsxs("span", {
48
49
  className: "inline-flex items-center gap-1.5",
49
- children: [/* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }), "Thinking"]
50
+ children: [/* @__PURE__ */ jsx(RunStatusIcon, {
51
+ status: "running",
52
+ "aria-hidden": true,
53
+ className: "size-3"
54
+ }), "Thinking"]
50
55
  }) : null]
51
56
  }), /* @__PURE__ */ jsx("div", {
52
57
  className: ASSISTANT_SHELL,
@@ -249,7 +254,10 @@ const RunGroup = memo(({ run, partMap, collapsed, onToggle, branding = DEFAULT_B
249
254
  }, key);
250
255
  if (toolPart.state.status === "running") return /* @__PURE__ */ jsxs("div", {
251
256
  className: "flex items-center gap-2 rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--md3-surface-container)] px-4 py-3 text-sm text-muted-foreground",
252
- children: [/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-primary" }), "Building view…"]
257
+ children: [/* @__PURE__ */ jsx(RunStatusIcon, {
258
+ status: "running",
259
+ "aria-hidden": true
260
+ }), "Building view…"]
253
261
  }, key);
254
262
  }
255
263
  if (part.type === "text" && !part.synthetic && part.text.trim()) return /* @__PURE__ */ jsx("div", {
@@ -302,12 +310,13 @@ const RunGroup = memo(({ run, partMap, collapsed, onToggle, branding = DEFAULT_B
302
310
  }) : null,
303
311
  /* @__PURE__ */ jsxs("span", {
304
312
  className: "ml-auto flex shrink-0 items-center gap-1.5",
305
- children: [/* @__PURE__ */ jsx(CategoryBadges, { categories: stats.toolCategories }), isStreaming ? /* @__PURE__ */ jsxs("span", {
306
- 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)]",
307
- children: [/* @__PURE__ */ jsx(Loader2, { className: "h-2.5 w-2.5 animate-spin" }), "Running"]
308
- }) : /* @__PURE__ */ jsxs("span", {
313
+ children: [/* @__PURE__ */ jsx(CategoryBadges, { categories: stats.toolCategories }), /* @__PURE__ */ jsxs("span", {
309
314
  className: "inline-flex items-center gap-1 rounded-full border border-border px-2 py-px text-[10px] font-semibold uppercase text-muted-foreground",
310
- children: [/* @__PURE__ */ jsx(Sparkles, { className: "h-2.5 w-2.5" }), "Done"]
315
+ children: [/* @__PURE__ */ jsx(RunStatusIcon, {
316
+ status: isStreaming ? "running" : "done",
317
+ "aria-hidden": true,
318
+ className: "size-3"
319
+ }), statusMeta(isStreaming ? "running" : "done").label]
311
320
  })]
312
321
  })
313
322
  ]
@@ -346,7 +355,10 @@ const RunGroup = memo(({ run, partMap, collapsed, onToggle, branding = DEFAULT_B
346
355
  });
347
356
  else if (toolPart.state.status === "running") node = /* @__PURE__ */ jsxs("div", {
348
357
  className: "flex items-center gap-3 rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--md3-surface-container)] px-4 py-3 text-sm text-muted-foreground",
349
- children: [/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-primary" }), "Building view…"]
358
+ children: [/* @__PURE__ */ jsx(RunStatusIcon, {
359
+ status: "running",
360
+ "aria-hidden": true
361
+ }), "Building view…"]
350
362
  });
351
363
  }
352
364
  if (node === null) node = /* @__PURE__ */ jsx(InlineToolItem, {
@@ -1,8 +1,9 @@
1
1
  import { focusRing } from "../lib/focus.js";
2
2
  import { cn } from "../lib/utils.js";
3
+ import { RunStatusIcon } from "./status-meta.js";
3
4
  import { useState } from "react";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { ChevronRight, CircleAlert, Loader2 } from "lucide-react";
6
+ import { ChevronRight } from "lucide-react";
6
7
  // src/run/run-phase-list.tsx
7
8
  function PhaseRow({ phase }) {
8
9
  const [open, setOpen] = useState(false);
@@ -28,13 +29,9 @@ function PhaseRow({ phase }) {
28
29
  children: [" · ", phase.summary]
29
30
  })]
30
31
  }),
31
- phase.status === "running" && /* @__PURE__ */ jsx(Loader2, {
32
- "aria-label": "Running",
33
- className: "mt-1 size-4 shrink-0 animate-spin text-muted-foreground"
34
- }),
35
- phase.status === "error" && /* @__PURE__ */ jsx(CircleAlert, {
36
- "aria-label": "Had an error",
37
- className: "mt-1 size-4 shrink-0 text-[var(--surface-danger-text)]"
32
+ phase.status && phase.status !== "done" && /* @__PURE__ */ jsx(RunStatusIcon, {
33
+ status: phase.status,
34
+ className: "mt-1"
38
35
  })
39
36
  ]
40
37
  }), open && /* @__PURE__ */ jsx("div", {
@@ -0,0 +1,25 @@
1
+ import { LucideIcon, LucideProps } from "lucide-react";
2
+ /** The states an agent or a run reports. Every surface names them with `statusMeta`. */
3
+ export type RunStatus = "running" | "waiting" | "needs-you" | "done" | "failed" | "stopped" | "idle" | "unknown";
4
+ export interface StatusMeta {
5
+ /** The one word for the state. A qualifier such as "Shipped" follows Done; it never replaces it. */
6
+ label: string;
7
+ /** The lucide glyph, drawn on the 16px grid at stroke 1.75 (`RunStatusIcon` does both). */
8
+ icon: LucideIcon;
9
+ /** The Brand colour token for the glyph and any bare status text: `color: var(<token>)`. */
10
+ token: `--${string}`;
11
+ }
12
+ /** Any status string as a `RunStatus`; a value nothing names reads as Unknown, never as a guess. */
13
+ export declare function runStatus(status: string): RunStatus;
14
+ /** The label, glyph and colour token for a status, in every surface. */
15
+ export declare function statusMeta(status: RunStatus | (string & {})): StatusMeta;
16
+ export interface RunStatusIconProps extends Omit<LucideProps, "ref"> {
17
+ status: RunStatus | (string & {});
18
+ }
19
+ /**
20
+ * A status glyph on the icon grid, in its token colour and labelled with its
21
+ * word. Pass `aria-hidden` where the label is already printed beside it.
22
+ * Running breathes on `--motion-ambient`, the page's one loop period; the
23
+ * breathe is opacity only, so it is the essential motion reduced motion keeps.
24
+ */
25
+ export declare function RunStatusIcon({ status, className, style, ...props }: RunStatusIconProps): import("react").JSX.Element;
@@ -0,0 +1,105 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { CircleCheck, CircleDashed, CircleDot, CircleQuestionMark, CircleStop, CircleX, Hand, Hourglass } from "lucide-react";
4
+ // src/run/status-meta.tsx
5
+ const META = {
6
+ running: {
7
+ label: "Running",
8
+ icon: CircleDot,
9
+ token: "--surface-info-text"
10
+ },
11
+ waiting: {
12
+ label: "Waiting",
13
+ icon: Hourglass,
14
+ token: "--surface-neutral-text"
15
+ },
16
+ "needs-you": {
17
+ label: "Needs you",
18
+ icon: Hand,
19
+ token: "--surface-warning-text"
20
+ },
21
+ done: {
22
+ label: "Done",
23
+ icon: CircleCheck,
24
+ token: "--surface-success-text"
25
+ },
26
+ failed: {
27
+ label: "Failed",
28
+ icon: CircleX,
29
+ token: "--surface-danger-text"
30
+ },
31
+ stopped: {
32
+ label: "Stopped",
33
+ icon: CircleStop,
34
+ token: "--surface-neutral-text"
35
+ },
36
+ idle: {
37
+ label: "Idle",
38
+ icon: CircleDashed,
39
+ token: "--surface-neutral-text"
40
+ },
41
+ unknown: {
42
+ label: "Unknown",
43
+ icon: CircleQuestionMark,
44
+ token: "--surface-neutral-text"
45
+ }
46
+ };
47
+ const ALIASES = {
48
+ active: "running",
49
+ in_progress: "running",
50
+ "in-progress": "running",
51
+ streaming: "running",
52
+ pending: "waiting",
53
+ queued: "waiting",
54
+ blocked: "waiting",
55
+ "attention-needed": "needs-you",
56
+ needs_you: "needs-you",
57
+ needs_input: "needs-you",
58
+ awaiting_input: "needs-you",
59
+ completed: "done",
60
+ complete: "done",
61
+ success: "done",
62
+ succeeded: "done",
63
+ shipped: "done",
64
+ error: "failed",
65
+ errored: "failed",
66
+ cancelled: "stopped",
67
+ canceled: "stopped",
68
+ aborted: "stopped"
69
+ };
70
+ /** Any status string as a `RunStatus`; a value nothing names reads as Unknown, never as a guess. */
71
+ function runStatus(status) {
72
+ const key = status.trim().toLowerCase();
73
+ if (Object.hasOwn(META, key)) return key;
74
+ return Object.hasOwn(ALIASES, key) ? ALIASES[key] : "unknown";
75
+ }
76
+ /** The label, glyph and colour token for a status, in every surface. */
77
+ function statusMeta(status) {
78
+ return META[runStatus(status)];
79
+ }
80
+ /**
81
+ * A status glyph on the icon grid, in its token colour and labelled with its
82
+ * word. Pass `aria-hidden` where the label is already printed beside it.
83
+ * Running breathes on `--motion-ambient`, the page's one loop period; the
84
+ * breathe is opacity only, so it is the essential motion reduced motion keeps.
85
+ */
86
+ function RunStatusIcon({ status, className, style, ...props }) {
87
+ const key = runStatus(status);
88
+ const { label, icon: Icon, token } = META[key];
89
+ const running = key === "running";
90
+ return /* @__PURE__ */ jsx(Icon, {
91
+ role: "img",
92
+ "aria-label": label,
93
+ size: 16,
94
+ strokeWidth: 1.75,
95
+ "data-run-status": key,
96
+ "data-motion": running ? "essential" : void 0,
97
+ className: cn("shrink-0", running && "animate-[tangle-breathe_var(--motion-ambient)_infinite]", className),
98
+ style: {
99
+ color: `var(${token})`,
100
+ ...style
101
+ },
102
+ ...props
103
+ });
104
+ }
105
+ export { RunStatusIcon, runStatus, statusMeta };
package/dist/run.d.ts CHANGED
@@ -9,4 +9,5 @@ import { LiveDuration } from "./run/run-item-primitives.js";
9
9
  import { TaskList, TaskListItem, TaskListProps, TaskStatus, showsTaskPriority, taskProgress, taskStatus } from "./run/task-list.js";
10
10
  import { ApprovalCard, ApprovalCardProps, ApprovalDiffFile, ApprovalDiffSummary, ApprovalPlanStep } from "./run/approval-card.js";
11
11
  import { RunPhase, RunPhaseList } from "./run/run-phase-list.js";
12
- export { ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps, type RunPhase, RunPhaseList, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, parseToolEvent, showsTaskPriority, taskProgress, taskStatus };
12
+ import { RunStatus, RunStatusIcon, RunStatusIconProps, StatusMeta, runStatus, statusMeta } from "./run/status-meta.js";
13
+ export { ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps, type RunPhase, RunPhaseList, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, type RunStatus, RunStatusIcon, type RunStatusIconProps, type StatusMeta, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, parseToolEvent, runStatus, showsTaskPriority, statusMeta, taskProgress, taskStatus };
package/dist/run.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { RunStatusIcon, runStatus, statusMeta } from "./run/status-meta.js";
1
2
  import { ExpandedToolDetail } from "./run/expanded-tool-detail.js";
2
3
  import { RunRowShell, RunRowStatusDot } from "./run/run-row-shell.js";
3
4
  import { InlineToolItem } from "./run/inline-tool-item.js";
@@ -8,4 +9,4 @@ import { ToolCallFeed, parseToolEvent } from "./run/tool-call-feed.js";
8
9
  import { TaskList, showsTaskPriority, taskProgress, taskStatus } from "./run/task-list.js";
9
10
  import { ApprovalCard, ApprovalDiffSummary } from "./run/approval-card.js";
10
11
  import { RunPhaseList } from "./run/run-phase-list.js";
11
- export { ApprovalCard, ApprovalDiffSummary, ExpandedToolDetail, InlineThinkingItem, InlineToolItem, LiveDuration, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, TaskList, ToolCallFeed, parseToolEvent, showsTaskPriority, taskProgress, taskStatus };
12
+ export { ApprovalCard, ApprovalDiffSummary, ExpandedToolDetail, InlineThinkingItem, InlineToolItem, LiveDuration, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, RunStatusIcon, TaskList, ToolCallFeed, parseToolEvent, runStatus, showsTaskPriority, statusMeta, taskProgress, taskStatus };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.48.0",
3
+ "version": "11.50.1",
4
4
  "description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -166,7 +166,7 @@
166
166
  "peerDependencies": {
167
167
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
168
168
  "@nanostores/react": ">=0.8.4 <3.0.0",
169
- "@tangle-network/brand": "^1.29.0",
169
+ "@tangle-network/brand": "^1.31.0",
170
170
  "@tiptap/core": ">=3.20.4 <4.0.0",
171
171
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
172
172
  "@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
@@ -307,3 +307,12 @@ describe("transcript type scale", () => {
307
307
  expect(textClasses(agent)).toEqual(textClasses(user))
308
308
  })
309
309
  })
310
+
311
+ describe("AgentTimeline status cards", () => {
312
+ it("marks a failed card with the failed glyph and keeps the triangle for warnings", () => {
313
+ const glyph = (tone: "error" | "warning") =>
314
+ render(<AgentTimeline items={[{ id: tone, kind: "status", label: tone, tone }]} />).container.querySelector("svg")?.getAttribute("class")
315
+ expect(glyph("error")).toContain("lucide-circle-x")
316
+ expect(glyph("warning")).toContain("lucide-triangle-alert")
317
+ })
318
+ })