@tangle-network/ui 11.2.4 → 11.3.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,19 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e67a809: Raise the dark surface ladder to AA and add the page-level primitives.
8
+
9
+ In the dark spine, `--md3-surface-container-high` and `--md3-surface-container-highest` sat close enough to the ink ramp that `--text-dim` fell under 4.5:1 on them. The ladder is re-spaced and the ink ramp moves with it, so every ink tier clears AA on all five planes.
10
+
11
+ Dark status chips keep their hue, drop 14% chroma, and lift the fill to 1.50:1 from the card. A new `--run-mix-*` ramp carries proportional bars, spaced in relative luminance so adjacent segments clear the 3:1 floor for non-text contrast.
12
+
13
+ One light token moves: `--surface-warning-text` goes `#b45309` to `#ab4f09`. It is a contrast fix, not a hue change. The old value measured 4.16:1 on the light page canvas, under the 4.5:1 body floor, whenever the colour was used as text away from its own pill background; the new value measures 4.51:1 there and 5.25:1 on the pill. Every other light value is unchanged.
14
+
15
+ `@tangle-network/ui` gains four primitives: `PageHeader`, `StatusPill`, `MetricStrip`, and `Toolbar`/`FilterField`. All additive; no existing export changes.
16
+
3
17
  ## 11.2.4
4
18
 
5
19
  ### Patch Changes
@@ -1261,6 +1261,275 @@ function ThemeToggle() {
1261
1261
  );
1262
1262
  }
1263
1263
 
1264
+ // src/primitives/page-header.tsx
1265
+ import * as React12 from "react";
1266
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1267
+ var PageHeader = React12.forwardRef(
1268
+ ({
1269
+ className,
1270
+ title,
1271
+ description,
1272
+ actions,
1273
+ meta,
1274
+ titleId,
1275
+ level = 1,
1276
+ ...props
1277
+ }, ref) => {
1278
+ const Heading = level === 1 ? "h1" : "h2";
1279
+ return /* @__PURE__ */ jsxs15("header", { ref, className: cn("mb-6", className), ...props, children: [
1280
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-start justify-between gap-x-6 gap-y-3", children: [
1281
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
1282
+ /* @__PURE__ */ jsx17(
1283
+ Heading,
1284
+ {
1285
+ id: titleId,
1286
+ className: cn(
1287
+ "text-balance font-semibold tracking-tight",
1288
+ level === 1 ? "text-2xl" : "text-lg"
1289
+ ),
1290
+ children: title
1291
+ }
1292
+ ),
1293
+ description && /* @__PURE__ */ jsx17("p", { className: "mt-1 max-w-prose text-muted-foreground text-sm", children: description })
1294
+ ] }),
1295
+ actions && /* @__PURE__ */ jsx17("div", { className: "flex flex-wrap items-center gap-2", children: actions })
1296
+ ] }),
1297
+ meta && /* @__PURE__ */ jsx17("div", { className: "mt-3 flex flex-wrap items-center gap-x-4 gap-y-2 text-[var(--text-dim)] text-xs", children: meta })
1298
+ ] });
1299
+ }
1300
+ );
1301
+ PageHeader.displayName = "PageHeader";
1302
+
1303
+ // src/primitives/status-pill.tsx
1304
+ import * as React13 from "react";
1305
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1306
+ var TONE_SURFACE = {
1307
+ success: "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] border-[var(--surface-success-border)]",
1308
+ warning: "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)] border-[var(--surface-warning-border)]",
1309
+ danger: "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)] border-[var(--surface-danger-border)]",
1310
+ info: "bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] border-[var(--surface-info-border)]",
1311
+ // `running` shares the info triple on purpose. `--status-running` exists but
1312
+ // is the green dot on a sandbox — a machine's power state, not a run's
1313
+ // outcome — and it ships as one colour with no paired background or text
1314
+ // tier, which is the pairing this component is built to guarantee. Green also
1315
+ // already means SUCCEEDED here, so a green "running" pill would say a run had
1316
+ // finished well while it was still going.
1317
+ running: "bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] border-[var(--surface-info-border)]",
1318
+ neutral: "bg-[var(--surface-neutral-bg)] text-[var(--surface-neutral-text)] border-[var(--surface-neutral-border)]"
1319
+ };
1320
+ var TONE_TEXT = {
1321
+ success: "text-[var(--surface-success-text)]",
1322
+ warning: "text-[var(--surface-warning-text)]",
1323
+ danger: "text-[var(--surface-danger-text)]",
1324
+ info: "text-[var(--surface-info-text)]",
1325
+ running: "text-[var(--surface-info-text)]",
1326
+ neutral: "text-[var(--surface-neutral-text)]"
1327
+ };
1328
+ function ToneGlyph({ tone }) {
1329
+ const common = {
1330
+ viewBox: "0 0 8 8",
1331
+ className: "size-2 shrink-0"
1332
+ };
1333
+ switch (tone) {
1334
+ // Settled and good: a solid disc.
1335
+ case "success":
1336
+ return /* @__PURE__ */ jsx18("svg", { ...common, "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx18("circle", { cx: "4", cy: "4", r: "3.25" }) });
1337
+ // Settled and bad: a disc with a bar through it, so it differs from
1338
+ // success in OUTLINE and not only in hue.
1339
+ case "danger":
1340
+ return /* @__PURE__ */ jsxs16(
1341
+ "svg",
1342
+ {
1343
+ ...common,
1344
+ "aria-hidden": "true",
1345
+ fill: "none",
1346
+ stroke: "currentColor",
1347
+ strokeWidth: "1.5",
1348
+ children: [
1349
+ /* @__PURE__ */ jsx18("circle", { cx: "4", cy: "4", r: "3.1", fill: "currentColor", opacity: "0.35" }),
1350
+ /* @__PURE__ */ jsx18("path", { d: "M1.9 6.1 6.1 1.9", strokeLinecap: "round" })
1351
+ ]
1352
+ }
1353
+ );
1354
+ // Needs a person: a triangle.
1355
+ case "warning":
1356
+ return /* @__PURE__ */ jsx18("svg", { ...common, "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx18("path", { d: "M4 0.6 7.7 7.1H0.3z" }) });
1357
+ // Still moving: an open ring, visibly hollow at 8px.
1358
+ case "running":
1359
+ return /* @__PURE__ */ jsx18(
1360
+ "svg",
1361
+ {
1362
+ ...common,
1363
+ "aria-hidden": "true",
1364
+ fill: "none",
1365
+ stroke: "currentColor",
1366
+ strokeWidth: "1.75",
1367
+ children: /* @__PURE__ */ jsx18("circle", { cx: "4", cy: "4", r: "2.9" })
1368
+ }
1369
+ );
1370
+ // Informational: a square, the only right-angled mark in the set.
1371
+ case "info":
1372
+ return /* @__PURE__ */ jsx18("svg", { ...common, "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx18("rect", { x: "0.9", y: "0.9", width: "6.2", height: "6.2", rx: "1.2" }) });
1373
+ // Nothing has happened: a dash. No enclosed area at all.
1374
+ default:
1375
+ return /* @__PURE__ */ jsx18(
1376
+ "svg",
1377
+ {
1378
+ ...common,
1379
+ "aria-hidden": "true",
1380
+ fill: "none",
1381
+ stroke: "currentColor",
1382
+ strokeWidth: "1.75",
1383
+ children: /* @__PURE__ */ jsx18("path", { d: "M1 4h6", strokeLinecap: "round" })
1384
+ }
1385
+ );
1386
+ }
1387
+ }
1388
+ var StatusPill = React13.forwardRef(
1389
+ ({ className, tone, children, bare = false, size = "sm", ...props }, ref) => /* @__PURE__ */ jsxs16(
1390
+ "span",
1391
+ {
1392
+ ref,
1393
+ className: cn(
1394
+ "inline-flex items-center gap-1.5 whitespace-nowrap rounded-full font-medium",
1395
+ size === "sm" ? "px-2 py-0.5 text-xs" : "px-2.5 py-1 text-sm",
1396
+ bare ? "border border-transparent bg-transparent px-0" : cn("border", TONE_SURFACE[tone]),
1397
+ className
1398
+ ),
1399
+ ...props,
1400
+ children: [
1401
+ /* @__PURE__ */ jsx18("span", { className: cn("inline-flex", bare && TONE_TEXT[tone]), children: /* @__PURE__ */ jsx18(ToneGlyph, { tone }) }),
1402
+ children
1403
+ ]
1404
+ }
1405
+ )
1406
+ );
1407
+ StatusPill.displayName = "StatusPill";
1408
+
1409
+ // src/primitives/metric-strip.tsx
1410
+ import * as React14 from "react";
1411
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
1412
+ var MetricStrip = React14.forwardRef(
1413
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx19(
1414
+ "dl",
1415
+ {
1416
+ ref,
1417
+ className: cn(
1418
+ "grid grid-cols-2 rounded-[var(--radius-lg)] border border-border bg-card shadow-[var(--shadow-card)] sm:grid-cols-4",
1419
+ className
1420
+ ),
1421
+ ...props,
1422
+ children
1423
+ }
1424
+ )
1425
+ );
1426
+ MetricStrip.displayName = "MetricStrip";
1427
+ var Metric = React14.forwardRef(
1428
+ ({ className, label, value, hint, attention, ...props }, ref) => /* @__PURE__ */ jsxs17(
1429
+ "div",
1430
+ {
1431
+ ref,
1432
+ className: cn(
1433
+ // Leading hairline, DRAWN only where a divider belongs — never drawn
1434
+ // and then suppressed. Stating it as `border-l` plus `border-l-0`
1435
+ // variants puts both in one conflict group, where the winner is decided
1436
+ // by emitted-rule order rather than by intent; that resolved the wrong
1437
+ // way at every breakpoint and left a rule down the start of each row.
1438
+ // `:not(:nth-child(Nn+1))` is the row-start test for the column count
1439
+ // at that breakpoint, so no rule ever contradicts another.
1440
+ //
1441
+ // N is COUPLED to the grid template on `MetricStrip`: 2 columns below
1442
+ // `sm`, 4 from `sm`. A new breakpoint there — `md:grid-cols-3`, say —
1443
+ // needs its `md:[&:not(:nth-child(3n+1))]:border-l` here in the same
1444
+ // change, or the dividers land mid-row at that width.
1445
+ "border-border p-4 sm:p-5",
1446
+ "max-sm:[&:not(:nth-child(2n+1))]:border-l",
1447
+ "sm:[&:not(:nth-child(4n+1))]:border-l",
1448
+ className
1449
+ ),
1450
+ ...props,
1451
+ children: [
1452
+ /* @__PURE__ */ jsxs17("dt", { className: "flex items-center gap-2 text-muted-foreground text-sm", children: [
1453
+ /* @__PURE__ */ jsx19("span", { className: "truncate", children: label }),
1454
+ attention && /* @__PURE__ */ jsx19(StatusPill, { tone: attention.tone, children: attention.label })
1455
+ ] }),
1456
+ /* @__PURE__ */ jsx19(
1457
+ "dd",
1458
+ {
1459
+ className: cn(
1460
+ "mt-1 truncate font-semibold text-xl tabular-nums tracking-tight lg:text-2xl",
1461
+ attention?.tone === "danger" && "text-[var(--surface-danger-text)]"
1462
+ ),
1463
+ title: typeof value === "string" ? value : void 0,
1464
+ children: value
1465
+ }
1466
+ ),
1467
+ hint && // Titled for the same reason the value is: the line truncates, and a
1468
+ // hint is where the qualifying detail lives — which wallet, which
1469
+ // window — so a clipped one is the case most worth recovering.
1470
+ /* @__PURE__ */ jsx19(
1471
+ "dd",
1472
+ {
1473
+ className: "mt-0.5 truncate text-[var(--text-dim)] text-xs",
1474
+ title: typeof hint === "string" ? hint : void 0,
1475
+ children: hint
1476
+ }
1477
+ )
1478
+ ]
1479
+ }
1480
+ )
1481
+ );
1482
+ Metric.displayName = "Metric";
1483
+
1484
+ // src/primitives/toolbar.tsx
1485
+ import * as React15 from "react";
1486
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
1487
+ var Toolbar = React15.forwardRef(
1488
+ ({ className, search, filters, actions, ...props }, ref) => /* @__PURE__ */ jsxs18(
1489
+ "div",
1490
+ {
1491
+ ref,
1492
+ className: cn(
1493
+ "mb-4 flex flex-col gap-3 lg:flex-row lg:items-center",
1494
+ className
1495
+ ),
1496
+ ...props,
1497
+ children: [
1498
+ search && /* @__PURE__ */ jsx20("div", { className: "min-w-0 lg:max-w-sm lg:flex-1", children: search }),
1499
+ filters && // `py-1` leaves room for a focus ring above and below; horizontally the
1500
+ // scroll container clips it, which is the trade a single-line filter row
1501
+ // makes. No negative margin — pulling the row wider than its parent is
1502
+ // what made every page report 4px of horizontal overflow.
1503
+ /* @__PURE__ */ jsx20("div", { className: "flex min-w-0 items-center gap-2 overflow-x-auto py-1 lg:flex-1", children: filters }),
1504
+ actions && /* @__PURE__ */ jsx20("div", { className: "flex shrink-0 items-center gap-2 lg:ml-auto", children: actions })
1505
+ ]
1506
+ }
1507
+ )
1508
+ );
1509
+ Toolbar.displayName = "Toolbar";
1510
+ var FilterField = React15.forwardRef(
1511
+ ({ className, label, htmlFor, children, ...props }, ref) => /* @__PURE__ */ jsxs18(
1512
+ "div",
1513
+ {
1514
+ ref,
1515
+ className: cn("flex shrink-0 items-center gap-2", className),
1516
+ ...props,
1517
+ children: [
1518
+ /* @__PURE__ */ jsx20(
1519
+ "label",
1520
+ {
1521
+ htmlFor,
1522
+ className: "whitespace-nowrap text-muted-foreground text-xs",
1523
+ children: label
1524
+ }
1525
+ ),
1526
+ children
1527
+ ]
1528
+ }
1529
+ )
1530
+ );
1531
+ FilterField.displayName = "FilterField";
1532
+
1264
1533
  export {
1265
1534
  Dialog,
1266
1535
  DialogTrigger,
@@ -1305,6 +1574,12 @@ export {
1305
1574
  SidebarDropZone,
1306
1575
  useTheme,
1307
1576
  ThemeToggle,
1577
+ PageHeader,
1578
+ StatusPill,
1579
+ MetricStrip,
1580
+ Metric,
1581
+ Toolbar,
1582
+ FilterField,
1308
1583
  Logo,
1309
1584
  TangleKnot
1310
1585
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { B as Button, a as ButtonProps, b as buttonVariants } from './button-CMQuQEW_.js';
2
- export { Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EmptyStateProps, InlineCode, InlineCodeProps, Input, InputProps, Label, Progress, SegmentedControl, SegmentedControlOption, SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, StatCard, StatCardProps, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, Textarea, TextareaProps, ThemeToggle, Toast, ToastContainer, ToastProvider, UploadFile, UploadProgress, UploadProgressProps, badgeVariants, useTheme, useToast } from './primitives.js';
2
+ export { Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EmptyStateProps, FilterField, FilterFieldProps, InlineCode, InlineCodeProps, Input, InputProps, Label, Metric, MetricProps, MetricStrip, MetricStripProps, PageHeader, PageHeaderProps, Progress, SegmentedControl, SegmentedControlOption, SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, StatCard, StatCardProps, StatusPill, StatusPillProps, StatusTone, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, Textarea, TextareaProps, ThemeToggle, Toast, ToastContainer, ToastProvider, Toolbar, ToolbarProps, UploadFile, UploadProgress, UploadProgressProps, badgeVariants, useTheme, useToast } from './primitives.js';
3
3
  export { Logo, LogoProps, TangleKnot } from '@tangle-network/brand';
4
4
  export { A as ArtifactPane, a as ArtifactPaneProps } from './artifact-pane-DpuWcAiF.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';
package/dist/index.js CHANGED
@@ -76,9 +76,13 @@ import {
76
76
  DialogTrigger,
77
77
  DropZone,
78
78
  EmptyState,
79
+ FilterField,
79
80
  Input,
80
81
  Label,
81
82
  Logo,
83
+ Metric,
84
+ MetricStrip,
85
+ PageHeader,
82
86
  Progress,
83
87
  SegmentedControl,
84
88
  Select,
@@ -96,6 +100,7 @@ import {
96
100
  SkeletonCard,
97
101
  SkeletonTable,
98
102
  StatCard,
103
+ StatusPill,
99
104
  Switch,
100
105
  TangleKnot,
101
106
  TerminalCursor,
@@ -106,10 +111,11 @@ import {
106
111
  ThemeToggle,
107
112
  ToastContainer,
108
113
  ToastProvider,
114
+ Toolbar,
109
115
  UploadProgress,
110
116
  useTheme,
111
117
  useToast
112
- } from "./chunk-HSMVDKL3.js";
118
+ } from "./chunk-EJ5NBW7V.js";
113
119
  import {
114
120
  Avatar,
115
121
  AvatarFallback,
@@ -388,6 +394,7 @@ export {
388
394
  FilePreview,
389
395
  FileTabs,
390
396
  FileTree,
397
+ FilterField,
391
398
  GitHubLoginButton,
392
399
  GlobResultsPreview,
393
400
  GrepResultsPreview,
@@ -401,7 +408,10 @@ export {
401
408
  Logo,
402
409
  Markdown,
403
410
  MessageList,
411
+ Metric,
412
+ MetricStrip,
404
413
  OpenUIArtifactRenderer,
414
+ PageHeader,
405
415
  Progress,
406
416
  QuestionPreview,
407
417
  RealtimeSessionRegistry,
@@ -424,6 +434,7 @@ export {
424
434
  SkeletonCard,
425
435
  SkeletonTable,
426
436
  StatCard,
437
+ StatusPill,
427
438
  Switch,
428
439
  TOOL_CATEGORY_ICONS,
429
440
  Table,
@@ -449,6 +460,7 @@ export {
449
460
  ToastContainer,
450
461
  ToastProvider,
451
462
  ToolCallFeed,
463
+ Toolbar,
452
464
  UploadProgress,
453
465
  UserMenu,
454
466
  UserMessage,
@@ -336,4 +336,174 @@ declare function useTheme(): {
336
336
  };
337
337
  declare function ThemeToggle(): react_jsx_runtime.JSX.Element;
338
338
 
339
- export { Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, 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, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Progress, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, StatCard, type StatCardProps, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, Textarea, type TextareaProps, ThemeToggle, type Toast, ToastContainer, ToastProvider, type UploadFile, UploadProgress, type UploadProgressProps, badgeVariants, useTheme, useToast };
339
+ /**
340
+ * The one masthead every console page opens with.
341
+ *
342
+ * A page identifies itself in three separate registers, and each gets its own
343
+ * slot here so they cannot compete for the same one:
344
+ *
345
+ * - `title` — what the page IS. The `<h1>`, and the only `<h1>`.
346
+ * - `description` — one line on what it is for. Prose belongs here, never in
347
+ * the title slot: a paragraph rendered where a heading goes reads as body
348
+ * copy to a sighted reader and as nothing at all to a screen reader, which
349
+ * is how a page ends up with no accessible name.
350
+ * - `actions` — what the reader can DO from here, right-aligned and wrapping
351
+ * under the title on a narrow viewport rather than squeezing it.
352
+ *
353
+ * `meta` is the fourth register and the one most often missing: the small facts
354
+ * that qualify everything below (a count, a scope, a window). They sit on their
355
+ * own line under the title so a reader can tell the difference between "this
356
+ * page has nothing in it" and "this filter matches nothing".
357
+ *
358
+ * `titleId` is exposed so a page can point `aria-labelledby` at the heading
359
+ * from a region further down without minting a second copy of the string.
360
+ */
361
+ interface PageHeaderProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "title"> {
362
+ title: React$1.ReactNode;
363
+ description?: React$1.ReactNode;
364
+ actions?: React$1.ReactNode;
365
+ meta?: React$1.ReactNode;
366
+ titleId?: string;
367
+ /** Renders the title one step down for a nested/tab surface. */
368
+ level?: 1 | 2;
369
+ }
370
+ declare const PageHeader: React$1.ForwardRefExoticComponent<PageHeaderProps & React$1.RefAttributes<HTMLElement>>;
371
+
372
+ /**
373
+ * A run/resource state, told three ways at once.
374
+ *
375
+ * Status is the one place a console cannot afford to speak in colour alone: a
376
+ * red dot and a green dot are the same dot to roughly one man in twelve, and
377
+ * identical in a greyscale print or a screenshot pasted into a ticket. So every
378
+ * pill carries all three channels — a GLYPH whose silhouette differs per tone,
379
+ * the tone's COLOUR, and the state's own LABEL as text.
380
+ *
381
+ * The glyphs are chosen to survive at 8px and to differ in outline rather than
382
+ * in fill: a ring reads as "still open", a solid disc as "settled", a slashed
383
+ * disc as "stopped". Two states never share one silhouette.
384
+ *
385
+ * Each tone draws its fill, border and text from ONE matched token triple.
386
+ * That pairing is the whole point: a status colour is solved against its own
387
+ * background, and nothing guarantees it against an arbitrary one. A component
388
+ * that always brings its own background cannot be placed onto a plane that
389
+ * breaks it.
390
+ */
391
+ type StatusTone = "success" | "warning" | "danger" | "info" | "neutral" | "running";
392
+ interface StatusPillProps extends Omit<React$1.HTMLAttributes<HTMLSpanElement>, "children"> {
393
+ tone: StatusTone;
394
+ children: React$1.ReactNode;
395
+ /**
396
+ * Drops the fill and border, leaving a toned GLYPH beside a label in the
397
+ * inherited body colour.
398
+ *
399
+ * The label deliberately does not keep the tone. Every status text token
400
+ * clears the 4.5:1 body floor on the page canvas — a gate holds them there —
401
+ * but in light the margin is thin (4.51:1 for warning, 4.54:1 for success),
402
+ * and the canvas is only one of the planes a caller can put a pill on. The
403
+ * glyph keeps the tone instead because a glyph is non-text content against a
404
+ * 3:1 floor, so the tone still reads with room to spare on any plane.
405
+ *
406
+ * For a control that supplies its own surface (a chip, a selected row).
407
+ */
408
+ bare?: boolean;
409
+ size?: "sm" | "md";
410
+ }
411
+ declare const StatusPill: React$1.ForwardRefExoticComponent<StatusPillProps & React$1.RefAttributes<HTMLSpanElement>>;
412
+
413
+ /**
414
+ * The headline figures for a console page, as ONE statement rather than four.
415
+ *
416
+ * A row of separate stat cards is the default and it is usually wrong here.
417
+ * Each card is a box that must be tall enough for its longest member, so a row
418
+ * mixing "$248.55 / Personal wallet" against a bare "0" leaves the short ones
419
+ * mostly empty; and four bordered boxes read as four unrelated facts when they
420
+ * are four readings of one account. `MetricStrip` puts them on one plane
421
+ * divided by hairlines: same information, one object, no holes.
422
+ *
423
+ * The divider is a border on the item rather than a `divide-*` utility on the
424
+ * parent, because these wrap. `divide-x` draws from DOM order and leaves a
425
+ * stray rule at the start of every wrapped line; a per-item leading border
426
+ * suppressed at each row start does not.
427
+ */
428
+ interface MetricStripProps extends React$1.HTMLAttributes<HTMLDListElement> {
429
+ children: React$1.ReactNode;
430
+ }
431
+ declare const MetricStrip: React$1.ForwardRefExoticComponent<MetricStripProps & React$1.RefAttributes<HTMLDListElement>>;
432
+ interface MetricProps extends React$1.HTMLAttributes<HTMLDivElement> {
433
+ label: React$1.ReactNode;
434
+ value: React$1.ReactNode;
435
+ /** The qualifying line: which wallet, which window, what the limit is. */
436
+ hint?: React$1.ReactNode;
437
+ /** Raises the value's tone and shows a pill beside the label. A metric is
438
+ * `attention` only when a person has to DO something — not merely when a
439
+ * number is zero. */
440
+ attention?: {
441
+ tone: StatusTone;
442
+ label: string;
443
+ };
444
+ }
445
+ /**
446
+ * One reading inside a `MetricStrip`.
447
+ *
448
+ * `<dt>` carries the label and `<dd>` the value, so the pair is announced as a
449
+ * described term rather than as two loose strings — the semantic that makes a
450
+ * figure legible without the visual grouping.
451
+ *
452
+ * The value is `tabular-nums`: these sit in a row and change on a timer, and
453
+ * proportional digits make the column jitter every time a 1 becomes an 8.
454
+ */
455
+ declare const Metric: React$1.ForwardRefExoticComponent<MetricProps & React$1.RefAttributes<HTMLDivElement>>;
456
+
457
+ /**
458
+ * The control row above a table or list: search, filters, then the view switch.
459
+ *
460
+ * It exists because the alternative keeps producing the same two failures. A
461
+ * bare flex row gives every control its natural width, so a search field sits
462
+ * at 1120px on one line and three selects stack full-width beneath it — three
463
+ * enormous empty bars where a compact bar belongs. And each control brought its
464
+ * own visual language, so one row carried a native `<select>`, a custom input
465
+ * and a segmented control side by side.
466
+ *
467
+ * So search and filters SHARE the free space and `actions` stays at its content
468
+ * width, and the row is the one place those controls are composed, which is what
469
+ * keeps them looking like one set.
470
+ *
471
+ * Sharing is deliberate, and the alternative is worse. Giving the filter row its
472
+ * content width starves search: measured with a 537px filter row in an 832px
473
+ * toolbar, the search field collapses to 89px. Letting both flex bounds the
474
+ * filter row instead, so it scrolls within its half — 313px each in that same
475
+ * case — and search stays usable however many filters a page adds. `max-w-sm`
476
+ * then stops search claiming more than it can use when there are few filters.
477
+ *
478
+ * On a narrow viewport the row becomes a column and the filters scroll
479
+ * horizontally as a group rather than wrapping into a tall stack that pushes
480
+ * the table itself below the fold.
481
+ */
482
+ /**
483
+ * Slots only — `children` is omitted deliberately. A free child would render as
484
+ * a bare flex item with none of the `min-w-0` / `shrink-0` guards the named
485
+ * slots carry, so it would size itself off its content and push the row into
486
+ * the overflow this layout exists to prevent.
487
+ */
488
+ interface ToolbarProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"> {
489
+ /** The one control that expands to fill the row. */
490
+ search?: React$1.ReactNode;
491
+ /** Filters, in reading order. Kept on one scrollable line when space runs out. */
492
+ filters?: React$1.ReactNode;
493
+ /** View switches and exports, pinned to the end. */
494
+ actions?: React$1.ReactNode;
495
+ }
496
+ declare const Toolbar: React$1.ForwardRefExoticComponent<ToolbarProps & React$1.RefAttributes<HTMLDivElement>>;
497
+ /**
498
+ * A labelled filter control. The label is visible, not a placeholder: a select
499
+ * whose current value IS its label ("All products") tells the reader what is
500
+ * selected but never what the control governs, so a row of three reads as three
501
+ * unrelated words.
502
+ */
503
+ interface FilterFieldProps extends React$1.HTMLAttributes<HTMLDivElement> {
504
+ label: string;
505
+ htmlFor?: string;
506
+ }
507
+ declare const FilterField: React$1.ForwardRefExoticComponent<FilterFieldProps & React$1.RefAttributes<HTMLDivElement>>;
508
+
509
+ export { Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, 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, FilterField, type FilterFieldProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Metric, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, Progress, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, Textarea, type TextareaProps, ThemeToggle, type Toast, ToastContainer, ToastProvider, Toolbar, type ToolbarProps, type UploadFile, UploadProgress, type UploadProgressProps, badgeVariants, useTheme, useToast };
@@ -11,9 +11,13 @@ import {
11
11
  DialogTrigger,
12
12
  DropZone,
13
13
  EmptyState,
14
+ FilterField,
14
15
  Input,
15
16
  Label,
16
17
  Logo,
18
+ Metric,
19
+ MetricStrip,
20
+ PageHeader,
17
21
  Progress,
18
22
  SegmentedControl,
19
23
  Select,
@@ -31,6 +35,7 @@ import {
31
35
  SkeletonCard,
32
36
  SkeletonTable,
33
37
  StatCard,
38
+ StatusPill,
34
39
  Switch,
35
40
  TangleKnot,
36
41
  TerminalCursor,
@@ -41,10 +46,11 @@ import {
41
46
  ThemeToggle,
42
47
  ToastContainer,
43
48
  ToastProvider,
49
+ Toolbar,
44
50
  UploadProgress,
45
51
  useTheme,
46
52
  useToast
47
- } from "./chunk-HSMVDKL3.js";
53
+ } from "./chunk-EJ5NBW7V.js";
48
54
  import {
49
55
  Avatar,
50
56
  AvatarFallback,
@@ -144,10 +150,14 @@ export {
144
150
  DropdownMenuSubTrigger,
145
151
  DropdownMenuTrigger,
146
152
  EmptyState,
153
+ FilterField,
147
154
  InlineCode,
148
155
  Input,
149
156
  Label,
150
157
  Logo,
158
+ Metric,
159
+ MetricStrip,
160
+ PageHeader,
151
161
  Progress,
152
162
  SegmentedControl,
153
163
  Select,
@@ -165,6 +175,7 @@ export {
165
175
  SkeletonCard,
166
176
  SkeletonTable,
167
177
  StatCard,
178
+ StatusPill,
168
179
  Switch,
169
180
  Table,
170
181
  TableBody,
@@ -187,6 +198,7 @@ export {
187
198
  ThemeToggle,
188
199
  ToastContainer,
189
200
  ToastProvider,
201
+ Toolbar,
190
202
  UploadProgress,
191
203
  badgeVariants,
192
204
  buttonVariants,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.2.4",
3
+ "version": "11.3.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",
@@ -141,7 +141,7 @@
141
141
  "react-dom": "^18 || ^19",
142
142
  "react-router": "^7 || ^8",
143
143
  "yjs": "^13.6.0",
144
- "@tangle-network/brand": "^1.3.0"
144
+ "@tangle-network/brand": "^1.4.0"
145
145
  },
146
146
  "peerDependenciesMeta": {
147
147
  "@nanostores/react": {
@@ -118,3 +118,15 @@ export { CodeBlock, CopyButton, InlineCode } from "./code-block";
118
118
  export type { CodeBlockProps, InlineCodeProps } from "./code-block";
119
119
 
120
120
  export { ThemeToggle, useTheme } from "./theme-toggle";
121
+
122
+ export { PageHeader } from "./page-header";
123
+ export type { PageHeaderProps } from "./page-header";
124
+
125
+ export { StatusPill } from "./status-pill";
126
+ export type { StatusPillProps, StatusTone } from "./status-pill";
127
+
128
+ export { MetricStrip, Metric } from "./metric-strip";
129
+ export type { MetricStripProps, MetricProps } from "./metric-strip";
130
+
131
+ export { Toolbar, FilterField } from "./toolbar";
132
+ export type { ToolbarProps, FilterFieldProps } from "./toolbar";