@corbits/react-ui 0.1.0 → 0.2.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 (170) hide show
  1. package/LICENSE +174 -499
  2. package/README.md +228 -85
  3. package/dist/blocks/login/auth-layout.d.ts +1 -1
  4. package/dist/blocks/login/auth-layout.js +3 -33
  5. package/dist/blocks/login/login-form.js +6 -119
  6. package/dist/hooks/use-command-palette-navigation.js +18 -17
  7. package/dist/hooks/use-controllable-state.d.ts +2 -2
  8. package/dist/hooks/use-controllable-state.js +23 -15
  9. package/dist/hooks/use-delayed-autofocus.js +10 -11
  10. package/dist/hooks/use-dismissable-popover.d.ts +2 -2
  11. package/dist/hooks/use-dismissable-popover.js +21 -29
  12. package/dist/hooks/use-list-selection.js +54 -85
  13. package/dist/hooks/use-prefers-reduced-motion.js +8 -4
  14. package/dist/hooks/use-scroll-current-into-view.js +5 -7
  15. package/dist/index.d.ts +0 -11
  16. package/dist/index.js +10 -19
  17. package/dist/lib/artifact.js +16 -35
  18. package/dist/lib/chart-geometry.js +33 -20
  19. package/dist/lib/chart-palette.js +17 -31
  20. package/dist/lib/chat-message.js +32 -13
  21. package/dist/lib/chat-parts.js +2 -1
  22. package/dist/lib/csv.js +42 -26
  23. package/dist/lib/metrics.js +30 -48
  24. package/dist/lib/now-item.js +26 -25
  25. package/dist/lib/relative-time.js +14 -29
  26. package/dist/lib/theme.js +32 -30
  27. package/dist/lib/url.js +9 -8
  28. package/dist/lib/workflow-display-flow.js +37 -33
  29. package/dist/lib/workflow-registry.d.ts +0 -41
  30. package/dist/lib/workflow-registry.js +13 -23
  31. package/dist/lib/workflow-run-error.js +16 -20
  32. package/dist/lib/workflow-run-progress.js +94 -52
  33. package/dist/lib/workflow-run.js +17 -13
  34. package/dist/styles.css +11 -11
  35. package/dist/test/render-hook.js +11 -12
  36. package/dist/theme.css +51 -14
  37. package/dist/ui/agent-turn.d.ts +1 -1
  38. package/dist/ui/agent-turn.js +4 -37
  39. package/dist/ui/animated-number.js +15 -28
  40. package/dist/ui/approval-card.js +5 -90
  41. package/dist/ui/artifact-body.js +47 -125
  42. package/dist/ui/artifact-notice.d.ts +1 -1
  43. package/dist/ui/artifact-notice.js +9 -16
  44. package/dist/ui/avatar.d.ts +1 -1
  45. package/dist/ui/avatar.js +11 -48
  46. package/dist/ui/badge.js +4 -12
  47. package/dist/ui/bar-chart.d.ts +1 -1
  48. package/dist/ui/bar-chart.js +22 -74
  49. package/dist/ui/block-card.js +7 -45
  50. package/dist/ui/boot-screen.d.ts +1 -1
  51. package/dist/ui/boot-screen.js +4 -33
  52. package/dist/ui/bulk-action-bar.d.ts +1 -1
  53. package/dist/ui/bulk-action-bar.js +14 -33
  54. package/dist/ui/button.js +10 -24
  55. package/dist/ui/card.js +5 -25
  56. package/dist/ui/category-bars.js +14 -59
  57. package/dist/ui/chart-frame.d.ts +2 -2
  58. package/dist/ui/chart-frame.js +6 -85
  59. package/dist/ui/chat-composer.js +4 -16
  60. package/dist/ui/chat-dock-timing.js +7 -3
  61. package/dist/ui/chat-input.js +20 -100
  62. package/dist/ui/chat-panel.d.ts +1 -1
  63. package/dist/ui/chat-panel.js +7 -41
  64. package/dist/ui/chat-thread.js +13 -31
  65. package/dist/ui/checkbox.js +10 -52
  66. package/dist/ui/command-palette.js +71 -246
  67. package/dist/ui/compare-body.d.ts +1 -1
  68. package/dist/ui/compare-body.js +28 -98
  69. package/dist/ui/confirm-button.js +8 -15
  70. package/dist/ui/corbits-mark.js +7 -16
  71. package/dist/ui/csv-table.d.ts +1 -1
  72. package/dist/ui/csv-table.js +8 -50
  73. package/dist/ui/dialog.d.ts +1 -1
  74. package/dist/ui/dialog.js +32 -62
  75. package/dist/ui/dictation-status-line.js +7 -25
  76. package/dist/ui/dither-canvas.d.ts +1 -1
  77. package/dist/ui/dither-canvas.js +31 -56
  78. package/dist/ui/embed-body.d.ts +1 -1
  79. package/dist/ui/embed-body.js +7 -56
  80. package/dist/ui/empty-state.d.ts +1 -1
  81. package/dist/ui/empty-state.js +3 -26
  82. package/dist/ui/file-input.js +17 -37
  83. package/dist/ui/filter-bar.d.ts +1 -1
  84. package/dist/ui/filter-bar.js +11 -71
  85. package/dist/ui/filter-chip.d.ts +1 -1
  86. package/dist/ui/filter-chip.js +7 -15
  87. package/dist/ui/gate-block.d.ts +2 -1
  88. package/dist/ui/gate-block.js +4 -29
  89. package/dist/ui/horizontal-stepper.d.ts +1 -1
  90. package/dist/ui/horizontal-stepper.js +18 -45
  91. package/dist/ui/input.js +1 -6
  92. package/dist/ui/inspector-shell.d.ts +5 -5
  93. package/dist/ui/inspector-shell.js +14 -81
  94. package/dist/ui/intake-form.d.ts +1 -1
  95. package/dist/ui/intake-form.js +21 -97
  96. package/dist/ui/kind-card-grid.js +9 -31
  97. package/dist/ui/library-page-header.js +4 -32
  98. package/dist/ui/list-detail.js +5 -35
  99. package/dist/ui/live-run-banner.d.ts +1 -1
  100. package/dist/ui/live-run-banner.js +5 -15
  101. package/dist/ui/live-status-line.js +6 -24
  102. package/dist/ui/menu.js +7 -24
  103. package/dist/ui/message-bubble.d.ts +3 -3
  104. package/dist/ui/message-bubble.js +5 -44
  105. package/dist/ui/mic-button.d.ts +1 -1
  106. package/dist/ui/mic-button.js +15 -21
  107. package/dist/ui/mic-permission-dialog.js +5 -41
  108. package/dist/ui/notifications-bell.js +9 -43
  109. package/dist/ui/now-cards.d.ts +2 -2
  110. package/dist/ui/now-cards.js +16 -94
  111. package/dist/ui/page-panel.js +11 -11
  112. package/dist/ui/page-shell.js +4 -11
  113. package/dist/ui/parts-renderer.js +51 -149
  114. package/dist/ui/profile-card.js +7 -111
  115. package/dist/ui/progress-checklist.js +11 -49
  116. package/dist/ui/provider-mark.d.ts +2 -2
  117. package/dist/ui/provider-mark.js +9 -16
  118. package/dist/ui/quick-reply-chips.js +6 -14
  119. package/dist/ui/quote-card.js +12 -32
  120. package/dist/ui/reasoning-block.d.ts +1 -1
  121. package/dist/ui/reasoning-block.js +14 -33
  122. package/dist/ui/research-body.js +32 -213
  123. package/dist/ui/rich-empty-state.js +6 -48
  124. package/dist/ui/run-now-button.js +14 -15
  125. package/dist/ui/section.d.ts +1 -1
  126. package/dist/ui/section.js +4 -40
  127. package/dist/ui/select.js +1 -15
  128. package/dist/ui/selection-checkbox.d.ts +1 -1
  129. package/dist/ui/selection-checkbox.js +11 -29
  130. package/dist/ui/settings-panel.js +7 -50
  131. package/dist/ui/shimmer-text.js +3 -6
  132. package/dist/ui/sidebar-item-row.d.ts +1 -1
  133. package/dist/ui/sidebar-item-row.js +9 -39
  134. package/dist/ui/sidebar-panel.js +15 -40
  135. package/dist/ui/sidebar.js +15 -76
  136. package/dist/ui/skeleton.js +3 -7
  137. package/dist/ui/sortable-table.js +29 -72
  138. package/dist/ui/sparkline.js +14 -38
  139. package/dist/ui/stat-grid.js +17 -52
  140. package/dist/ui/status-dot.d.ts +1 -1
  141. package/dist/ui/status-dot.js +8 -20
  142. package/dist/ui/step-list.d.ts +1 -0
  143. package/dist/ui/step-list.js +11 -50
  144. package/dist/ui/switch.js +4 -16
  145. package/dist/ui/table.js +7 -32
  146. package/dist/ui/tabs.js +39 -54
  147. package/dist/ui/textarea.js +10 -19
  148. package/dist/ui/theme-provider.js +54 -78
  149. package/dist/ui/theme-toggle.js +13 -16
  150. package/dist/ui/thinking-indicator.js +3 -15
  151. package/dist/ui/thinking-label.js +16 -35
  152. package/dist/ui/thinking-mark.d.ts +1 -1
  153. package/dist/ui/thinking-mark.js +4 -145
  154. package/dist/ui/time-series-chart.js +43 -174
  155. package/dist/ui/toast.js +12 -20
  156. package/dist/ui/token-mosaic.js +5 -43
  157. package/dist/ui/tool-block.js +18 -70
  158. package/dist/ui/tool-narrative.d.ts +1 -1
  159. package/dist/ui/tool-narrative.js +10 -57
  160. package/dist/ui/tool-picker.js +57 -145
  161. package/dist/ui/tooltip.d.ts +1 -1
  162. package/dist/ui/tooltip.js +4 -33
  163. package/dist/ui/top-bar.js +14 -72
  164. package/dist/ui/trace-waterfall.d.ts +1 -1
  165. package/dist/ui/trace-waterfall.js +21 -93
  166. package/dist/ui/typing-indicator.d.ts +1 -1
  167. package/dist/ui/typing-indicator.js +4 -18
  168. package/dist/ui/view-toggle.js +10 -29
  169. package/dist/ui/voice-waveform.js +3 -11
  170. package/package.json +94 -535
@@ -14,7 +14,7 @@ export type SidebarItemRowProps = {
14
14
  * one more frame (~150ms) so the leave transition gets to play. List
15
15
  * membership is the consumer's data — nothing here manages it for you.
16
16
  */
17
- readonly leaving?: boolean;
17
+ readonly leaving?: boolean | undefined;
18
18
  readonly onSelect?: () => void;
19
19
  readonly className?: string;
20
20
  };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useEffect, useState } from "react";
3
4
  import { cn } from "../lib/utils.js";
@@ -19,46 +20,15 @@ import { cn } from "../lib/utils.js";
19
20
  * component to supply one.
20
21
  *
21
22
  * Not `ui/sidebar`'s `SidebarItem` — that's the single-column family's row.
22
- */ export function SidebarItemRow({ name, leading, meta, action, unread = false, selected = false, leaving = false, onSelect, className }) {
23
+ */
24
+ export function SidebarItemRow({ name, leading, meta, action, unread = false, selected = false, leaving = false, onSelect, className, }) {
23
25
  const [mounted, setMounted] = useState(false);
24
- useEffect(()=>{
25
- const raf = requestAnimationFrame(()=>setMounted(true));
26
- return ()=>cancelAnimationFrame(raf);
26
+ useEffect(() => {
27
+ const raf = requestAnimationFrame(() => setMounted(true));
28
+ return () => cancelAnimationFrame(raf);
27
29
  }, []);
28
30
  const state = !mounted ? "entering" : leaving ? "leaving" : "visible";
29
- return /*#__PURE__*/ _jsx("li", {
30
- "data-slot": "sidebar-item-row",
31
- "data-state": state,
32
- className: cn("group/row transition-[transform,opacity] duration-150 ease-out", state === "entering" && "-translate-y-1 opacity-0", state === "leaving" && "pointer-events-none translate-y-1 opacity-0", state === "visible" && "translate-y-0 opacity-100", className),
33
- children: /*#__PURE__*/ _jsxs("div", {
34
- className: cn("flex w-full items-center gap-2 rounded-[var(--sidebar-row-radius)] pr-1.5 text-sm transition-colors", selected ? "bg-[var(--sidebar-row-selected-bg)] text-primary-emphasis" : "text-foreground hover:bg-muted"),
35
- children: [
36
- /*#__PURE__*/ _jsxs("button", {
37
- type: "button",
38
- "aria-current": selected ? "true" : undefined,
39
- onClick: onSelect,
40
- className: "flex min-w-0 flex-1 items-center gap-2 rounded-[var(--sidebar-row-radius)] py-1.5 pl-2 text-left active:brightness-95",
41
- children: [
42
- leading === undefined ? null : /*#__PURE__*/ _jsx("span", {
43
- className: "grid size-5 shrink-0 place-items-center [&_svg]:size-4",
44
- "aria-hidden": true,
45
- children: leading
46
- }),
47
- /*#__PURE__*/ _jsx("span", {
48
- className: cn("min-w-0 flex-1 truncate", unread && "font-semibold"),
49
- children: name
50
- }),
51
- meta === undefined ? null : /*#__PURE__*/ _jsx("span", {
52
- className: "flex shrink-0 items-center gap-1.5",
53
- children: meta
54
- })
55
- ]
56
- }),
57
- action === undefined ? null : /*#__PURE__*/ _jsx("span", {
58
- className: "shrink-0 opacity-0 transition-opacity group-hover/row:opacity-100 group-focus-within/row:opacity-100",
59
- children: action
60
- })
61
- ]
62
- })
63
- });
31
+ return (_jsx("li", { "data-slot": "sidebar-item-row", "data-state": state, className: cn("group/row transition-[transform,opacity] duration-150 ease-out", state === "entering" && "-translate-y-1 opacity-0", state === "leaving" && "pointer-events-none translate-y-1 opacity-0", state === "visible" && "translate-y-0 opacity-100", className), children: _jsxs("div", { className: cn("flex w-full items-center gap-2 rounded-[var(--sidebar-row-radius)] pr-1.5 text-sm transition-colors", selected
32
+ ? "bg-[var(--sidebar-row-selected-bg)] text-primary-emphasis"
33
+ : "text-foreground hover:bg-muted"), children: [_jsxs("button", { type: "button", "aria-current": selected ? "true" : undefined, onClick: onSelect, className: "flex min-w-0 flex-1 items-center gap-2 rounded-[var(--sidebar-row-radius)] py-1.5 pl-2 text-left active:brightness-95", children: [leading === undefined ? null : (_jsx("span", { className: "grid size-5 shrink-0 place-items-center [&_svg]:size-4", "aria-hidden": true, children: leading })), _jsx("span", { className: cn("min-w-0 flex-1 truncate", unread && "font-semibold"), children: name }), meta === undefined ? null : (_jsx("span", { className: "flex shrink-0 items-center gap-1.5", children: meta }))] }), action === undefined ? null : (_jsx("span", { className: "shrink-0 opacity-0 transition-opacity group-hover/row:opacity-100 group-focus-within/row:opacity-100", children: action }))] }) }));
64
34
  }
@@ -5,52 +5,27 @@ import { cn } from "../lib/utils.js";
5
5
  * assembles `SidebarPanelHeader`, `SidebarPanelPins`, `SidebarPanelBody` and
6
6
  * `SidebarPanelFooter` in whatever combination the active page calls for, and
7
7
  * the root only supplies the width, the border and the flex column.
8
- */ export function SidebarPanel({ className, ...props }) {
9
- return /*#__PURE__*/ _jsx("div", {
10
- "data-slot": "sidebar-panel",
11
- className: cn("flex h-full w-72 shrink-0 flex-col border-r border-border bg-card text-card-foreground", className),
12
- ...props
13
- });
8
+ */
9
+ export function SidebarPanel({ className, ...props }) {
10
+ return (_jsx("div", { "data-slot": "sidebar-panel", className: cn("flex h-full w-72 shrink-0 flex-col border-r border-border bg-card text-card-foreground", className), ...props }));
14
11
  }
15
- /** Not `ui/sidebar`'s `SidebarHeader` — that's the single-column family's header slot. */ export function SidebarPanelHeader({ title, action, className, ...props }) {
16
- return /*#__PURE__*/ _jsxs("div", {
17
- "data-slot": "sidebar-panel-header",
18
- className: cn("flex min-h-14 shrink-0 items-center justify-between gap-2 border-b border-border px-3", className),
19
- ...props,
20
- children: [
21
- /*#__PURE__*/ _jsx("h2", {
22
- className: "min-w-0 truncate text-sm font-semibold",
23
- children: title
24
- }),
25
- action === undefined ? null : /*#__PURE__*/ _jsx("div", {
26
- className: "shrink-0",
27
- children: action
28
- })
29
- ]
30
- });
12
+ /** Not `ui/sidebar`'s `SidebarHeader` — that's the single-column family's header slot. */
13
+ export function SidebarPanelHeader({ title, action, className, ...props }) {
14
+ return (_jsxs("div", { "data-slot": "sidebar-panel-header", className: cn("flex min-h-14 shrink-0 items-center justify-between gap-2 border-b border-border px-3", className), ...props, children: [_jsx("h2", { className: "min-w-0 truncate text-sm font-semibold", children: title }), action === undefined ? null : _jsx("div", { className: "shrink-0", children: action })] }));
31
15
  }
32
16
  /**
33
17
  * Global pins live above the per-page sections and never scroll away with
34
18
  * them — that is the entire reason this is a separate slot rather than just
35
19
  * the first `SidebarPanelSection` inside the body.
36
- */ export function SidebarPanelPins({ className, ...props }) {
37
- return /*#__PURE__*/ _jsx("div", {
38
- "data-slot": "sidebar-panel-pins",
39
- className: cn("shrink-0 border-b border-border px-2 py-2", className),
40
- ...props
41
- });
20
+ */
21
+ export function SidebarPanelPins({ className, ...props }) {
22
+ return (_jsx("div", { "data-slot": "sidebar-panel-pins", className: cn("shrink-0 border-b border-border px-2 py-2", className), ...props }));
42
23
  }
43
- /** The scrollable region: sections go here. Everything above and below it is pinned. */ export function SidebarPanelBody({ className, ...props }) {
44
- return /*#__PURE__*/ _jsx("div", {
45
- "data-slot": "sidebar-panel-body",
46
- className: cn("min-h-0 flex-1 overflow-y-auto py-1", className),
47
- ...props
48
- });
24
+ /** The scrollable region: sections go here. Everything above and below it is pinned. */
25
+ export function SidebarPanelBody({ className, ...props }) {
26
+ return (_jsx("div", { "data-slot": "sidebar-panel-body", className: cn("min-h-0 flex-1 overflow-y-auto py-1", className), ...props }));
49
27
  }
50
- /** Not `ui/sidebar`'s `SidebarFooter` — that's the single-column family's footer slot. */ export function SidebarPanelFooter({ className, ...props }) {
51
- return /*#__PURE__*/ _jsx("div", {
52
- "data-slot": "sidebar-panel-footer",
53
- className: cn("shrink-0 border-t border-border p-2", className),
54
- ...props
55
- });
28
+ /** Not `ui/sidebar`'s `SidebarFooter` — that's the single-column family's footer slot. */
29
+ export function SidebarPanelFooter({ className, ...props }) {
30
+ return (_jsx("div", { "data-slot": "sidebar-panel-footer", className: cn("shrink-0 border-t border-border p-2", className), ...props }));
56
31
  }
@@ -17,95 +17,34 @@ import { cn } from "../lib/utils.js";
17
17
  *
18
18
  * Collapsing hides labels with `sr-only`, never `hidden`: the icon-only rail
19
19
  * must keep its accessible names or it is unusable with a screen reader.
20
- */ export function Sidebar({ collapsed = false, className, ...props }) {
21
- return /*#__PURE__*/ _jsx("aside", {
22
- "data-slot": "sidebar",
23
- "data-collapsed": collapsed,
24
- className: cn("group/sidebar flex h-full shrink-0 flex-col border-r border-border bg-card text-card-foreground", "w-60 transition-[width] duration-200 data-[collapsed=true]:w-14", className),
25
- ...props
26
- });
20
+ */
21
+ export function Sidebar({ collapsed = false, className, ...props }) {
22
+ return (_jsx("aside", { "data-slot": "sidebar", "data-collapsed": collapsed, className: cn("group/sidebar flex h-full shrink-0 flex-col border-r border-border bg-card text-card-foreground", "w-60 transition-[width] duration-200 data-[collapsed=true]:w-14", className), ...props }));
27
23
  }
28
24
  export function SidebarHeader({ className, ...props }) {
29
- return /*#__PURE__*/ _jsx("div", {
30
- "data-slot": "sidebar-header",
31
- className: cn("flex min-h-14 items-center gap-2 border-b border-border px-3", className),
32
- ...props
33
- });
25
+ return (_jsx("div", { "data-slot": "sidebar-header", className: cn("flex min-h-14 items-center gap-2 border-b border-border px-3", className), ...props }));
34
26
  }
35
27
  export function SidebarContent({ className, ...props }) {
36
- return /*#__PURE__*/ _jsx("div", {
37
- "data-slot": "sidebar-content",
38
- className: cn("min-h-0 flex-1 overflow-y-auto py-2", className),
39
- ...props
40
- });
28
+ return (_jsx("div", { "data-slot": "sidebar-content", className: cn("min-h-0 flex-1 overflow-y-auto py-2", className), ...props }));
41
29
  }
42
30
  export function SidebarFooter({ className, ...props }) {
43
- return /*#__PURE__*/ _jsx("div", {
44
- "data-slot": "sidebar-footer",
45
- className: cn("border-t border-border p-2", className),
46
- ...props
47
- });
31
+ return (_jsx("div", { "data-slot": "sidebar-footer", className: cn("border-t border-border p-2", className), ...props }));
48
32
  }
49
- /** One nav section. `label` is required — an unlabelled group is invisible to AT. */ export function SidebarSection({ label, className, children, ...props }) {
50
- return /*#__PURE__*/ _jsxs("nav", {
51
- "data-slot": "sidebar-section",
52
- "aria-label": label,
53
- className: cn("px-2 py-1", className),
54
- ...props,
55
- children: [
56
- /*#__PURE__*/ _jsx("p", {
57
- className: "px-2 py-1 text-[11px] font-semibold tracking-[0.12em] text-muted-foreground uppercase group-data-[collapsed=true]/sidebar:sr-only",
58
- children: label
59
- }),
60
- /*#__PURE__*/ _jsx("ul", {
61
- className: "flex flex-col gap-0.5",
62
- children: children
63
- })
64
- ]
65
- });
33
+ /** One nav section. `label` is required — an unlabelled group is invisible to AT. */
34
+ export function SidebarSection({ label, className, children, ...props }) {
35
+ return (_jsxs("nav", { "data-slot": "sidebar-section", "aria-label": label, className: cn("px-2 py-1", className), ...props, children: [_jsx("p", { className: "px-2 py-1 text-[11px] font-semibold tracking-[0.12em] text-muted-foreground uppercase group-data-[collapsed=true]/sidebar:sr-only", children: label }), _jsx("ul", { className: "flex flex-col gap-0.5", children: children })] }));
66
36
  }
67
37
  export function SidebarItem({ active = false, icon, count, asChild = false, className, children, ...props }) {
68
38
  const Comp = asChild ? Slot : "a";
69
- return /*#__PURE__*/ _jsx("li", {
70
- children: /*#__PURE__*/ _jsxs(Comp, {
71
- "data-slot": "sidebar-item",
72
- "aria-current": active ? "page" : undefined,
73
- className: cn("flex items-center gap-2.5 rounded-md px-2 py-2 text-sm transition-colors", "group-data-[collapsed=true]/sidebar:justify-center", active ? "bg-primary/10 font-medium text-primary-emphasis" : "text-muted-foreground hover:bg-muted hover:text-foreground", className),
74
- ...props,
75
- children: [
76
- icon === undefined ? null : /*#__PURE__*/ _jsx("span", {
77
- className: "grid size-4 shrink-0 place-items-center [&_svg]:size-4",
78
- "aria-hidden": true,
79
- children: icon
80
- }),
81
- /*#__PURE__*/ _jsx("span", {
82
- className: "min-w-0 flex-1 truncate group-data-[collapsed=true]/sidebar:sr-only",
83
- children: children
84
- }),
85
- count === undefined ? null : /*#__PURE__*/ _jsx("span", {
86
- className: "shrink-0 rounded-full bg-muted px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground group-data-[collapsed=true]/sidebar:sr-only",
87
- children: count
88
- })
89
- ]
90
- })
91
- });
39
+ return (_jsx("li", { children: _jsxs(Comp, { "data-slot": "sidebar-item", "aria-current": active ? "page" : undefined, className: cn("flex items-center gap-2.5 rounded-md px-2 py-2 text-sm transition-colors", "group-data-[collapsed=true]/sidebar:justify-center", active
40
+ ? "bg-primary/10 font-medium text-primary-emphasis"
41
+ : "text-muted-foreground hover:bg-muted hover:text-foreground", className), ...props, children: [icon === undefined ? null : (_jsx("span", { className: "grid size-4 shrink-0 place-items-center [&_svg]:size-4", "aria-hidden": true, children: icon })), _jsx("span", { className: "min-w-0 flex-1 truncate group-data-[collapsed=true]/sidebar:sr-only", children: children }), count === undefined ? null : (_jsx("span", { className: "shrink-0 rounded-full bg-muted px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground group-data-[collapsed=true]/sidebar:sr-only", children: count }))] }) }));
92
42
  }
93
43
  /**
94
44
  * Collapse control. `aria-expanded` describes the rail it controls, so pass the
95
45
  * rail's id in `aria-controls` when the button lives outside the `<aside>`.
96
- */ export function SidebarCollapseToggle({ collapsed, onToggle, className, ...props }) {
46
+ */
47
+ export function SidebarCollapseToggle({ collapsed, onToggle, className, ...props }) {
97
48
  const Icon = collapsed ? PanelLeftOpen : PanelLeftClose;
98
- return /*#__PURE__*/ _jsx("button", {
99
- type: "button",
100
- "data-slot": "sidebar-collapse-toggle",
101
- "aria-expanded": !collapsed,
102
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
103
- onClick: onToggle,
104
- className: cn("grid size-8 shrink-0 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", className),
105
- ...props,
106
- children: /*#__PURE__*/ _jsx(Icon, {
107
- className: "size-4",
108
- "aria-hidden": true
109
- })
110
- });
49
+ return (_jsx("button", { type: "button", "data-slot": "sidebar-collapse-toggle", "aria-expanded": !collapsed, "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar", onClick: onToggle, className: cn("grid size-8 shrink-0 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", className), ...props, children: _jsx(Icon, { className: "size-4", "aria-hidden": true }) }));
111
50
  }
@@ -10,11 +10,7 @@ import { cn } from "../lib/utils.js";
10
10
  * `aria-hidden`, always. A screen reader should hear "Loading…" once from a
11
11
  * sibling `role="status"`, not the geometry of six grey boxes. If you render
12
12
  * these, render a status line with them.
13
- */ export function Skeleton({ className, ...props }) {
14
- return /*#__PURE__*/ _jsx("div", {
15
- "data-slot": "skeleton",
16
- "aria-hidden": true,
17
- className: cn("animate-pulse rounded-md bg-muted", className),
18
- ...props
19
- });
13
+ */
14
+ export function Skeleton({ className, ...props }) {
15
+ return (_jsx("div", { "data-slot": "skeleton", "aria-hidden": true, className: cn("animate-pulse rounded-md bg-muted", className), ...props }));
20
16
  }
@@ -1,9 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { ChevronDown, ChevronUp, ChevronsUpDown } from "lucide-react";
3
4
  import { useMemo, useState } from "react";
4
5
  import { cn } from "../lib/utils.js";
5
6
  import { EmptyState } from "./empty-state.js";
6
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
7
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "./table.js";
7
8
  /**
8
9
  * A table you can sort by clicking a header.
9
10
  *
@@ -18,86 +19,42 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from ".
18
19
  * invisible to a screen reader, and sort order is exactly the sort of state
19
20
  * that goes unnoticed without it. Inactive sortable headers show a neutral
20
21
  * affordance so it is discoverable which columns can be sorted at all.
21
- */ export function SortableTable({ caption, rows, columns, rowKey, initialSort, empty, className }) {
22
+ */
23
+ export function SortableTable({ caption, rows, columns, rowKey, initialSort, empty, className, }) {
22
24
  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;
25
+ const sorted = useMemo(() => {
26
+ if (sort === null)
27
+ return rows;
28
+ const column = columns.find((candidate) => candidate.key === sort.key);
29
+ if (column?.sortValue === undefined)
30
+ return rows;
27
31
  const { sortValue } = column;
28
32
  // Sorts a copy: `rows` belongs to the caller and may be frozen.
29
- return [
30
- ...rows
31
- ].sort((a, b)=>{
33
+ return [...rows].sort((a, b) => {
32
34
  const left = sortValue(a);
33
35
  const right = sortValue(b);
34
- const comparison = typeof left === "number" && typeof right === "number" ? left - right : String(left).localeCompare(String(right));
36
+ const comparison = typeof left === "number" && typeof right === "number"
37
+ ? left - right
38
+ : // localeCompare so "Ä" sorts next to "A" rather than after "Z".
39
+ String(left).localeCompare(String(right));
35
40
  return sort.direction === "asc" ? comparison : -comparison;
36
41
  });
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
- });
42
+ }, [rows, columns, sort]);
43
+ const toggle = (key) => {
44
+ setSort((current) => current?.key === key
45
+ ? { key, direction: current.direction === "asc" ? "desc" : "asc" }
46
+ : { key, direction: "asc" });
50
47
  };
51
48
  if (rows.length === 0) {
52
- return /*#__PURE__*/ _jsx(_Fragment, {
53
- children: empty ?? /*#__PURE__*/ _jsx(EmptyState, {
54
- title: "Nothing to show"
55
- })
56
- });
49
+ return _jsx(_Fragment, { children: empty ?? _jsx(EmptyState, { title: "Nothing to show" }) });
57
50
  }
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)=>{
51
+ return (_jsxs(Table, { "aria-label": caption, className: className, children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: columns.map((column) => {
65
52
  const active = sort?.key === column.key;
66
53
  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
- });
54
+ return (_jsx(TableHead, { scope: "col", "aria-sort": active
55
+ ? sort.direction === "asc"
56
+ ? "ascending"
57
+ : "descending"
58
+ : undefined, className: cn(column.align === "right" && "text-right", column.className), children: sortable ? (_jsxs("button", { type: "button", onClick: () => toggle(column.key), className: cn("inline-flex items-center gap-1 rounded-sm transition-colors hover:text-foreground", !active && "text-muted-foreground"), children: [column.header, active ? (sort.direction === "asc" ? (_jsx(ChevronUp, { className: "size-3.5", "aria-hidden": true })) : (_jsx(ChevronDown, { className: "size-3.5", "aria-hidden": true }))) : (_jsx(ChevronsUpDown, { className: "size-3.5 opacity-50", "aria-hidden": true }))] })) : (column.header) }, column.key));
59
+ }) }) }), _jsx(TableBody, { children: sorted.map((row) => (_jsx(TableRow, { children: columns.map((column) => (_jsx(TableCell, { className: cn(column.align === "right" && "text-right", column.className), children: column.cell(row) }, column.key))) }, rowKey(row)))) })] }));
103
60
  }
@@ -19,50 +19,26 @@ const HEIGHT = 24;
19
19
  * `role="img"` with the caller's summary as its name. The summary is required
20
20
  * for the same reason `alt` is: a picture with no text alternative is simply
21
21
  * missing for some readers, and this one has no fallback content to fall back to.
22
- */ export function Sparkline({ values, summary, className }) {
22
+ */
23
+ export function Sparkline({ values, summary, className }) {
23
24
  if (values.length < 2) {
24
- return /*#__PURE__*/ _jsx("span", {
25
- className: cn("inline-block", className),
26
- role: "img",
27
- "aria-label": summary
28
- });
25
+ return (_jsx("span", { className: cn("inline-block", className), role: "img", "aria-label": summary }));
29
26
  }
30
27
  const min = Math.min(...values);
31
28
  const max = Math.max(...values);
32
29
  const span = max - min;
33
30
  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
- }));
31
+ const points = values.map((value, index) => ({
32
+ x: index * stepX,
33
+ // A flat series has no span to scale against; centre it rather than
34
+ // dividing by zero.
35
+ y: span === 0
36
+ ? HEIGHT / 2
37
+ : HEIGHT - scaleLinear(value - min, span, HEIGHT - 4) - 2,
38
+ }));
40
39
  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
40
+ return (_jsxs("svg", { viewBox: `0 0 ${WIDTH} ${HEIGHT}`, className: cn("h-6 w-24", className), role: "img", "aria-label": summary, focusable: "false", children: [_jsx("path", { d: linePath(points), fill: "none", stroke: seriesColor(0), strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }), last === undefined ? null : (
41
+ // The end dot marks "now" — which end is the current period is
57
42
  // 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
- });
43
+ _jsx("circle", { cx: last.x, cy: last.y, r: 2.5, fill: seriesColor(0), stroke: "var(--card)", strokeWidth: 2 }))] }));
68
44
  }
@@ -7,7 +7,7 @@ const COLUMN_CLASS = {
7
7
  2: "grid-cols-2",
8
8
  3: "grid-cols-2 sm:grid-cols-3",
9
9
  4: "grid-cols-2 sm:grid-cols-4",
10
- 5: "grid-cols-2 sm:grid-cols-5"
10
+ 5: "grid-cols-2 sm:grid-cols-5",
11
11
  };
12
12
  /**
13
13
  * The KPI band: a fixed responsive grid of `StatGridItem`s.
@@ -15,15 +15,15 @@ const COLUMN_CLASS = {
15
15
  * Fixed columns rather than auto-fit, because a KPI row is designed as a set —
16
16
  * four headline numbers are four columns, and a row that reflows to five-up on
17
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
- });
18
+ */
19
+ export function StatGrid({ children, columns = 4, className }) {
20
+ return (_jsx("div", { className: cn("grid gap-3", COLUMN_CLASS[columns], className), children: children }));
23
21
  }
24
22
  function statValueClass(accent, danger) {
25
- if (danger === true) return "text-destructive";
26
- if (accent === true) return "text-primary-emphasis";
23
+ if (danger === true)
24
+ return "text-destructive";
25
+ if (accent === true)
26
+ return "text-primary-emphasis";
27
27
  return "text-foreground";
28
28
  }
29
29
  /**
@@ -36,52 +36,17 @@ function statValueClass(accent, danger) {
36
36
  * wearing `role="button"`) so keyboard activation is native. Shared layout
37
37
  * classes stay identical; only the interactive shell adds hover/press
38
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);
39
+ */
40
+ export function StatGridItem({ label, value, sub, accent, danger, emphasis, delta, sparkline, sparklineValues, sparklineLabel, onClick, className, }) {
41
+ const sparklineNode = sparkline ??
42
+ (sparklineValues !== undefined && sparklineValues.length > 0 ? (_jsx(Sparkline, { values: sparklineValues, summary: sparklineLabel ?? `${label} trend` })) : null);
44
43
  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
- });
44
+ const body = (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-[10px] font-semibold tracking-[0.12em] uppercase text-muted-foreground", children: label }), _jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: cn("font-black leading-none tabular-nums", emphasis === true ? "text-[34px]" : "text-[26px]", statValueClass(accent, danger)), children: value }), delta] }), sparklineNode === null ? null : (_jsx("div", { className: "mt-0.5", children: sparklineNode })), sub === undefined ? null : (_jsx("span", { className: "text-[10px] tracking-[0.08em] uppercase text-muted-foreground", children: sub }))] }));
71
45
  if (onClick === undefined) {
72
- return /*#__PURE__*/ _jsx("div", {
73
- "data-slot": "stat-grid-item",
74
- className: shell,
75
- children: body
76
- });
46
+ return (_jsx("div", { "data-slot": "stat-grid-item", className: shell, children: body }));
77
47
  }
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.
48
+ return (_jsx("button", { type: "button", "data-slot": "stat-grid-item", onClick: onClick, className: cn(shell,
49
+ // text-left/w-full: button UA styles centre content and shrink-wrap.
83
50
  // 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
- });
51
+ "w-full cursor-pointer text-left transition-colors hover:border-primary-emphasis active:brightness-95"), children: body }));
87
52
  }
@@ -10,4 +10,4 @@ export type StatusDotProps = {
10
10
  readonly className?: string;
11
11
  };
12
12
  export type StatusDotSize = "xs" | "sm";
13
- export declare function StatusDot({ label, live, tone, size, className }: StatusDotProps): import("react").JSX.Element;
13
+ export declare function StatusDot({ label, live, tone, size, className, }: StatusDotProps): import("react").JSX.Element;
@@ -11,14 +11,15 @@ import { cn } from "../lib/utils.js";
11
11
  *
12
12
  * Classes are spelled out rather than composed at runtime — Tailwind's scanner
13
13
  * only emits utilities it can see literally in the source.
14
- */ const TONE_CLASS = {
14
+ */
15
+ const TONE_CLASS = {
15
16
  neutral: "bg-muted-foreground",
16
17
  emphasis: "bg-primary-emphasis",
17
18
  // success/warning use emphasis orange until dedicated high-contrast
18
19
  // fills clear 3:1 at 8px; Badge remains the full-status carrier.
19
20
  success: "bg-primary-emphasis",
20
21
  warning: "bg-primary-emphasis",
21
- danger: "bg-destructive"
22
+ danger: "bg-destructive",
22
23
  };
23
24
  /**
24
25
  * A state marker carrying its own accessible name — a coloured dot with no
@@ -28,24 +29,11 @@ import { cn } from "../lib/utils.js";
28
29
  * The `live` ring is a separate `aria-hidden` element: motion says "still
29
30
  * changing" to people who can see it, and the label already says so to those
30
31
  * who cannot. It stills under the theme's reduced-motion rule.
31
- */ const SIZE_CLASS = {
32
+ */
33
+ const SIZE_CLASS = {
32
34
  xs: "size-1.5",
33
- sm: "size-2"
35
+ sm: "size-2",
34
36
  };
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
- });
37
+ export function StatusDot({ label, live = false, tone = "neutral", size = "sm", className, }) {
38
+ return (_jsxs("span", { role: "img", "aria-label": label, className: cn("relative inline-flex shrink-0", SIZE_CLASS[size], className), children: [live ? (_jsx("span", { "aria-hidden": true, className: cn("absolute inset-0 rounded-full [animation:corbits-status-pulse_1.8s_ease-out_infinite]", TONE_CLASS[tone]) })) : null, _jsx("span", { "aria-hidden": true, className: cn("relative size-full rounded-full", TONE_CLASS[tone]) })] }));
51
39
  }
@@ -1,4 +1,5 @@
1
1
  import type { StepListItem } from "../lib/workflow-registry.js";
2
+ export type { StepDisplayStatus, StepListItem, } from "../lib/workflow-registry.js";
2
3
  export type StepListRowProps = {
3
4
  readonly step: StepListItem;
4
5
  readonly className?: string;