@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,35 @@
1
+ import type { ReactNode } from "react";
2
+ export type SettingsPanelProps = {
3
+ readonly title: string;
4
+ readonly description?: ReactNode;
5
+ /**
6
+ * Present a save control. Omit for panels whose controls take effect on
7
+ * change — a Save button that saves nothing new is worse than none.
8
+ */
9
+ readonly onSave?: () => void;
10
+ /** Unsaved changes. Drives the save control and the status line. */
11
+ readonly dirty?: boolean;
12
+ readonly saving?: boolean;
13
+ readonly error?: string | null;
14
+ /** Set after a successful save so the panel can confirm it happened. */
15
+ readonly savedAt?: string | null;
16
+ readonly onReset?: () => void;
17
+ readonly children: ReactNode;
18
+ readonly className?: string;
19
+ };
20
+ /**
21
+ * The frame every settings panel shares: a heading, the controls, and — only
22
+ * where it means something — a save control.
23
+ *
24
+ * There is one shell rather than one component per setting group. "Defaults",
25
+ * "Instructions", "Style" and "Preferences" differ in the fields inside them
26
+ * and in nothing else; a component per group would be nine files forwarding
27
+ * props to the same markup, and they would drift.
28
+ *
29
+ * Nothing here is agent-specific. The panel is titled by its host.
30
+ *
31
+ * The save control is disabled when there is nothing to save, and the panel
32
+ * says why in text as well: a greyed-out button with no explanation is the most
33
+ * common dead end in a settings screen.
34
+ */
35
+ export declare function SettingsPanel({ title, description, onSave, dirty, saving, error, savedAt, onReset, children, className, }: SettingsPanelProps): import("react").JSX.Element;
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ import { Button } from "./button.js";
4
+ /**
5
+ * The frame every settings panel shares: a heading, the controls, and — only
6
+ * where it means something — a save control.
7
+ *
8
+ * There is one shell rather than one component per setting group. "Defaults",
9
+ * "Instructions", "Style" and "Preferences" differ in the fields inside them
10
+ * and in nothing else; a component per group would be nine files forwarding
11
+ * props to the same markup, and they would drift.
12
+ *
13
+ * Nothing here is agent-specific. The panel is titled by its host.
14
+ *
15
+ * The save control is disabled when there is nothing to save, and the panel
16
+ * says why in text as well: a greyed-out button with no explanation is the most
17
+ * common dead end in a settings screen.
18
+ */ export function SettingsPanel({ title, description, onSave, dirty = false, saving = false, error = null, savedAt = null, onReset, children, className }) {
19
+ return /*#__PURE__*/ _jsxs("section", {
20
+ "data-slot": "settings-panel",
21
+ "aria-label": title,
22
+ className: cn("flex flex-col gap-4 rounded-lg border border-border bg-card p-5 text-card-foreground", className),
23
+ children: [
24
+ /*#__PURE__*/ _jsxs("div", {
25
+ className: "flex flex-col gap-1",
26
+ children: [
27
+ /*#__PURE__*/ _jsx("h3", {
28
+ className: "text-sm font-semibold",
29
+ children: title
30
+ }),
31
+ description === undefined ? null : /*#__PURE__*/ _jsx("p", {
32
+ className: "text-sm leading-snug text-muted-foreground",
33
+ children: description
34
+ })
35
+ ]
36
+ }),
37
+ children,
38
+ error === null ? null : /*#__PURE__*/ _jsx("p", {
39
+ role: "alert",
40
+ className: "text-sm text-destructive",
41
+ children: error
42
+ }),
43
+ onSave === undefined ? null : /*#__PURE__*/ _jsxs("div", {
44
+ className: "flex flex-wrap items-center gap-2 border-t border-border pt-4",
45
+ children: [
46
+ /*#__PURE__*/ _jsx(Button, {
47
+ size: "sm",
48
+ onClick: onSave,
49
+ disabled: !dirty || saving,
50
+ children: saving ? "Saving…" : "Save changes"
51
+ }),
52
+ onReset === undefined || !dirty ? null : /*#__PURE__*/ _jsx(Button, {
53
+ variant: "ghost",
54
+ size: "sm",
55
+ onClick: onReset,
56
+ disabled: saving,
57
+ children: "Discard"
58
+ }),
59
+ /*#__PURE__*/ _jsx("p", {
60
+ "aria-live": "polite",
61
+ className: "ml-auto text-xs text-muted-foreground",
62
+ children: dirty ? "Unsaved changes" : savedAt === null ? "" : `Saved ${savedAt}`
63
+ })
64
+ ]
65
+ })
66
+ ]
67
+ });
68
+ }
@@ -0,0 +1,19 @@
1
+ export type ShimmerTextProps = {
2
+ readonly children: string;
3
+ /** Omit when an ancestor already carries the live region. */
4
+ readonly role?: "status";
5
+ readonly className?: string;
6
+ };
7
+ /**
8
+ * A shimmer sweep over inline text: a brighter band slides through the
9
+ * words to signal "still going" without the words themselves changing. The
10
+ * sweep is a `background-clip: text` gradient animating its position, built
11
+ * from `--muted-foreground` and `--foreground` only — no new hex, so the
12
+ * contrast gate never sees it as a text/background pair.
13
+ *
14
+ * The theme's global reduced-motion rule collapses animation duration, but
15
+ * that would still leave `text-transparent` + `background-clip: text` with
16
+ * nothing to paint. Under `prefers-reduced-motion` and `forced-colors` this
17
+ * falls back to solid `text-muted-foreground` with no clip.
18
+ */
19
+ export declare function ShimmerText({ children, role, className }: ShimmerTextProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ /**
4
+ * A shimmer sweep over inline text: a brighter band slides through the
5
+ * words to signal "still going" without the words themselves changing. The
6
+ * sweep is a `background-clip: text` gradient animating its position, built
7
+ * from `--muted-foreground` and `--foreground` only — no new hex, so the
8
+ * contrast gate never sees it as a text/background pair.
9
+ *
10
+ * The theme's global reduced-motion rule collapses animation duration, but
11
+ * that would still leave `text-transparent` + `background-clip: text` with
12
+ * nothing to paint. Under `prefers-reduced-motion` and `forced-colors` this
13
+ * falls back to solid `text-muted-foreground` with no clip.
14
+ */ export function ShimmerText({ children, role, className }) {
15
+ return /*#__PURE__*/ _jsx("span", {
16
+ role: role,
17
+ className: cn("bg-clip-text text-transparent", "[background-image:linear-gradient(90deg,var(--muted-foreground)_40%,var(--foreground)_50%,var(--muted-foreground)_60%)]", "[background-size:200%_100%]", "[animation:corbits-shimmer-sweep_1.8s_linear_infinite]", "motion-reduce:animate-none motion-reduce:bg-none motion-reduce:bg-clip-border motion-reduce:text-muted-foreground", "forced-colors:animate-none forced-colors:bg-none forced-colors:bg-clip-border forced-colors:text-muted-foreground", className),
18
+ children: children
19
+ });
20
+ }
@@ -0,0 +1,40 @@
1
+ import type * as React from "react";
2
+ export type SidebarItemRowProps = {
3
+ readonly name: React.ReactNode;
4
+ /** An icon, an avatar, a status dot — whatever leads the row. */
5
+ readonly leading?: React.ReactNode;
6
+ /** A badge, a status dot, a timestamp — right-aligned, always visible. */
7
+ readonly meta?: React.ReactNode;
8
+ /** A menu trigger or icon button, revealed on hover/focus only. */
9
+ readonly action?: React.ReactNode;
10
+ readonly unread?: boolean;
11
+ readonly selected?: boolean;
12
+ /**
13
+ * Set right before removing the row from its list, and keep it mounted for
14
+ * one more frame (~150ms) so the leave transition gets to play. List
15
+ * membership is the consumer's data — nothing here manages it for you.
16
+ */
17
+ readonly leaving?: boolean;
18
+ readonly onSelect?: () => void;
19
+ readonly className?: string;
20
+ };
21
+ /**
22
+ * The live-updating row: a channel, a thread, a running workflow. Motion is a
23
+ * CSS transition on `transform`/`opacity` driven by a `data-state` of
24
+ * `entering` | `visible` | `leaving`, not an enter/exit keyframe pair — a
25
+ * keyframe animation restarts from its `from` value every time it is
26
+ * retriggered, so a row that gets un-marked `leaving` mid-exit (the live-
27
+ * activity list does this) would snap instead of reversing. A transition has
28
+ * no such restart: toggling the state class mid-flight just continues from
29
+ * wherever the interrupted transition already got to.
30
+ *
31
+ * `unread` is weight only: the name goes semibold. `meta` carries whatever
32
+ * the caller wants rendered — a `Badge`, a `StatusDot`, a timestamp — with no
33
+ * shape enforced by this component; callers who need the unread state to
34
+ * survive colour-blindness and grayscale screenshots should pass a `meta`
35
+ * that is not colour-only (a `StatusDot`, a count), not rely on this
36
+ * component to supply one.
37
+ *
38
+ * Not `ui/sidebar`'s `SidebarItem` — that's the single-column family's row.
39
+ */
40
+ export declare function SidebarItemRow({ name, leading, meta, action, unread, selected, leaving, onSelect, className, }: SidebarItemRowProps): React.JSX.Element;
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { cn } from "../lib/utils.js";
4
+ /**
5
+ * The live-updating row: a channel, a thread, a running workflow. Motion is a
6
+ * CSS transition on `transform`/`opacity` driven by a `data-state` of
7
+ * `entering` | `visible` | `leaving`, not an enter/exit keyframe pair — a
8
+ * keyframe animation restarts from its `from` value every time it is
9
+ * retriggered, so a row that gets un-marked `leaving` mid-exit (the live-
10
+ * activity list does this) would snap instead of reversing. A transition has
11
+ * no such restart: toggling the state class mid-flight just continues from
12
+ * wherever the interrupted transition already got to.
13
+ *
14
+ * `unread` is weight only: the name goes semibold. `meta` carries whatever
15
+ * the caller wants rendered — a `Badge`, a `StatusDot`, a timestamp — with no
16
+ * shape enforced by this component; callers who need the unread state to
17
+ * survive colour-blindness and grayscale screenshots should pass a `meta`
18
+ * that is not colour-only (a `StatusDot`, a count), not rely on this
19
+ * component to supply one.
20
+ *
21
+ * 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
+ const [mounted, setMounted] = useState(false);
24
+ useEffect(()=>{
25
+ const raf = requestAnimationFrame(()=>setMounted(true));
26
+ return ()=>cancelAnimationFrame(raf);
27
+ }, []);
28
+ 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
+ });
64
+ }
@@ -0,0 +1,25 @@
1
+ import type * as React from "react";
2
+ /**
3
+ * The second column's shell. Composable parts, like `ui/sidebar` — a consumer
4
+ * assembles `SidebarPanelHeader`, `SidebarPanelPins`, `SidebarPanelBody` and
5
+ * `SidebarPanelFooter` in whatever combination the active page calls for, and
6
+ * the root only supplies the width, the border and the flex column.
7
+ */
8
+ export declare function SidebarPanel({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
9
+ export type SidebarPanelHeaderProps = React.ComponentProps<"div"> & {
10
+ readonly title: React.ReactNode;
11
+ /** A single trailing control — "New channel", a filter, a settings icon. */
12
+ readonly action?: React.ReactNode;
13
+ };
14
+ /** Not `ui/sidebar`'s `SidebarHeader` — that's the single-column family's header slot. */
15
+ export declare function SidebarPanelHeader({ title, action, className, ...props }: SidebarPanelHeaderProps): React.JSX.Element;
16
+ /**
17
+ * Global pins live above the per-page sections and never scroll away with
18
+ * them — that is the entire reason this is a separate slot rather than just
19
+ * the first `SidebarPanelSection` inside the body.
20
+ */
21
+ export declare function SidebarPanelPins({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
22
+ /** The scrollable region: sections go here. Everything above and below it is pinned. */
23
+ export declare function SidebarPanelBody({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
24
+ /** Not `ui/sidebar`'s `SidebarFooter` — that's the single-column family's footer slot. */
25
+ export declare function SidebarPanelFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ /**
4
+ * The second column's shell. Composable parts, like `ui/sidebar` — a consumer
5
+ * assembles `SidebarPanelHeader`, `SidebarPanelPins`, `SidebarPanelBody` and
6
+ * `SidebarPanelFooter` in whatever combination the active page calls for, and
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
+ });
14
+ }
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
+ });
31
+ }
32
+ /**
33
+ * Global pins live above the per-page sections and never scroll away with
34
+ * them — that is the entire reason this is a separate slot rather than just
35
+ * 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
+ });
42
+ }
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
+ });
49
+ }
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
+ });
56
+ }
@@ -0,0 +1,46 @@
1
+ import type * as React from "react";
2
+ /**
3
+ * The app rail. Composable parts rather than one `items={...}` component: a
4
+ * consumer owns this file after `shadcn add`, and nav shape (sections, counts,
5
+ * a tenant switcher, an environment badge) differs per app. Every part is a
6
+ * slot — `SidebarHeader` is where an environment badge goes, `SidebarFooter` is
7
+ * where a tenant selector goes — and nothing here fetches anything.
8
+ *
9
+ * Collapse is a prop, not internal state or a context: the host already owns
10
+ * that bit (it persists it, and the top bar's menu button toggles it). The root
11
+ * publishes it as `data-collapsed` and the children style off
12
+ * `group-data-[collapsed=true]/sidebar:` — so a new part added later collapses
13
+ * correctly with no wiring.
14
+ *
15
+ * Collapsing hides labels with `sr-only`, never `hidden`: the icon-only rail
16
+ * must keep its accessible names or it is unusable with a screen reader.
17
+ */
18
+ export declare function Sidebar({ collapsed, className, ...props }: React.ComponentProps<"aside"> & {
19
+ collapsed?: boolean;
20
+ }): React.JSX.Element;
21
+ export declare function SidebarHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
22
+ export declare function SidebarContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
23
+ export declare function SidebarFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
24
+ /** One nav section. `label` is required — an unlabelled group is invisible to AT. */
25
+ export declare function SidebarSection({ label, className, children, ...props }: React.ComponentProps<"nav"> & {
26
+ label: string;
27
+ }): React.JSX.Element;
28
+ export type SidebarItemProps = React.ComponentProps<"a"> & {
29
+ /** Renders the current route. Sets `aria-current="page"`, not just a colour. */
30
+ active?: boolean;
31
+ /** Leading icon. Kept visible when collapsed — it is the whole control then. */
32
+ icon?: React.ReactNode;
33
+ /** Unread / pending count. Part of the accessible name, so it survives collapse. */
34
+ count?: number;
35
+ /** Renders the child element instead of an `<a>` — for a router's Link. */
36
+ asChild?: boolean;
37
+ };
38
+ export declare function SidebarItem({ active, icon, count, asChild, className, children, ...props }: SidebarItemProps): React.JSX.Element;
39
+ /**
40
+ * Collapse control. `aria-expanded` describes the rail it controls, so pass the
41
+ * rail's id in `aria-controls` when the button lives outside the `<aside>`.
42
+ */
43
+ export declare function SidebarCollapseToggle({ collapsed, onToggle, className, ...props }: Omit<React.ComponentProps<"button">, "onToggle"> & {
44
+ collapsed: boolean;
45
+ onToggle: () => void;
46
+ }): React.JSX.Element;
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
4
+ import { cn } from "../lib/utils.js";
5
+ /**
6
+ * The app rail. Composable parts rather than one `items={...}` component: a
7
+ * consumer owns this file after `shadcn add`, and nav shape (sections, counts,
8
+ * a tenant switcher, an environment badge) differs per app. Every part is a
9
+ * slot — `SidebarHeader` is where an environment badge goes, `SidebarFooter` is
10
+ * where a tenant selector goes — and nothing here fetches anything.
11
+ *
12
+ * Collapse is a prop, not internal state or a context: the host already owns
13
+ * that bit (it persists it, and the top bar's menu button toggles it). The root
14
+ * publishes it as `data-collapsed` and the children style off
15
+ * `group-data-[collapsed=true]/sidebar:` — so a new part added later collapses
16
+ * correctly with no wiring.
17
+ *
18
+ * Collapsing hides labels with `sr-only`, never `hidden`: the icon-only rail
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
+ });
27
+ }
28
+ 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
+ });
34
+ }
35
+ 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
+ });
41
+ }
42
+ 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
+ });
48
+ }
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
+ });
66
+ }
67
+ export function SidebarItem({ active = false, icon, count, asChild = false, className, children, ...props }) {
68
+ 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
+ });
92
+ }
93
+ /**
94
+ * Collapse control. `aria-expanded` describes the rail it controls, so pass the
95
+ * rail's id in `aria-controls` when the button lives outside the `<aside>`.
96
+ */ export function SidebarCollapseToggle({ collapsed, onToggle, className, ...props }) {
97
+ 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
+ });
111
+ }
@@ -0,0 +1,13 @@
1
+ import type * as React from "react";
2
+ /**
3
+ * A placeholder for content that has not arrived.
4
+ *
5
+ * Sizing and shape come from the caller's className — a skeleton is a shape,
6
+ * and a `variant="text" | "avatar"` prop would only be a lookup table for
7
+ * classes the caller can write directly.
8
+ *
9
+ * `aria-hidden`, always. A screen reader should hear "Loading…" once from a
10
+ * sibling `role="status"`, not the geometry of six grey boxes. If you render
11
+ * these, render a status line with them.
12
+ */
13
+ export declare function Skeleton({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ /**
4
+ * A placeholder for content that has not arrived.
5
+ *
6
+ * Sizing and shape come from the caller's className — a skeleton is a shape,
7
+ * and a `variant="text" | "avatar"` prop would only be a lookup table for
8
+ * classes the caller can write directly.
9
+ *
10
+ * `aria-hidden`, always. A screen reader should hear "Loading…" once from a
11
+ * sibling `role="status"`, not the geometry of six grey boxes. If you render
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
+ });
20
+ }
@@ -0,0 +1,43 @@
1
+ import type { ReactNode } from "react";
2
+ export type SortDirection = "asc" | "desc";
3
+ export type SortableColumn<T> = {
4
+ /** Stable id — the React key and the sort key. */
5
+ readonly key: string;
6
+ readonly header: string;
7
+ readonly cell: (row: T) => ReactNode;
8
+ /**
9
+ * Makes the column sortable by returning its comparable value. Omit for
10
+ * presentation-only columns such as a row-actions cell.
11
+ */
12
+ readonly sortValue?: (row: T) => string | number;
13
+ readonly align?: "left" | "right";
14
+ readonly className?: string;
15
+ };
16
+ export type SortableTableProps<T> = {
17
+ readonly caption: string;
18
+ readonly rows: readonly T[];
19
+ readonly columns: readonly SortableColumn<T>[];
20
+ readonly rowKey: (row: T) => string;
21
+ readonly initialSort?: {
22
+ readonly key: string;
23
+ readonly direction: SortDirection;
24
+ };
25
+ readonly empty?: ReactNode;
26
+ readonly className?: string;
27
+ };
28
+ /**
29
+ * A table you can sort by clicking a header.
30
+ *
31
+ * Rows are an array, not a `DataPort` collection — this sorts client-side over
32
+ * rows it already has, which is only correct for a set small enough to hold in
33
+ * full. Once the data is paginated, sorting has to happen at the source or the
34
+ * user is sorting one page and being told it is the whole set; use `DataTable`
35
+ * with a server-sorted request for that. The two are different tools and this
36
+ * one does not pretend to be the other.
37
+ *
38
+ * `aria-sort` on the active header is the part that matters. A caret glyph is
39
+ * invisible to a screen reader, and sort order is exactly the sort of state
40
+ * that goes unnoticed without it. Inactive sortable headers show a neutral
41
+ * affordance so it is discoverable which columns can be sorted at all.
42
+ */
43
+ export declare function SortableTable<T>({ caption, rows, columns, rowKey, initialSort, empty, className, }: SortableTableProps<T>): import("react").JSX.Element;