@corbits/react-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/LICENSE +501 -0
  2. package/README.md +125 -0
  3. package/dist/blocks/login/auth-layout.d.ts +38 -0
  4. package/dist/blocks/login/auth-layout.js +52 -0
  5. package/dist/blocks/login/login-form.d.ts +60 -0
  6. package/dist/blocks/login/login-form.js +151 -0
  7. package/dist/hooks/use-command-palette-navigation.d.ts +25 -0
  8. package/dist/hooks/use-command-palette-navigation.js +52 -0
  9. package/dist/hooks/use-controllable-state.d.ts +28 -0
  10. package/dist/hooks/use-controllable-state.js +44 -0
  11. package/dist/hooks/use-delayed-autofocus.d.ts +9 -0
  12. package/dist/hooks/use-delayed-autofocus.js +20 -0
  13. package/dist/hooks/use-dismissable-popover.d.ts +37 -0
  14. package/dist/hooks/use-dismissable-popover.js +64 -0
  15. package/dist/hooks/use-list-selection.d.ts +66 -0
  16. package/dist/hooks/use-list-selection.js +147 -0
  17. package/dist/hooks/use-prefers-reduced-motion.d.ts +13 -0
  18. package/dist/hooks/use-prefers-reduced-motion.js +28 -0
  19. package/dist/hooks/use-scroll-current-into-view.d.ts +19 -0
  20. package/dist/hooks/use-scroll-current-into-view.js +33 -0
  21. package/dist/index.d.ts +122 -0
  22. package/dist/index.js +131 -0
  23. package/dist/lib/artifact.d.ts +72 -0
  24. package/dist/lib/artifact.js +67 -0
  25. package/dist/lib/chart-geometry.d.ts +62 -0
  26. package/dist/lib/chart-geometry.js +86 -0
  27. package/dist/lib/chart-palette.d.ts +76 -0
  28. package/dist/lib/chart-palette.js +92 -0
  29. package/dist/lib/chat-message.d.ts +115 -0
  30. package/dist/lib/chat-message.js +41 -0
  31. package/dist/lib/chat-parts.d.ts +64 -0
  32. package/dist/lib/chat-parts.js +16 -0
  33. package/dist/lib/csv.d.ts +60 -0
  34. package/dist/lib/csv.js +108 -0
  35. package/dist/lib/metrics.d.ts +70 -0
  36. package/dist/lib/metrics.js +101 -0
  37. package/dist/lib/now-item.d.ts +65 -0
  38. package/dist/lib/now-item.js +59 -0
  39. package/dist/lib/relative-time.d.ts +9 -0
  40. package/dist/lib/relative-time.js +47 -0
  41. package/dist/lib/theme.d.ts +30 -0
  42. package/dist/lib/theme.js +77 -0
  43. package/dist/lib/url.d.ts +26 -0
  44. package/dist/lib/url.js +37 -0
  45. package/dist/lib/utils.d.ts +2 -0
  46. package/dist/lib/utils.js +5 -0
  47. package/dist/lib/workflow-display-flow.d.ts +83 -0
  48. package/dist/lib/workflow-display-flow.js +104 -0
  49. package/dist/lib/workflow-registry.d.ts +90 -0
  50. package/dist/lib/workflow-registry.js +56 -0
  51. package/dist/lib/workflow-run-error.d.ts +31 -0
  52. package/dist/lib/workflow-run-error.js +105 -0
  53. package/dist/lib/workflow-run-progress.d.ts +97 -0
  54. package/dist/lib/workflow-run-progress.js +135 -0
  55. package/dist/lib/workflow-run.d.ts +43 -0
  56. package/dist/lib/workflow-run.js +56 -0
  57. package/dist/styles.css +3080 -0
  58. package/dist/test/render-hook.d.ts +14 -0
  59. package/dist/test/render-hook.js +38 -0
  60. package/dist/theme.css +628 -0
  61. package/dist/ui/agent-turn.d.ts +26 -0
  62. package/dist/ui/agent-turn.js +60 -0
  63. package/dist/ui/animated-number.d.ts +25 -0
  64. package/dist/ui/animated-number.js +63 -0
  65. package/dist/ui/approval-card.d.ts +46 -0
  66. package/dist/ui/approval-card.js +116 -0
  67. package/dist/ui/artifact-body.d.ts +26 -0
  68. package/dist/ui/artifact-body.js +170 -0
  69. package/dist/ui/artifact-notice.d.ts +21 -0
  70. package/dist/ui/artifact-notice.js +36 -0
  71. package/dist/ui/avatar.d.ts +37 -0
  72. package/dist/ui/avatar.js +73 -0
  73. package/dist/ui/badge.d.ts +10 -0
  74. package/dist/ui/badge.js +35 -0
  75. package/dist/ui/bar-chart.d.ts +38 -0
  76. package/dist/ui/bar-chart.js +118 -0
  77. package/dist/ui/block-card.d.ts +29 -0
  78. package/dist/ui/block-card.js +66 -0
  79. package/dist/ui/boot-screen.d.ts +23 -0
  80. package/dist/ui/boot-screen.js +49 -0
  81. package/dist/ui/bulk-action-bar.d.ts +37 -0
  82. package/dist/ui/bulk-action-bar.js +64 -0
  83. package/dist/ui/button.d.ts +12 -0
  84. package/dist/ui/button.js +73 -0
  85. package/dist/ui/card.d.ts +6 -0
  86. package/dist/ui/card.js +37 -0
  87. package/dist/ui/category-bars.d.ts +20 -0
  88. package/dist/ui/category-bars.js +69 -0
  89. package/dist/ui/chart-frame.d.ts +54 -0
  90. package/dist/ui/chart-frame.js +108 -0
  91. package/dist/ui/chat-composer.d.ts +24 -0
  92. package/dist/ui/chat-composer.js +32 -0
  93. package/dist/ui/chat-dock-timing.d.ts +15 -0
  94. package/dist/ui/chat-dock-timing.js +11 -0
  95. package/dist/ui/chat-input.d.ts +38 -0
  96. package/dist/ui/chat-input.js +138 -0
  97. package/dist/ui/chat-panel.d.ts +20 -0
  98. package/dist/ui/chat-panel.js +56 -0
  99. package/dist/ui/chat-thread.d.ts +28 -0
  100. package/dist/ui/chat-thread.js +61 -0
  101. package/dist/ui/checkbox.d.ts +25 -0
  102. package/dist/ui/checkbox.js +63 -0
  103. package/dist/ui/command-palette.d.ts +100 -0
  104. package/dist/ui/command-palette.js +341 -0
  105. package/dist/ui/compare-body.d.ts +52 -0
  106. package/dist/ui/compare-body.js +140 -0
  107. package/dist/ui/confirm-button.d.ts +29 -0
  108. package/dist/ui/confirm-button.js +41 -0
  109. package/dist/ui/corbits-mark.d.ts +29 -0
  110. package/dist/ui/corbits-mark.js +33 -0
  111. package/dist/ui/csv-table.d.ts +22 -0
  112. package/dist/ui/csv-table.js +85 -0
  113. package/dist/ui/dialog.d.ts +37 -0
  114. package/dist/ui/dialog.js +105 -0
  115. package/dist/ui/dictation-status-line.d.ts +20 -0
  116. package/dist/ui/dictation-status-line.js +38 -0
  117. package/dist/ui/dither-canvas.d.ts +31 -0
  118. package/dist/ui/dither-canvas.js +169 -0
  119. package/dist/ui/embed-body.d.ts +30 -0
  120. package/dist/ui/embed-body.js +92 -0
  121. package/dist/ui/empty-state.d.ts +26 -0
  122. package/dist/ui/empty-state.js +42 -0
  123. package/dist/ui/file-input.d.ts +25 -0
  124. package/dist/ui/file-input.js +66 -0
  125. package/dist/ui/filter-bar.d.ts +42 -0
  126. package/dist/ui/filter-bar.js +95 -0
  127. package/dist/ui/filter-chip.d.ts +14 -0
  128. package/dist/ui/filter-chip.js +23 -0
  129. package/dist/ui/gate-block.d.ts +16 -0
  130. package/dist/ui/gate-block.js +37 -0
  131. package/dist/ui/horizontal-stepper.d.ts +13 -0
  132. package/dist/ui/horizontal-stepper.js +65 -0
  133. package/dist/ui/input.d.ts +2 -0
  134. package/dist/ui/input.js +14 -0
  135. package/dist/ui/inspector-shell.d.ts +47 -0
  136. package/dist/ui/inspector-shell.js +98 -0
  137. package/dist/ui/intake-form.d.ts +39 -0
  138. package/dist/ui/intake-form.js +119 -0
  139. package/dist/ui/kind-card-grid.d.ts +23 -0
  140. package/dist/ui/kind-card-grid.js +43 -0
  141. package/dist/ui/library-page-header.d.ts +20 -0
  142. package/dist/ui/library-page-header.js +43 -0
  143. package/dist/ui/list-detail.d.ts +28 -0
  144. package/dist/ui/list-detail.js +55 -0
  145. package/dist/ui/live-run-banner.d.ts +13 -0
  146. package/dist/ui/live-run-banner.js +23 -0
  147. package/dist/ui/live-status-line.d.ts +22 -0
  148. package/dist/ui/live-status-line.js +37 -0
  149. package/dist/ui/menu.d.ts +18 -0
  150. package/dist/ui/menu.js +45 -0
  151. package/dist/ui/message-bubble.d.ts +24 -0
  152. package/dist/ui/message-bubble.js +64 -0
  153. package/dist/ui/mic-button.d.ts +35 -0
  154. package/dist/ui/mic-button.js +61 -0
  155. package/dist/ui/mic-permission-dialog.d.ts +20 -0
  156. package/dist/ui/mic-permission-dialog.js +50 -0
  157. package/dist/ui/notifications-bell.d.ts +28 -0
  158. package/dist/ui/notifications-bell.js +69 -0
  159. package/dist/ui/now-cards.d.ts +37 -0
  160. package/dist/ui/now-cards.js +116 -0
  161. package/dist/ui/page-panel.d.ts +37 -0
  162. package/dist/ui/page-panel.js +23 -0
  163. package/dist/ui/page-shell.d.ts +21 -0
  164. package/dist/ui/page-shell.js +26 -0
  165. package/dist/ui/parts-renderer.d.ts +22 -0
  166. package/dist/ui/parts-renderer.js +234 -0
  167. package/dist/ui/profile-card.d.ts +39 -0
  168. package/dist/ui/profile-card.js +124 -0
  169. package/dist/ui/progress-checklist.d.ts +25 -0
  170. package/dist/ui/progress-checklist.js +70 -0
  171. package/dist/ui/provider-mark.d.ts +32 -0
  172. package/dist/ui/provider-mark.js +31 -0
  173. package/dist/ui/quick-reply-chips.d.ts +14 -0
  174. package/dist/ui/quick-reply-chips.js +24 -0
  175. package/dist/ui/quote-card.d.ts +39 -0
  176. package/dist/ui/quote-card.js +66 -0
  177. package/dist/ui/reasoning-block.d.ts +35 -0
  178. package/dist/ui/reasoning-block.js +60 -0
  179. package/dist/ui/research-body.d.ts +70 -0
  180. package/dist/ui/research-body.js +270 -0
  181. package/dist/ui/rich-empty-state.d.ts +35 -0
  182. package/dist/ui/rich-empty-state.js +65 -0
  183. package/dist/ui/run-now-button.d.ts +23 -0
  184. package/dist/ui/run-now-button.js +42 -0
  185. package/dist/ui/section.d.ts +20 -0
  186. package/dist/ui/section.js +48 -0
  187. package/dist/ui/select.d.ts +2 -0
  188. package/dist/ui/select.js +22 -0
  189. package/dist/ui/selection-checkbox.d.ts +42 -0
  190. package/dist/ui/selection-checkbox.js +54 -0
  191. package/dist/ui/settings-panel.d.ts +35 -0
  192. package/dist/ui/settings-panel.js +68 -0
  193. package/dist/ui/shimmer-text.d.ts +19 -0
  194. package/dist/ui/shimmer-text.js +20 -0
  195. package/dist/ui/sidebar-item-row.d.ts +40 -0
  196. package/dist/ui/sidebar-item-row.js +64 -0
  197. package/dist/ui/sidebar-panel.d.ts +25 -0
  198. package/dist/ui/sidebar-panel.js +56 -0
  199. package/dist/ui/sidebar.d.ts +46 -0
  200. package/dist/ui/sidebar.js +111 -0
  201. package/dist/ui/skeleton.d.ts +13 -0
  202. package/dist/ui/skeleton.js +20 -0
  203. package/dist/ui/sortable-table.d.ts +43 -0
  204. package/dist/ui/sortable-table.js +103 -0
  205. package/dist/ui/sparkline.d.ts +27 -0
  206. package/dist/ui/sparkline.js +68 -0
  207. package/dist/ui/stat-grid.d.ts +55 -0
  208. package/dist/ui/stat-grid.js +87 -0
  209. package/dist/ui/status-dot.d.ts +13 -0
  210. package/dist/ui/status-dot.js +51 -0
  211. package/dist/ui/step-list.d.ts +21 -0
  212. package/dist/ui/step-list.js +77 -0
  213. package/dist/ui/switch.d.ts +23 -0
  214. package/dist/ui/switch.js +29 -0
  215. package/dist/ui/table.d.ts +8 -0
  216. package/dist/ui/table.js +48 -0
  217. package/dist/ui/tabs.d.ts +44 -0
  218. package/dist/ui/tabs.js +101 -0
  219. package/dist/ui/textarea.d.ts +8 -0
  220. package/dist/ui/textarea.js +32 -0
  221. package/dist/ui/theme-provider.d.ts +36 -0
  222. package/dist/ui/theme-provider.js +126 -0
  223. package/dist/ui/theme-toggle.d.ts +11 -0
  224. package/dist/ui/theme-toggle.js +32 -0
  225. package/dist/ui/thinking-indicator.d.ts +7 -0
  226. package/dist/ui/thinking-indicator.js +19 -0
  227. package/dist/ui/thinking-label.d.ts +19 -0
  228. package/dist/ui/thinking-label.js +54 -0
  229. package/dist/ui/thinking-mark.d.ts +10 -0
  230. package/dist/ui/thinking-mark.js +153 -0
  231. package/dist/ui/time-series-chart.d.ts +55 -0
  232. package/dist/ui/time-series-chart.js +249 -0
  233. package/dist/ui/toast.d.ts +16 -0
  234. package/dist/ui/toast.js +41 -0
  235. package/dist/ui/token-mosaic.d.ts +22 -0
  236. package/dist/ui/token-mosaic.js +65 -0
  237. package/dist/ui/tool-block.d.ts +54 -0
  238. package/dist/ui/tool-block.js +170 -0
  239. package/dist/ui/tool-narrative.d.ts +22 -0
  240. package/dist/ui/tool-narrative.js +77 -0
  241. package/dist/ui/tool-picker.d.ts +44 -0
  242. package/dist/ui/tool-picker.js +200 -0
  243. package/dist/ui/tooltip.d.ts +21 -0
  244. package/dist/ui/tooltip.js +48 -0
  245. package/dist/ui/top-bar.d.ts +53 -0
  246. package/dist/ui/top-bar.js +98 -0
  247. package/dist/ui/trace-waterfall.d.ts +38 -0
  248. package/dist/ui/trace-waterfall.js +121 -0
  249. package/dist/ui/typing-indicator.d.ts +9 -0
  250. package/dist/ui/typing-indicator.js +25 -0
  251. package/dist/ui/view-toggle.d.ts +17 -0
  252. package/dist/ui/view-toggle.js +46 -0
  253. package/dist/ui/voice-waveform.d.ts +16 -0
  254. package/dist/ui/voice-waveform.js +23 -0
  255. package/package.json +583 -0
@@ -0,0 +1,103 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ChevronDown, ChevronUp, ChevronsUpDown } from "lucide-react";
3
+ import { useMemo, useState } from "react";
4
+ import { cn } from "../lib/utils.js";
5
+ import { EmptyState } from "./empty-state.js";
6
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
7
+ /**
8
+ * A table you can sort by clicking a header.
9
+ *
10
+ * Rows are an array, not a `DataPort` collection — this sorts client-side over
11
+ * rows it already has, which is only correct for a set small enough to hold in
12
+ * full. Once the data is paginated, sorting has to happen at the source or the
13
+ * user is sorting one page and being told it is the whole set; use `DataTable`
14
+ * with a server-sorted request for that. The two are different tools and this
15
+ * one does not pretend to be the other.
16
+ *
17
+ * `aria-sort` on the active header is the part that matters. A caret glyph is
18
+ * invisible to a screen reader, and sort order is exactly the sort of state
19
+ * that goes unnoticed without it. Inactive sortable headers show a neutral
20
+ * affordance so it is discoverable which columns can be sorted at all.
21
+ */ export function SortableTable({ caption, rows, columns, rowKey, initialSort, empty, className }) {
22
+ const [sort, setSort] = useState(initialSort ?? null);
23
+ const sorted = useMemo(()=>{
24
+ if (sort === null) return rows;
25
+ const column = columns.find((candidate)=>candidate.key === sort.key);
26
+ if (column?.sortValue === undefined) return rows;
27
+ const { sortValue } = column;
28
+ // Sorts a copy: `rows` belongs to the caller and may be frozen.
29
+ return [
30
+ ...rows
31
+ ].sort((a, b)=>{
32
+ const left = sortValue(a);
33
+ const right = sortValue(b);
34
+ const comparison = typeof left === "number" && typeof right === "number" ? left - right : String(left).localeCompare(String(right));
35
+ return sort.direction === "asc" ? comparison : -comparison;
36
+ });
37
+ }, [
38
+ rows,
39
+ columns,
40
+ sort
41
+ ]);
42
+ const toggle = (key)=>{
43
+ setSort((current)=>current?.key === key ? {
44
+ key,
45
+ direction: current.direction === "asc" ? "desc" : "asc"
46
+ } : {
47
+ key,
48
+ direction: "asc"
49
+ });
50
+ };
51
+ if (rows.length === 0) {
52
+ return /*#__PURE__*/ _jsx(_Fragment, {
53
+ children: empty ?? /*#__PURE__*/ _jsx(EmptyState, {
54
+ title: "Nothing to show"
55
+ })
56
+ });
57
+ }
58
+ return /*#__PURE__*/ _jsxs(Table, {
59
+ "aria-label": caption,
60
+ className: className,
61
+ children: [
62
+ /*#__PURE__*/ _jsx(TableHeader, {
63
+ children: /*#__PURE__*/ _jsx(TableRow, {
64
+ children: columns.map((column)=>{
65
+ const active = sort?.key === column.key;
66
+ const sortable = column.sortValue !== undefined;
67
+ return /*#__PURE__*/ _jsx(TableHead, {
68
+ scope: "col",
69
+ "aria-sort": active ? sort.direction === "asc" ? "ascending" : "descending" : undefined,
70
+ className: cn(column.align === "right" && "text-right", column.className),
71
+ children: sortable ? /*#__PURE__*/ _jsxs("button", {
72
+ type: "button",
73
+ onClick: ()=>toggle(column.key),
74
+ className: cn("inline-flex items-center gap-1 rounded-sm transition-colors hover:text-foreground", !active && "text-muted-foreground"),
75
+ children: [
76
+ column.header,
77
+ active ? sort.direction === "asc" ? /*#__PURE__*/ _jsx(ChevronUp, {
78
+ className: "size-3.5",
79
+ "aria-hidden": true
80
+ }) : /*#__PURE__*/ _jsx(ChevronDown, {
81
+ className: "size-3.5",
82
+ "aria-hidden": true
83
+ }) : /*#__PURE__*/ _jsx(ChevronsUpDown, {
84
+ className: "size-3.5 opacity-50",
85
+ "aria-hidden": true
86
+ })
87
+ ]
88
+ }) : column.header
89
+ }, column.key);
90
+ })
91
+ })
92
+ }),
93
+ /*#__PURE__*/ _jsx(TableBody, {
94
+ children: sorted.map((row)=>/*#__PURE__*/ _jsx(TableRow, {
95
+ children: columns.map((column)=>/*#__PURE__*/ _jsx(TableCell, {
96
+ className: cn(column.align === "right" && "text-right", column.className),
97
+ children: column.cell(row)
98
+ }, column.key))
99
+ }, rowKey(row)))
100
+ })
101
+ ]
102
+ });
103
+ }
@@ -0,0 +1,27 @@
1
+ export type SparklineProps = {
2
+ readonly values: readonly number[];
3
+ /**
4
+ * What the shape means, in words — "Up 12% over 30 days". Without it the
5
+ * sparkline is invisible to anyone not looking at it, and a shape with no
6
+ * axes and no labels cannot be inferred from its markup.
7
+ */
8
+ readonly summary: string;
9
+ readonly className?: string;
10
+ };
11
+ /**
12
+ * A trend, at the size of a word.
13
+ *
14
+ * No axes, no ticks, no labels — that is the form, not an omission. A sparkline
15
+ * says "rising", "flat", "spiky" beside a number that says exactly how much; the
16
+ * moment it needs a scale it has outgrown itself and wants to be a chart.
17
+ *
18
+ * It is scaled between the series' own minimum and maximum rather than from
19
+ * zero. This is the one place a non-zero baseline is right: the shape is the
20
+ * only message, and anchoring to zero flattens every real series into a
21
+ * horizontal line near the top of a 24px box.
22
+ *
23
+ * `role="img"` with the caller's summary as its name. The summary is required
24
+ * for the same reason `alt` is: a picture with no text alternative is simply
25
+ * missing for some readers, and this one has no fallback content to fall back to.
26
+ */
27
+ export declare function Sparkline({ values, summary, className }: SparklineProps): import("react").JSX.Element;
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { linePath, scaleLinear } from "../lib/chart-geometry.js";
3
+ import { seriesColor } from "../lib/chart-palette.js";
4
+ import { cn } from "../lib/utils.js";
5
+ const WIDTH = 96;
6
+ const HEIGHT = 24;
7
+ /**
8
+ * A trend, at the size of a word.
9
+ *
10
+ * No axes, no ticks, no labels — that is the form, not an omission. A sparkline
11
+ * says "rising", "flat", "spiky" beside a number that says exactly how much; the
12
+ * moment it needs a scale it has outgrown itself and wants to be a chart.
13
+ *
14
+ * It is scaled between the series' own minimum and maximum rather than from
15
+ * zero. This is the one place a non-zero baseline is right: the shape is the
16
+ * only message, and anchoring to zero flattens every real series into a
17
+ * horizontal line near the top of a 24px box.
18
+ *
19
+ * `role="img"` with the caller's summary as its name. The summary is required
20
+ * for the same reason `alt` is: a picture with no text alternative is simply
21
+ * missing for some readers, and this one has no fallback content to fall back to.
22
+ */ export function Sparkline({ values, summary, className }) {
23
+ if (values.length < 2) {
24
+ return /*#__PURE__*/ _jsx("span", {
25
+ className: cn("inline-block", className),
26
+ role: "img",
27
+ "aria-label": summary
28
+ });
29
+ }
30
+ const min = Math.min(...values);
31
+ const max = Math.max(...values);
32
+ const span = max - min;
33
+ const stepX = WIDTH / (values.length - 1);
34
+ const points = values.map((value, index)=>({
35
+ x: index * stepX,
36
+ // A flat series has no span to scale against; centre it rather than
37
+ // dividing by zero.
38
+ y: span === 0 ? HEIGHT / 2 : HEIGHT - scaleLinear(value - min, span, HEIGHT - 4) - 2
39
+ }));
40
+ const last = points[points.length - 1];
41
+ return /*#__PURE__*/ _jsxs("svg", {
42
+ viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
43
+ className: cn("h-6 w-24", className),
44
+ role: "img",
45
+ "aria-label": summary,
46
+ focusable: "false",
47
+ children: [
48
+ /*#__PURE__*/ _jsx("path", {
49
+ d: linePath(points),
50
+ fill: "none",
51
+ stroke: seriesColor(0),
52
+ strokeWidth: 2,
53
+ strokeLinecap: "round",
54
+ strokeLinejoin: "round"
55
+ }),
56
+ last === undefined ? null : // The end dot marks "now" — which end is the current period is
57
+ // otherwise ambiguous on a shape with no axis.
58
+ /*#__PURE__*/ _jsx("circle", {
59
+ cx: last.x,
60
+ cy: last.y,
61
+ r: 2.5,
62
+ fill: seriesColor(0),
63
+ stroke: "var(--card)",
64
+ strokeWidth: 2
65
+ })
66
+ ]
67
+ });
68
+ }
@@ -0,0 +1,55 @@
1
+ import type { ReactNode } from "react";
2
+ export type StatGridColumns = 2 | 3 | 4 | 5;
3
+ export type StatGridProps = {
4
+ readonly children: ReactNode;
5
+ /** Responsive column count; defaults to four-up on wide screens. */
6
+ readonly columns?: StatGridColumns;
7
+ readonly className?: string;
8
+ };
9
+ /**
10
+ * The KPI band: a fixed responsive grid of `StatGridItem`s.
11
+ *
12
+ * Fixed columns rather than auto-fit, because a KPI row is designed as a set —
13
+ * four headline numbers are four columns, and a row that reflows to five-up on
14
+ * a wider screen silently reorders what the reader compares side by side.
15
+ */
16
+ export declare function StatGrid({ children, columns, className }: StatGridProps): import("react").JSX.Element;
17
+ export type StatGridItemProps = {
18
+ readonly label: string;
19
+ /** Pre-formatted. The tile does not know your units. */
20
+ readonly value: string;
21
+ /** Small print under the number — a rate, a share, a caveat. */
22
+ readonly sub?: ReactNode;
23
+ /** Orange action tone — reserve for genuine action/positive emphasis. */
24
+ readonly accent?: boolean;
25
+ /** Semantic danger — failure counts, not the action accent. */
26
+ readonly danger?: boolean;
27
+ /** Headline tile: larger value + padding. */
28
+ readonly emphasis?: boolean;
29
+ /** Optional period-over-period badge (e.g. a `DeltaBadge`). */
30
+ readonly delta?: ReactNode;
31
+ /** Custom sparkline slot; overrides `sparklineValues` when both are set. */
32
+ readonly sparkline?: ReactNode;
33
+ /** When set (and `sparkline` is omitted), renders the built-in trend line. */
34
+ readonly sparklineValues?: readonly number[];
35
+ /** Words for the built-in trend's shape; defaults to "{label} trend". */
36
+ readonly sparklineLabel?: string;
37
+ /**
38
+ * When set, the tile is a real `<button>` and activates on click/Enter/Space.
39
+ * Static tiles stay a non-interactive `div` so they do not join the tab order.
40
+ */
41
+ readonly onClick?: () => void;
42
+ readonly className?: string;
43
+ };
44
+ /**
45
+ * One KPI: caption label, headline number, optional delta, trend and small
46
+ * print. The value never carries direction by colour alone — `accent` and
47
+ * `danger` are semantic tones the caller opts into, and the delta slot carries
48
+ * its own words.
49
+ *
50
+ * Interaction is opt-in. With `onClick`, the shell is a `<button>` (not a div
51
+ * wearing `role="button"`) so keyboard activation is native. Shared layout
52
+ * classes stay identical; only the interactive shell adds hover/press
53
+ * affordance and the `text-left`/`w-full` button reset.
54
+ */
55
+ export declare function StatGridItem({ label, value, sub, accent, danger, emphasis, delta, sparkline, sparklineValues, sparklineLabel, onClick, className, }: StatGridItemProps): import("react").JSX.Element;
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ import { Sparkline } from "./sparkline.js";
4
+ // Spelled out rather than composed at runtime — Tailwind's scanner only emits
5
+ // utilities it can see literally in the source.
6
+ const COLUMN_CLASS = {
7
+ 2: "grid-cols-2",
8
+ 3: "grid-cols-2 sm:grid-cols-3",
9
+ 4: "grid-cols-2 sm:grid-cols-4",
10
+ 5: "grid-cols-2 sm:grid-cols-5"
11
+ };
12
+ /**
13
+ * The KPI band: a fixed responsive grid of `StatGridItem`s.
14
+ *
15
+ * Fixed columns rather than auto-fit, because a KPI row is designed as a set —
16
+ * four headline numbers are four columns, and a row that reflows to five-up on
17
+ * a wider screen silently reorders what the reader compares side by side.
18
+ */ export function StatGrid({ children, columns = 4, className }) {
19
+ return /*#__PURE__*/ _jsx("div", {
20
+ className: cn("grid gap-3", COLUMN_CLASS[columns], className),
21
+ children: children
22
+ });
23
+ }
24
+ function statValueClass(accent, danger) {
25
+ if (danger === true) return "text-destructive";
26
+ if (accent === true) return "text-primary-emphasis";
27
+ return "text-foreground";
28
+ }
29
+ /**
30
+ * One KPI: caption label, headline number, optional delta, trend and small
31
+ * print. The value never carries direction by colour alone — `accent` and
32
+ * `danger` are semantic tones the caller opts into, and the delta slot carries
33
+ * its own words.
34
+ *
35
+ * Interaction is opt-in. With `onClick`, the shell is a `<button>` (not a div
36
+ * wearing `role="button"`) so keyboard activation is native. Shared layout
37
+ * classes stay identical; only the interactive shell adds hover/press
38
+ * affordance and the `text-left`/`w-full` button reset.
39
+ */ export function StatGridItem({ label, value, sub, accent, danger, emphasis, delta, sparkline, sparklineValues, sparklineLabel, onClick, className }) {
40
+ const sparklineNode = sparkline ?? (sparklineValues !== undefined && sparklineValues.length > 0 ? /*#__PURE__*/ _jsx(Sparkline, {
41
+ values: sparklineValues,
42
+ summary: sparklineLabel ?? `${label} trend`
43
+ }) : null);
44
+ const shell = cn("flex flex-col gap-1.5 rounded-lg border border-border bg-card", emphasis === true ? "p-5 shadow-sm" : "p-4", className);
45
+ const body = /*#__PURE__*/ _jsxs(_Fragment, {
46
+ children: [
47
+ /*#__PURE__*/ _jsx("span", {
48
+ className: "text-[10px] font-semibold tracking-[0.12em] uppercase text-muted-foreground",
49
+ children: label
50
+ }),
51
+ /*#__PURE__*/ _jsxs("div", {
52
+ className: "flex items-baseline gap-2",
53
+ children: [
54
+ /*#__PURE__*/ _jsx("span", {
55
+ className: cn("font-black leading-none tabular-nums", emphasis === true ? "text-[34px]" : "text-[26px]", statValueClass(accent, danger)),
56
+ children: value
57
+ }),
58
+ delta
59
+ ]
60
+ }),
61
+ sparklineNode === null ? null : /*#__PURE__*/ _jsx("div", {
62
+ className: "mt-0.5",
63
+ children: sparklineNode
64
+ }),
65
+ sub === undefined ? null : /*#__PURE__*/ _jsx("span", {
66
+ className: "text-[10px] tracking-[0.08em] uppercase text-muted-foreground",
67
+ children: sub
68
+ })
69
+ ]
70
+ });
71
+ if (onClick === undefined) {
72
+ return /*#__PURE__*/ _jsx("div", {
73
+ "data-slot": "stat-grid-item",
74
+ className: shell,
75
+ children: body
76
+ });
77
+ }
78
+ return /*#__PURE__*/ _jsx("button", {
79
+ type: "button",
80
+ "data-slot": "stat-grid-item",
81
+ onClick: onClick,
82
+ className: cn(shell, // text-left/w-full: button UA styles centre content and shrink-wrap.
83
+ // Hover/press only on the interactive shell so static tiles stay quiet.
84
+ "w-full cursor-pointer text-left transition-colors hover:border-primary-emphasis active:brightness-95"),
85
+ children: body
86
+ });
87
+ }
@@ -0,0 +1,13 @@
1
+ export type StatusDotTone = "neutral" | "emphasis" | "success" | "warning" | "danger";
2
+ export type StatusDotProps = {
3
+ /** Names the state for assistive tech. Required — a bare colour says nothing. */
4
+ readonly label: string;
5
+ /** Adds a ring that pulses outward: this state is live and still changing. */
6
+ readonly live?: boolean;
7
+ readonly tone?: StatusDotTone;
8
+ /** `sm` (8px, default) beside body text; `xs` (6px) inside dense chrome. */
9
+ readonly size?: StatusDotSize;
10
+ readonly className?: string;
11
+ };
12
+ export type StatusDotSize = "xs" | "sm";
13
+ export declare function StatusDot({ label, live, tone, size, className }: StatusDotProps): import("react").JSX.Element;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ /**
4
+ * Three tones, not one per status, and that is deliberate. A dot is a non-text
5
+ * UI element and has to clear 3:1 against both the page and a card; the only
6
+ * fills in the theme that do so at 8px are the emphasis orange, the destructive
7
+ * red and the muted ink. A green "done" dot would have to invent a token, and
8
+ * the run's own `Badge` already carries the full status colour on a
9
+ * contrast-tested fill/foreground pair. The dot marks *liveness*; the badge
10
+ * names the state.
11
+ *
12
+ * Classes are spelled out rather than composed at runtime — Tailwind's scanner
13
+ * only emits utilities it can see literally in the source.
14
+ */ const TONE_CLASS = {
15
+ neutral: "bg-muted-foreground",
16
+ emphasis: "bg-primary-emphasis",
17
+ // success/warning use emphasis orange until dedicated high-contrast
18
+ // fills clear 3:1 at 8px; Badge remains the full-status carrier.
19
+ success: "bg-primary-emphasis",
20
+ warning: "bg-primary-emphasis",
21
+ danger: "bg-destructive"
22
+ };
23
+ /**
24
+ * A state marker carrying its own accessible name — a coloured dot with no
25
+ * label is invisible to a screen reader, so the label is required rather than
26
+ * optional.
27
+ *
28
+ * The `live` ring is a separate `aria-hidden` element: motion says "still
29
+ * changing" to people who can see it, and the label already says so to those
30
+ * who cannot. It stills under the theme's reduced-motion rule.
31
+ */ const SIZE_CLASS = {
32
+ xs: "size-1.5",
33
+ sm: "size-2"
34
+ };
35
+ export function StatusDot({ label, live = false, tone = "neutral", size = "sm", className }) {
36
+ return /*#__PURE__*/ _jsxs("span", {
37
+ role: "img",
38
+ "aria-label": label,
39
+ className: cn("relative inline-flex shrink-0", SIZE_CLASS[size], className),
40
+ children: [
41
+ live ? /*#__PURE__*/ _jsx("span", {
42
+ "aria-hidden": true,
43
+ className: cn("absolute inset-0 rounded-full [animation:corbits-status-pulse_1.8s_ease-out_infinite]", TONE_CLASS[tone])
44
+ }) : null,
45
+ /*#__PURE__*/ _jsx("span", {
46
+ "aria-hidden": true,
47
+ className: cn("relative size-full rounded-full", TONE_CLASS[tone])
48
+ })
49
+ ]
50
+ });
51
+ }
@@ -0,0 +1,21 @@
1
+ import type { StepListItem } from "../lib/workflow-registry.js";
2
+ export type StepListRowProps = {
3
+ readonly step: StepListItem;
4
+ readonly className?: string;
5
+ };
6
+ /** One step row — a mark carrying its own glyph and colour, a name, and the
7
+ * status spelled out in text so it never depends on colour alone. */
8
+ export declare function StepListRow({ step, className }: StepListRowProps): import("react").JSX.Element;
9
+ export type StepListProps = {
10
+ readonly steps: readonly StepListItem[];
11
+ /** Names the list. Required — a bare sequence of rows has no subject. */
12
+ readonly label?: string;
13
+ readonly className?: string;
14
+ };
15
+ /**
16
+ * A run's steps and how far along each one is. `aria-live="polite"` so a
17
+ * step flipping to done or failed is announced without stealing focus from
18
+ * wherever the reader actually is — the same rule `progress-checklist.tsx`
19
+ * follows for a long-running operation's checklist.
20
+ */
21
+ export declare function StepList({ steps, label, className }: StepListProps): import("react").JSX.Element | null;
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Check, X } from "lucide-react";
3
+ import { cn } from "../lib/utils.js";
4
+ import { StatusDot } from "./status-dot.js";
5
+ const STATE_LABEL = {
6
+ pending: "Pending",
7
+ active: "Now",
8
+ done: "Done",
9
+ failed: "Failed"
10
+ };
11
+ const MARK_CLASS = {
12
+ pending: "border-input text-muted-foreground",
13
+ active: "border-primary-emphasis bg-primary/10 text-primary-emphasis",
14
+ done: "border-transparent bg-success text-success-foreground",
15
+ failed: "border-transparent bg-destructive text-destructive-foreground"
16
+ };
17
+ const LABEL_CLASS = {
18
+ pending: "text-muted-foreground",
19
+ active: "font-medium text-primary-emphasis",
20
+ done: "text-foreground",
21
+ failed: "font-medium text-destructive"
22
+ };
23
+ /** One step row — a mark carrying its own glyph and colour, a name, and the
24
+ * status spelled out in text so it never depends on colour alone. */ export function StepListRow({ step, className }) {
25
+ return /*#__PURE__*/ _jsxs("li", {
26
+ "data-step-status": step.status,
27
+ className: cn("grid grid-cols-[20px_minmax(0,1fr)_auto] items-start gap-2 py-1.5", className),
28
+ children: [
29
+ /*#__PURE__*/ _jsx("span", {
30
+ "aria-hidden": true,
31
+ className: cn("mt-0.5 grid size-5 place-items-center rounded-sm border text-[10px] font-semibold", MARK_CLASS[step.status]),
32
+ children: step.status === "done" ? /*#__PURE__*/ _jsx(Check, {
33
+ className: "size-3"
34
+ }) : step.status === "failed" ? /*#__PURE__*/ _jsx(X, {
35
+ className: "size-3"
36
+ }) : step.status === "active" ? /*#__PURE__*/ _jsx(StatusDot, {
37
+ label: "",
38
+ tone: "emphasis",
39
+ live: true
40
+ }) : null
41
+ }),
42
+ /*#__PURE__*/ _jsxs("span", {
43
+ className: "min-w-0",
44
+ children: [
45
+ /*#__PURE__*/ _jsx("span", {
46
+ className: cn("block truncate text-sm", LABEL_CLASS[step.status]),
47
+ children: step.name
48
+ }),
49
+ step.meta === undefined ? null : /*#__PURE__*/ _jsx("span", {
50
+ className: "block truncate text-xs text-muted-foreground",
51
+ children: step.meta
52
+ })
53
+ ]
54
+ }),
55
+ /*#__PURE__*/ _jsx("span", {
56
+ className: "shrink-0 text-[10.5px] font-bold uppercase tracking-[0.05em] text-muted-foreground",
57
+ children: STATE_LABEL[step.status]
58
+ })
59
+ ]
60
+ });
61
+ }
62
+ /**
63
+ * A run's steps and how far along each one is. `aria-live="polite"` so a
64
+ * step flipping to done or failed is announced without stealing focus from
65
+ * wherever the reader actually is — the same rule `progress-checklist.tsx`
66
+ * follows for a long-running operation's checklist.
67
+ */ export function StepList({ steps, label = "Steps", className }) {
68
+ if (steps.length === 0) return null;
69
+ return /*#__PURE__*/ _jsx("ol", {
70
+ "aria-label": label,
71
+ "aria-live": "polite",
72
+ className: cn("flex flex-col", className),
73
+ children: steps.map((step)=>/*#__PURE__*/ _jsx(StepListRow, {
74
+ step: step
75
+ }, step.id))
76
+ });
77
+ }
@@ -0,0 +1,23 @@
1
+ export type SwitchProps = {
2
+ readonly checked: boolean;
3
+ readonly onCheckedChange: (checked: boolean) => void;
4
+ /** Accessible name. Omit only when a `<label htmlFor>` already names it. */
5
+ readonly label?: string;
6
+ readonly id?: string;
7
+ readonly disabled?: boolean;
8
+ /** Ids of the text describing the setting, for `aria-describedby`. */
9
+ readonly describedBy?: string;
10
+ readonly className?: string;
11
+ };
12
+ /**
13
+ * An on/off setting.
14
+ *
15
+ * `role="switch"` on a real `<button>`, not a styled checkbox: a switch takes
16
+ * effect immediately and a checkbox implies a form you still have to submit.
17
+ * Screen readers say "on/off" for a switch and "checked/unchecked" for a
18
+ * checkbox, and that difference is the whole reason the role exists.
19
+ *
20
+ * The knob is a `<span>` inside the button rather than a `::after`, so its
21
+ * transform animates independently of the track's colour transition.
22
+ */
23
+ export declare function Switch({ checked, onCheckedChange, label, id, disabled, describedBy, className, }: SwitchProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ /**
4
+ * An on/off setting.
5
+ *
6
+ * `role="switch"` on a real `<button>`, not a styled checkbox: a switch takes
7
+ * effect immediately and a checkbox implies a form you still have to submit.
8
+ * Screen readers say "on/off" for a switch and "checked/unchecked" for a
9
+ * checkbox, and that difference is the whole reason the role exists.
10
+ *
11
+ * The knob is a `<span>` inside the button rather than a `::after`, so its
12
+ * transform animates independently of the track's colour transition.
13
+ */ export function Switch({ checked, onCheckedChange, label, id, disabled = false, describedBy, className }) {
14
+ return /*#__PURE__*/ _jsx("button", {
15
+ type: "button",
16
+ id: id,
17
+ role: "switch",
18
+ "aria-checked": checked,
19
+ "aria-label": label,
20
+ "aria-describedby": describedBy,
21
+ disabled: disabled,
22
+ onClick: ()=>onCheckedChange(!checked),
23
+ className: cn("inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-transparent p-0.5 transition-colors disabled:opacity-50", checked ? "bg-primary" : "bg-input", className),
24
+ children: /*#__PURE__*/ _jsx("span", {
25
+ "aria-hidden": true,
26
+ className: cn("size-4 rounded-full bg-background transition-transform", checked && "translate-x-4")
27
+ })
28
+ });
29
+ }
@@ -0,0 +1,8 @@
1
+ import type * as React from "react";
2
+ export declare function Table({ className, ...props }: React.ComponentProps<"table">): React.JSX.Element;
3
+ export declare function TableHeader({ className, ...props }: React.ComponentProps<"thead">): React.JSX.Element;
4
+ export declare function TableBody({ className, ...props }: React.ComponentProps<"tbody">): React.JSX.Element;
5
+ export declare function TableRow({ className, ...props }: React.ComponentProps<"tr">): React.JSX.Element;
6
+ export declare function TableHead({ className, ...props }: React.ComponentProps<"th">): React.JSX.Element;
7
+ export declare function TableCell({ className, ...props }: React.ComponentProps<"td">): React.JSX.Element;
8
+ export declare function TableCaption({ className, ...props }: React.ComponentProps<"caption">): React.JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ export function Table({ className, ...props }) {
4
+ return /*#__PURE__*/ _jsx("div", {
5
+ className: "relative w-full overflow-x-auto",
6
+ children: /*#__PURE__*/ _jsx("table", {
7
+ "data-slot": "table",
8
+ className: cn("w-full caption-bottom text-sm", className),
9
+ ...props
10
+ })
11
+ });
12
+ }
13
+ export function TableHeader({ className, ...props }) {
14
+ return /*#__PURE__*/ _jsx("thead", {
15
+ className: cn("[&_tr]:border-b [&_tr]:border-border", className),
16
+ ...props
17
+ });
18
+ }
19
+ export function TableBody({ className, ...props }) {
20
+ return /*#__PURE__*/ _jsx("tbody", {
21
+ className: cn("[&_tr:last-child]:border-0", className),
22
+ ...props
23
+ });
24
+ }
25
+ export function TableRow({ className, ...props }) {
26
+ return /*#__PURE__*/ _jsx("tr", {
27
+ className: cn("border-b border-border transition-colors hover:bg-muted", className),
28
+ ...props
29
+ });
30
+ }
31
+ export function TableHead({ className, ...props }) {
32
+ return /*#__PURE__*/ _jsx("th", {
33
+ className: cn("h-10 px-3 text-left align-middle font-medium text-muted-foreground", className),
34
+ ...props
35
+ });
36
+ }
37
+ export function TableCell({ className, ...props }) {
38
+ return /*#__PURE__*/ _jsx("td", {
39
+ className: cn("p-3 align-middle", className),
40
+ ...props
41
+ });
42
+ }
43
+ export function TableCaption({ className, ...props }) {
44
+ return /*#__PURE__*/ _jsx("caption", {
45
+ className: cn("mt-3 text-sm text-muted-foreground", className),
46
+ ...props
47
+ });
48
+ }