@robr0/design-system 0.11.0 → 0.13.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.
Files changed (73) hide show
  1. package/README.md +4 -4
  2. package/charts.d.ts +1 -0
  3. package/charts.js +2 -0
  4. package/components/AppLayout/AppLayout.d.ts +7 -1
  5. package/components/AppLayout/AppLayout.js +2 -1
  6. package/components/AppSidebar/AppSidebar.css +104 -40
  7. package/components/AppSidebar/AppSidebar.d.ts +14 -1
  8. package/components/AppSidebar/AppSidebar.js +26 -17
  9. package/components/AvatarGroup/AvatarGroup.css +61 -0
  10. package/components/AvatarGroup/AvatarGroup.d.ts +32 -0
  11. package/components/AvatarGroup/AvatarGroup.js +31 -0
  12. package/components/Badge/Badge.css +4 -5
  13. package/components/Chart/AreaChart.d.ts +5 -1
  14. package/components/Chart/AreaChart.js +5 -4
  15. package/components/Chart/BarChart.d.ts +3 -1
  16. package/components/Chart/BarChart.js +3 -3
  17. package/components/Chart/Chart.css +45 -0
  18. package/components/Chart/ComboChart.d.ts +40 -0
  19. package/components/Chart/ComboChart.js +142 -0
  20. package/components/Chart/LineChart.d.ts +3 -1
  21. package/components/Chart/LineChart.js +3 -3
  22. package/components/Chart/PieChart.d.ts +3 -1
  23. package/components/Chart/PieChart.js +3 -3
  24. package/components/Chart/RadarChart.d.ts +3 -1
  25. package/components/Chart/RadarChart.js +3 -3
  26. package/components/Chart/RadialChart.d.ts +7 -1
  27. package/components/Chart/RadialChart.js +51 -43
  28. package/components/Chart/ScatterChart.d.ts +3 -1
  29. package/components/Chart/ScatterChart.js +3 -3
  30. package/components/Chart/StackedBarChart.d.ts +3 -1
  31. package/components/Chart/StackedBarChart.js +3 -3
  32. package/components/Chart/Treemap.d.ts +3 -1
  33. package/components/Chart/Treemap.js +3 -3
  34. package/components/Chart/palette.js +1 -3
  35. package/components/FilterBar/FilterBar.css +202 -0
  36. package/components/FilterBar/FilterBar.d.ts +55 -0
  37. package/components/FilterBar/FilterBar.js +249 -0
  38. package/components/FunnelChart/FunnelChart.css +41 -0
  39. package/components/FunnelChart/FunnelChart.d.ts +40 -0
  40. package/components/FunnelChart/FunnelChart.js +48 -0
  41. package/components/Gauge/Gauge.css +93 -0
  42. package/components/Gauge/Gauge.d.ts +56 -0
  43. package/components/Gauge/Gauge.js +95 -0
  44. package/components/LegendTile/LegendTile.css +60 -0
  45. package/components/LegendTile/LegendTile.d.ts +30 -0
  46. package/components/LegendTile/LegendTile.js +21 -0
  47. package/components/Panel/Panel.css +26 -0
  48. package/components/Panel/Panel.d.ts +22 -0
  49. package/components/Panel/Panel.js +19 -0
  50. package/components/SplitPane/SplitPane.css +106 -0
  51. package/components/SplitPane/SplitPane.d.ts +36 -0
  52. package/components/SplitPane/SplitPane.js +130 -0
  53. package/components/Stat/Stat.css +35 -2
  54. package/components/Stat/Stat.d.ts +3 -1
  55. package/components/Stat/Stat.js +7 -1
  56. package/components/StreamingText/StreamingText.css +40 -0
  57. package/components/StreamingText/StreamingText.d.ts +44 -0
  58. package/components/StreamingText/StreamingText.js +61 -0
  59. package/components/registry.json +73 -0
  60. package/components/registry.json.d.ts +73 -0
  61. package/components/registry.json.js +1 -1
  62. package/index.d.ts +8 -0
  63. package/index.js +16 -0
  64. package/package.json +1 -1
  65. package/tokens/motion.d.ts +7 -3
  66. package/tokens/motion.js +3 -1
  67. package/tokens/registry.json +4 -1
  68. package/tokens/registry.json.d.ts +4 -1
  69. package/tokens/registry.json.js +1 -1
  70. package/tokens/tokens-dark.css +3 -0
  71. package/tokens/tokens-light.css +11 -0
  72. package/tokens/tokens-primitives.css +2 -0
  73. package/tokens/tokens-typography.css +12 -5
@@ -0,0 +1,130 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import React, { useState, useRef } from "react";
4
+ import "./SplitPane.css";
5
+ const KEY_STEP = 2;
6
+ const KEY_STEP_LARGE = 10;
7
+ const SplitPane = React.forwardRef(
8
+ ({
9
+ children,
10
+ direction = "horizontal",
11
+ split,
12
+ defaultSplit = 50,
13
+ minSplit = 10,
14
+ maxSplit = 90,
15
+ onSplitChange,
16
+ separatorLabel = "Resize panes",
17
+ className = "",
18
+ ...rest
19
+ }, ref) => {
20
+ const baseClass = "ds-split-pane";
21
+ const [uncontrolledSplit, setUncontrolledSplit] = useState(defaultSplit);
22
+ const [dragging, setDragging] = useState(false);
23
+ const draggingRef = useRef(false);
24
+ const internalRef = useRef(null);
25
+ const setRef = (node) => {
26
+ internalRef.current = node;
27
+ if (typeof ref === "function") ref(node);
28
+ else if (ref) ref.current = node;
29
+ };
30
+ const clamp = (value) => Math.min(Math.max(value, minSplit), maxSplit);
31
+ const currentSplit = clamp(split ?? uncontrolledSplit);
32
+ const applySplit = (value) => {
33
+ const next = clamp(value);
34
+ if (split === void 0) setUncontrolledSplit(next);
35
+ onSplitChange?.(next);
36
+ };
37
+ const splitFromPointer = (e) => {
38
+ const rect = internalRef.current?.getBoundingClientRect();
39
+ if (!rect) return;
40
+ const fraction = direction === "horizontal" ? (e.clientX - rect.left) / rect.width : (e.clientY - rect.top) / rect.height;
41
+ applySplit(fraction * 100);
42
+ };
43
+ const handlePointerDown = (e) => {
44
+ e.currentTarget.setPointerCapture(e.pointerId);
45
+ draggingRef.current = true;
46
+ setDragging(true);
47
+ splitFromPointer(e);
48
+ };
49
+ const handlePointerMove = (e) => {
50
+ if (!draggingRef.current) return;
51
+ splitFromPointer(e);
52
+ };
53
+ const handlePointerUp = (e) => {
54
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
55
+ e.currentTarget.releasePointerCapture(e.pointerId);
56
+ }
57
+ draggingRef.current = false;
58
+ setDragging(false);
59
+ };
60
+ const handleKeyDown = (e) => {
61
+ const step = e.shiftKey ? KEY_STEP_LARGE : KEY_STEP;
62
+ const shrinkKey = direction === "horizontal" ? "ArrowLeft" : "ArrowUp";
63
+ const growKey = direction === "horizontal" ? "ArrowRight" : "ArrowDown";
64
+ switch (e.key) {
65
+ case shrinkKey:
66
+ e.preventDefault();
67
+ applySplit(currentSplit - step);
68
+ break;
69
+ case growKey:
70
+ e.preventDefault();
71
+ applySplit(currentSplit + step);
72
+ break;
73
+ case "Home":
74
+ e.preventDefault();
75
+ applySplit(minSplit);
76
+ break;
77
+ case "End":
78
+ e.preventDefault();
79
+ applySplit(maxSplit);
80
+ break;
81
+ }
82
+ };
83
+ const classes = [
84
+ baseClass,
85
+ `${baseClass}--${direction}`,
86
+ dragging && `${baseClass}--dragging`,
87
+ className
88
+ ].filter(Boolean).join(" ");
89
+ const [first, second] = React.Children.toArray(children);
90
+ return /* @__PURE__ */ jsxs(
91
+ "div",
92
+ {
93
+ ...rest,
94
+ ref: setRef,
95
+ className: classes,
96
+ style: {
97
+ "--ds-split-pane-split": `${currentSplit}%`,
98
+ ...rest.style
99
+ },
100
+ children: [
101
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__pane ${baseClass}__pane--first`, children: first }),
102
+ /* @__PURE__ */ jsx(
103
+ "div",
104
+ {
105
+ className: `${baseClass}__separator`,
106
+ role: "separator",
107
+ tabIndex: 0,
108
+ "aria-label": separatorLabel,
109
+ "aria-orientation": direction === "horizontal" ? "vertical" : "horizontal",
110
+ "aria-valuenow": Math.round(currentSplit),
111
+ "aria-valuemin": Math.round(minSplit),
112
+ "aria-valuemax": Math.round(maxSplit),
113
+ onPointerDown: handlePointerDown,
114
+ onPointerMove: handlePointerMove,
115
+ onPointerUp: handlePointerUp,
116
+ onPointerCancel: handlePointerUp,
117
+ onKeyDown: handleKeyDown,
118
+ children: /* @__PURE__ */ jsx("span", { className: `${baseClass}__grip`, "aria-hidden": "true" })
119
+ }
120
+ ),
121
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__pane ${baseClass}__pane--second`, children: second })
122
+ ]
123
+ }
124
+ );
125
+ }
126
+ );
127
+ SplitPane.displayName = "SplitPane";
128
+ export {
129
+ SplitPane
130
+ };
@@ -65,18 +65,51 @@
65
65
  line-height: 1;
66
66
  }
67
67
 
68
+ /* The trend tokens are theme-split for contrast: deep status inks on
69
+ light surfaces, the mint/coral accents on dark ones. */
68
70
  .ds-stat__delta--up {
69
- color: var(--color-core-accent-mint);
71
+ color: var(--color-trend-up);
70
72
  }
71
73
 
72
74
  .ds-stat__delta--down {
73
- color: var(--color-core-accent-coral);
75
+ color: var(--color-trend-down);
74
76
  }
75
77
 
76
78
  .ds-stat__delta--neutral {
77
79
  color: var(--color-text-tertiary);
78
80
  }
79
81
 
82
+ /* ============================================
83
+ INLINE DELTA — delta to the right of the
84
+ value, bottom-aligned to its baseline
85
+ ============================================ */
86
+
87
+ .ds-stat--delta-inline {
88
+ display: grid;
89
+ grid-template-columns: auto 1fr;
90
+ grid-template-areas:
91
+ 'value delta'
92
+ 'label label';
93
+ column-gap: var(--gap-sm);
94
+ row-gap: var(--gap-xxs);
95
+ }
96
+
97
+ .ds-stat--delta-inline .ds-stat__value {
98
+ grid-area: value;
99
+ }
100
+
101
+ .ds-stat--delta-inline .ds-stat__label {
102
+ grid-area: label;
103
+ }
104
+
105
+ .ds-stat--delta-inline .ds-stat__delta {
106
+ grid-area: delta;
107
+ align-self: end;
108
+ justify-self: start;
109
+ /* Optical baseline: compensates the display value's half-leading. */
110
+ margin-bottom: var(--gap-xs);
111
+ }
112
+
80
113
  /* Icons take the colour of the text around them. Explicit, not just
81
114
  inherited by default: a consumer stylesheet that sets a colour on
82
115
  .material-symbols-rounded itself (a common global default) would
@@ -7,6 +7,8 @@ export interface StatProps {
7
7
  delta?: string;
8
8
  /** Direction of the delta — colours it and adds an arrow */
9
9
  trend?: 'up' | 'down' | 'neutral';
10
+ /** Where the delta sits: stacked below the label, or inline to the right of the value, bottom-aligned */
11
+ deltaPlacement?: 'stacked' | 'inline';
10
12
  /** Stat size */
11
13
  size?: 'default' | 'large';
12
14
  /** Additional CSS classes */
@@ -16,4 +18,4 @@ export interface StatProps {
16
18
  * Stat component — a single headline metric with a label and an
17
19
  * optional trend delta. Compose several in a row for a metrics band.
18
20
  */
19
- export declare const Stat: ({ value, label, delta, trend, size, className, }: StatProps) => import("react").JSX.Element;
21
+ export declare const Stat: ({ value, label, delta, trend, deltaPlacement, size, className, }: StatProps) => import("react").JSX.Element;
@@ -11,11 +11,17 @@ const Stat = ({
11
11
  label,
12
12
  delta,
13
13
  trend = "neutral",
14
+ deltaPlacement = "stacked",
14
15
  size = "default",
15
16
  className = ""
16
17
  }) => {
17
18
  const baseClass = "ds-stat";
18
- const classes = [baseClass, `${baseClass}--${size}`, className].filter(Boolean).join(" ");
19
+ const classes = [
20
+ baseClass,
21
+ `${baseClass}--${size}`,
22
+ deltaPlacement === "inline" ? `${baseClass}--delta-inline` : "",
23
+ className
24
+ ].filter(Boolean).join(" ");
19
25
  return /* @__PURE__ */ jsxs("div", { className: classes, children: [
20
26
  /* @__PURE__ */ jsx("span", { className: `${baseClass}__value`, children: value }),
21
27
  /* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: label }),
@@ -0,0 +1,40 @@
1
+ /* ============================================
2
+ STREAMING TEXT COMPONENT
3
+ Progressive reveal for text arriving in
4
+ chunks, with a blinking cursor. Inherits the
5
+ surrounding typography — the reveal is not a
6
+ text style of its own.
7
+ ============================================ */
8
+
9
+ /* Base */
10
+
11
+ .ds-streaming-text {
12
+ white-space: pre-wrap;
13
+ overflow-wrap: break-word;
14
+ }
15
+
16
+ /* Cursor */
17
+
18
+ .ds-streaming-text__cursor {
19
+ display: inline-block;
20
+ width: 0.55em;
21
+ height: 1em;
22
+ margin-left: 0.1em;
23
+ vertical-align: text-bottom;
24
+ border-radius: var(--radius-xxs);
25
+ background-color: currentColor;
26
+ animation: ds-streaming-text-blink var(--motion-duration-loop-spin) var(--motion-ease-standard)
27
+ infinite;
28
+ }
29
+
30
+ @keyframes ds-streaming-text-blink {
31
+ 0%,
32
+ 45% {
33
+ opacity: 1;
34
+ }
35
+
36
+ 55%,
37
+ 100% {
38
+ opacity: 0.15;
39
+ }
40
+ }
@@ -0,0 +1,44 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by StreamingText itself — everything else falls through to the root span. */
3
+ type StreamingTextOwnProps = {
4
+ /**
5
+ * The text received so far. Grow it across renders as chunks arrive; the
6
+ * reveal animates through the appended part. A value that does not extend
7
+ * the previous one is treated as a new message and reveals from the start.
8
+ */
9
+ text: string;
10
+ /**
11
+ * Whether the source is still producing text. Keeps the cursor visible
12
+ * between chunks, when the reveal has caught up but more may arrive.
13
+ */
14
+ streaming?: boolean;
15
+ /**
16
+ * Milliseconds between reveal steps. The reveal adds more characters per
17
+ * step the further it falls behind, so a large chunk catches up instead of
18
+ * typing for seconds.
19
+ */
20
+ charIntervalMs?: number;
21
+ /** Shows the blinking cursor while streaming or revealing. */
22
+ cursor?: boolean;
23
+ /**
24
+ * Fires once when the reveal catches up with `text` after `streaming` has
25
+ * ended — the moment the message is fully on screen.
26
+ */
27
+ onRevealComplete?: () => void;
28
+ /** Additional CSS classes */
29
+ className?: string;
30
+ };
31
+ export interface StreamingTextProps extends StreamingTextOwnProps, Omit<React.ComponentPropsWithoutRef<'span'>, keyof StreamingTextOwnProps | 'children'> {
32
+ }
33
+ /**
34
+ * StreamingText — the reveal for text that arrives in chunks: an LLM
35
+ * response typing itself out, with a cursor that blinks while more is
36
+ * coming. Feed it the accumulated text on every render and it animates
37
+ * through what was appended, catching up faster the further behind it
38
+ * falls. Under `prefers-reduced-motion` the reveal is skipped and each
39
+ * chunk appears whole. Announcement is the container's job — pair it with
40
+ * an `aria-live` region when the surrounding UI does not already announce
41
+ * the message.
42
+ */
43
+ export declare const StreamingText: React.ForwardRefExoticComponent<StreamingTextProps & React.RefAttributes<HTMLSpanElement>>;
44
+ export {};
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import React, { useState, useRef, useEffect } from "react";
4
+ import { MOTION_STREAM_CHAR_INTERVAL_MS } from "../../tokens/motion.js";
5
+ import "./StreamingText.css";
6
+ const StreamingText = React.forwardRef(
7
+ ({
8
+ text,
9
+ streaming = false,
10
+ charIntervalMs = MOTION_STREAM_CHAR_INTERVAL_MS,
11
+ cursor = true,
12
+ onRevealComplete,
13
+ className = "",
14
+ ...rest
15
+ }, ref) => {
16
+ const baseClass = "ds-streaming-text";
17
+ const [revealed, setRevealed] = useState(text.length);
18
+ const previousText = useRef(text);
19
+ const completed = useRef(false);
20
+ useEffect(() => {
21
+ if (!text.startsWith(previousText.current)) {
22
+ setRevealed(0);
23
+ completed.current = false;
24
+ }
25
+ previousText.current = text;
26
+ }, [text]);
27
+ const caughtUp = revealed >= text.length;
28
+ useEffect(() => {
29
+ if (caughtUp) return;
30
+ completed.current = false;
31
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
32
+ setRevealed(text.length);
33
+ return;
34
+ }
35
+ const interval = setInterval(() => {
36
+ setRevealed((current) => {
37
+ const pending = text.length - current;
38
+ if (pending <= 0) return current;
39
+ return current + Math.max(1, Math.round(pending / 20));
40
+ });
41
+ }, charIntervalMs);
42
+ return () => clearInterval(interval);
43
+ }, [text, caughtUp, charIntervalMs]);
44
+ useEffect(() => {
45
+ if (caughtUp && !streaming && text.length > 0 && !completed.current) {
46
+ completed.current = true;
47
+ onRevealComplete?.();
48
+ }
49
+ }, [caughtUp, streaming, text, onRevealComplete]);
50
+ const showCursor = cursor && (streaming || !caughtUp);
51
+ const classes = [baseClass, className].filter(Boolean).join(" ");
52
+ return /* @__PURE__ */ jsxs("span", { ...rest, ref, className: classes, children: [
53
+ caughtUp ? text : text.slice(0, revealed),
54
+ showCursor && /* @__PURE__ */ jsx("span", { className: `${baseClass}__cursor`, "aria-hidden": "true" })
55
+ ] });
56
+ }
57
+ );
58
+ StreamingText.displayName = "StreamingText";
59
+ export {
60
+ StreamingText
61
+ };
@@ -147,6 +147,14 @@
147
147
  "category": "data-display",
148
148
  "client": true
149
149
  },
150
+ {
151
+ "name": "AvatarGroup",
152
+ "label": "Avatar group",
153
+ "slug": "avatar-group",
154
+ "description": "Overlapping avatar stack with a +N counter for the overflow.",
155
+ "category": "data-display",
156
+ "client": false
157
+ },
150
158
  {
151
159
  "name": "Badge",
152
160
  "label": "Badge",
@@ -292,6 +300,15 @@
292
300
  "category": "forms",
293
301
  "client": true
294
302
  },
303
+ {
304
+ "name": "ComboChart",
305
+ "label": "Combo chart",
306
+ "slug": "combo-chart",
307
+ "description": "Bar and line series in one chart, with an optional second y-axis for pairs in different units, like spend and ROAS.",
308
+ "category": "charts",
309
+ "client": false,
310
+ "folder": "Chart"
311
+ },
295
312
  {
296
313
  "name": "Combobox",
297
314
  "label": "Combobox",
@@ -460,6 +477,30 @@
460
477
  "category": "forms",
461
478
  "client": true
462
479
  },
480
+ {
481
+ "name": "FilterBar",
482
+ "label": "Filter bar",
483
+ "slug": "filter-bar",
484
+ "description": "A row of filter chips for narrowing a collection, each opening a popover of options, with per-filter and clear-all resets.",
485
+ "category": "forms",
486
+ "client": true
487
+ },
488
+ {
489
+ "name": "FunnelChart",
490
+ "label": "Funnel chart",
491
+ "slug": "funnel-chart",
492
+ "description": "Ordered funnel stages as stepped bars with conversion percentages, each sized by its share of the first stage.",
493
+ "category": "charts",
494
+ "client": false
495
+ },
496
+ {
497
+ "name": "Gauge",
498
+ "label": "Gauge",
499
+ "slug": "gauge",
500
+ "description": "A radial dial for a single bounded reading, recoloured through the status roles as it crosses thresholds.",
501
+ "category": "charts",
502
+ "client": false
503
+ },
463
504
  {
464
505
  "name": "Globe",
465
506
  "label": "Globe",
@@ -500,6 +541,14 @@
500
541
  "category": "data-display",
501
542
  "client": false
502
543
  },
544
+ {
545
+ "name": "LegendTile",
546
+ "label": "Legend tile",
547
+ "slug": "legend-tile",
548
+ "description": "The labelled value tile under a chart: a series dot, the series name, and its reading, on an inset fill.",
549
+ "category": "charts",
550
+ "client": false
551
+ },
503
552
  {
504
553
  "name": "LineChart",
505
554
  "label": "Line chart",
@@ -597,6 +646,14 @@
597
646
  "category": "navigation",
598
647
  "client": true
599
648
  },
649
+ {
650
+ "name": "Panel",
651
+ "label": "Panel",
652
+ "slug": "panel",
653
+ "description": "The plain dashboard surface: a rounded container with no border or shadow, just padding and a gap.",
654
+ "category": "layout",
655
+ "client": false
656
+ },
600
657
  {
601
658
  "name": "PieChart",
602
659
  "label": "Pie chart",
@@ -769,6 +826,14 @@
769
826
  "category": "feedback",
770
827
  "client": false
771
828
  },
829
+ {
830
+ "name": "SplitPane",
831
+ "label": "Split pane",
832
+ "slug": "split-pane",
833
+ "description": "Two resizable regions with a draggable, keyboard-operable divider between them.",
834
+ "category": "layout",
835
+ "client": true
836
+ },
772
837
  {
773
838
  "name": "StackedBarChart",
774
839
  "label": "Stacked bar chart",
@@ -794,6 +859,14 @@
794
859
  "category": "navigation",
795
860
  "client": true
796
861
  },
862
+ {
863
+ "name": "StreamingText",
864
+ "label": "Streaming text",
865
+ "slug": "streaming-text",
866
+ "description": "Progressive reveal for text arriving in chunks, with a blinking cursor while more is coming.",
867
+ "category": "ai",
868
+ "client": true
869
+ },
797
870
  {
798
871
  "name": "Swatch",
799
872
  "label": "Swatch",
@@ -147,6 +147,14 @@ declare const _default: {
147
147
  "category": "data-display",
148
148
  "client": true
149
149
  },
150
+ {
151
+ "name": "AvatarGroup",
152
+ "label": "Avatar group",
153
+ "slug": "avatar-group",
154
+ "description": "Overlapping avatar stack with a +N counter for the overflow.",
155
+ "category": "data-display",
156
+ "client": false
157
+ },
150
158
  {
151
159
  "name": "Badge",
152
160
  "label": "Badge",
@@ -292,6 +300,15 @@ declare const _default: {
292
300
  "category": "forms",
293
301
  "client": true
294
302
  },
303
+ {
304
+ "name": "ComboChart",
305
+ "label": "Combo chart",
306
+ "slug": "combo-chart",
307
+ "description": "Bar and line series in one chart, with an optional second y-axis for pairs in different units, like spend and ROAS.",
308
+ "category": "charts",
309
+ "client": false,
310
+ "folder": "Chart"
311
+ },
295
312
  {
296
313
  "name": "Combobox",
297
314
  "label": "Combobox",
@@ -460,6 +477,30 @@ declare const _default: {
460
477
  "category": "forms",
461
478
  "client": true
462
479
  },
480
+ {
481
+ "name": "FilterBar",
482
+ "label": "Filter bar",
483
+ "slug": "filter-bar",
484
+ "description": "A row of filter chips for narrowing a collection, each opening a popover of options, with per-filter and clear-all resets.",
485
+ "category": "forms",
486
+ "client": true
487
+ },
488
+ {
489
+ "name": "FunnelChart",
490
+ "label": "Funnel chart",
491
+ "slug": "funnel-chart",
492
+ "description": "Ordered funnel stages as stepped bars with conversion percentages, each sized by its share of the first stage.",
493
+ "category": "charts",
494
+ "client": false
495
+ },
496
+ {
497
+ "name": "Gauge",
498
+ "label": "Gauge",
499
+ "slug": "gauge",
500
+ "description": "A radial dial for a single bounded reading, recoloured through the status roles as it crosses thresholds.",
501
+ "category": "charts",
502
+ "client": false
503
+ },
463
504
  {
464
505
  "name": "Globe",
465
506
  "label": "Globe",
@@ -500,6 +541,14 @@ declare const _default: {
500
541
  "category": "data-display",
501
542
  "client": false
502
543
  },
544
+ {
545
+ "name": "LegendTile",
546
+ "label": "Legend tile",
547
+ "slug": "legend-tile",
548
+ "description": "The labelled value tile under a chart: a series dot, the series name, and its reading, on an inset fill.",
549
+ "category": "charts",
550
+ "client": false
551
+ },
503
552
  {
504
553
  "name": "LineChart",
505
554
  "label": "Line chart",
@@ -597,6 +646,14 @@ declare const _default: {
597
646
  "category": "navigation",
598
647
  "client": true
599
648
  },
649
+ {
650
+ "name": "Panel",
651
+ "label": "Panel",
652
+ "slug": "panel",
653
+ "description": "The plain dashboard surface: a rounded container with no border or shadow, just padding and a gap.",
654
+ "category": "layout",
655
+ "client": false
656
+ },
600
657
  {
601
658
  "name": "PieChart",
602
659
  "label": "Pie chart",
@@ -769,6 +826,14 @@ declare const _default: {
769
826
  "category": "feedback",
770
827
  "client": false
771
828
  },
829
+ {
830
+ "name": "SplitPane",
831
+ "label": "Split pane",
832
+ "slug": "split-pane",
833
+ "description": "Two resizable regions with a draggable, keyboard-operable divider between them.",
834
+ "category": "layout",
835
+ "client": true
836
+ },
772
837
  {
773
838
  "name": "StackedBarChart",
774
839
  "label": "Stacked bar chart",
@@ -794,6 +859,14 @@ declare const _default: {
794
859
  "category": "navigation",
795
860
  "client": true
796
861
  },
862
+ {
863
+ "name": "StreamingText",
864
+ "label": "Streaming text",
865
+ "slug": "streaming-text",
866
+ "description": "Progressive reveal for text arriving in chunks, with a blinking cursor while more is coming.",
867
+ "category": "ai",
868
+ "client": true
869
+ },
797
870
  {
798
871
  "name": "Swatch",
799
872
  "label": "Swatch",