@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,270 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { toSafeHref } from "../lib/url.js";
3
+ import { cn } from "../lib/utils.js";
4
+ /**
5
+ * Reads a brief (and its optional prose report) out of an artifact payload.
6
+ *
7
+ * Checks only `topic` and the two counts — the fields the header renders
8
+ * unconditionally and the ones whose absence would print "undefined sources".
9
+ * Everything below the header is already guarded by a presence check at its own
10
+ * render site, so validating it here would only reject briefs this component
11
+ * draws correctly.
12
+ */ export function parseResearchArtifact(json) {
13
+ let raw;
14
+ try {
15
+ raw = JSON.parse(json);
16
+ } catch {
17
+ return null;
18
+ }
19
+ if (typeof raw !== "object" || raw === null) return null;
20
+ const candidate = raw;
21
+ if (typeof candidate.topic !== "string" || typeof candidate.sourceCount !== "number" || typeof candidate.itemCount !== "number") {
22
+ return null;
23
+ }
24
+ const body = typeof candidate.body === "string" ? candidate.body : undefined;
25
+ return body === undefined ? {
26
+ brief: candidate
27
+ } : {
28
+ brief: candidate,
29
+ body
30
+ };
31
+ }
32
+ /**
33
+ * A citation list can name the same URL twice — one item that clustered under
34
+ * two sources. Both the numbering the reader sees and any export they take must
35
+ * agree on the count, so dedupe once, here, at the point of render.
36
+ */ function dedupeByUrl(sources) {
37
+ const seen = new Set();
38
+ return sources.filter((source)=>{
39
+ if (seen.has(source.url)) return false;
40
+ seen.add(source.url);
41
+ return true;
42
+ });
43
+ }
44
+ /**
45
+ * A structured research brief: what was found, grouped, quoted and cited.
46
+ *
47
+ * The layout is an argument in order — headline stat line, the lead insight,
48
+ * then clusters, then evidence, then citations. A reader who stops after the
49
+ * first screen should still have the finding; a reader who wants to audit it
50
+ * can walk down to the sources without leaving the page.
51
+ *
52
+ * `body` is the prose report when the producer wrote one. Given both, the prose
53
+ * is the deliverable and the structured data becomes supporting evidence behind
54
+ * a `<details>` — collapsed rather than removed, because "show me your working"
55
+ * is the second question every reader of a generated report asks. `<details>` is
56
+ * a native disclosure: keyboard-operable and announced as expandable with no JS
57
+ * and no ARIA of our own.
58
+ *
59
+ * Prose is rendered as pre-wrapped text, not parsed as Markdown. The registry
60
+ * ships no Markdown dependency, and adding ~40kB of parser that a consumer then
61
+ * owns is not something a component should decide on their behalf. Wrap the
62
+ * `body` slot in your own renderer if you have one.
63
+ */ export function ResearchBody({ brief, body, className }) {
64
+ const report = body?.trim() ?? "";
65
+ const citations = dedupeByUrl(brief.citations ?? []);
66
+ const data = /*#__PURE__*/ _jsxs("div", {
67
+ className: "flex flex-col gap-6",
68
+ children: [
69
+ brief.clusters === undefined || brief.clusters.length === 0 ? null : /*#__PURE__*/ _jsxs("section", {
70
+ className: "flex flex-col gap-3",
71
+ "aria-label": "Themes",
72
+ children: [
73
+ /*#__PURE__*/ _jsx("h3", {
74
+ className: "text-sm font-semibold",
75
+ children: "Themes"
76
+ }),
77
+ brief.clusters.map((cluster)=>/*#__PURE__*/ _jsxs("article", {
78
+ className: "flex flex-col gap-2 rounded-lg border border-border p-4",
79
+ children: [
80
+ /*#__PURE__*/ _jsxs("div", {
81
+ className: "flex items-start justify-between gap-3",
82
+ children: [
83
+ /*#__PURE__*/ _jsx("h4", {
84
+ className: "text-sm leading-snug font-semibold",
85
+ children: cluster.title
86
+ }),
87
+ cluster.sources === undefined || cluster.sources.length === 0 ? null : /*#__PURE__*/ _jsx("span", {
88
+ className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 text-xs text-muted-foreground",
89
+ children: cluster.sources.join(", ")
90
+ })
91
+ ]
92
+ }),
93
+ cluster.summary === undefined ? null : /*#__PURE__*/ _jsx("p", {
94
+ className: "text-xs leading-relaxed text-muted-foreground",
95
+ children: cluster.summary
96
+ }),
97
+ cluster.items === undefined || cluster.items.length === 0 ? null : /*#__PURE__*/ _jsx("ul", {
98
+ className: "flex flex-col gap-1.5",
99
+ children: cluster.items.map((item)=>{
100
+ const href = toSafeHref(item.url);
101
+ return /*#__PURE__*/ _jsx("li", {
102
+ className: "min-w-0",
103
+ children: href === undefined ? /*#__PURE__*/ _jsx("span", {
104
+ className: "text-xs leading-snug text-muted-foreground",
105
+ children: item.title ?? item.url
106
+ }) : /*#__PURE__*/ _jsx("a", {
107
+ href: href,
108
+ target: "_blank",
109
+ rel: "noopener noreferrer",
110
+ className: "text-xs leading-snug text-primary-emphasis underline-offset-2 hover:underline",
111
+ children: item.title ?? item.url
112
+ })
113
+ }, item.url);
114
+ })
115
+ })
116
+ ]
117
+ }, cluster.id))
118
+ ]
119
+ }),
120
+ brief.quotes === undefined || brief.quotes.length === 0 ? null : /*#__PURE__*/ _jsxs("section", {
121
+ className: "flex flex-col gap-3",
122
+ "aria-label": "Quotes",
123
+ children: [
124
+ /*#__PURE__*/ _jsx("h3", {
125
+ className: "text-sm font-semibold",
126
+ children: "Quotes"
127
+ }),
128
+ brief.quotes.map((quote)=>{
129
+ const href = toSafeHref(quote.url);
130
+ return(// <figure>/<figcaption> rather than a div and a span: a quotation
131
+ // with an attribution is exactly what the element pair is for, and
132
+ // it is what makes the attribution reachable from the quote for a
133
+ // screen reader instead of being a loose line of text after it.
134
+ /*#__PURE__*/ _jsxs("figure", {
135
+ className: "border-l-2 border-border pl-4",
136
+ children: [
137
+ /*#__PURE__*/ _jsx("blockquote", {
138
+ className: "text-sm leading-relaxed italic",
139
+ children: quote.quote
140
+ }),
141
+ /*#__PURE__*/ _jsxs("figcaption", {
142
+ className: "mt-1 text-xs text-muted-foreground",
143
+ children: [
144
+ quote.author === undefined ? "" : `${quote.author} · `,
145
+ quote.source,
146
+ href === undefined ? null : /*#__PURE__*/ _jsxs(_Fragment, {
147
+ children: [
148
+ " · ",
149
+ /*#__PURE__*/ _jsx("a", {
150
+ href: href,
151
+ target: "_blank",
152
+ rel: "noopener noreferrer",
153
+ className: "text-primary-emphasis underline-offset-2 hover:underline",
154
+ children: "source"
155
+ })
156
+ ]
157
+ })
158
+ ]
159
+ })
160
+ ]
161
+ }, `${quote.source}-${quote.quote.slice(0, 32)}`));
162
+ })
163
+ ]
164
+ }),
165
+ citations.length === 0 ? null : /*#__PURE__*/ _jsxs("section", {
166
+ className: "flex flex-col gap-3",
167
+ "aria-label": "Citations",
168
+ children: [
169
+ /*#__PURE__*/ _jsx("h3", {
170
+ className: "text-sm font-semibold",
171
+ children: "Citations"
172
+ }),
173
+ /*#__PURE__*/ _jsx("ol", {
174
+ className: "flex flex-col gap-2",
175
+ children: citations.map((citation, index)=>{
176
+ const href = toSafeHref(citation.url);
177
+ return /*#__PURE__*/ _jsxs("li", {
178
+ className: "flex items-baseline gap-3 text-xs",
179
+ children: [
180
+ /*#__PURE__*/ _jsx("span", {
181
+ className: "w-6 shrink-0 text-right font-mono text-muted-foreground tabular-nums",
182
+ children: index + 1
183
+ }),
184
+ /*#__PURE__*/ _jsxs("span", {
185
+ className: "min-w-0 leading-relaxed",
186
+ children: [
187
+ href === undefined ? /*#__PURE__*/ _jsx("span", {
188
+ className: "break-words",
189
+ children: citation.title ?? citation.url
190
+ }) : /*#__PURE__*/ _jsx("a", {
191
+ href: href,
192
+ target: "_blank",
193
+ rel: "noopener noreferrer",
194
+ className: "break-words text-primary-emphasis underline-offset-2 hover:underline",
195
+ children: citation.title ?? citation.url
196
+ }),
197
+ /*#__PURE__*/ _jsxs("span", {
198
+ className: "text-muted-foreground",
199
+ children: [
200
+ " · ",
201
+ citation.source
202
+ ]
203
+ })
204
+ ]
205
+ })
206
+ ]
207
+ }, citation.url);
208
+ })
209
+ })
210
+ ]
211
+ })
212
+ ]
213
+ });
214
+ return /*#__PURE__*/ _jsxs("div", {
215
+ className: cn("flex flex-col gap-6", className),
216
+ children: [
217
+ /*#__PURE__*/ _jsxs("header", {
218
+ className: "flex flex-col gap-1.5",
219
+ children: [
220
+ /*#__PURE__*/ _jsx("h2", {
221
+ className: "text-base leading-snug font-semibold",
222
+ children: brief.topic
223
+ }),
224
+ /*#__PURE__*/ _jsxs("p", {
225
+ className: "font-mono text-xs text-muted-foreground tabular-nums",
226
+ children: [
227
+ brief.sourceCount.toLocaleString(),
228
+ " sources · ",
229
+ brief.itemCount.toLocaleString(),
230
+ " items",
231
+ brief.range === undefined ? "" : ` · ${brief.range}`
232
+ ]
233
+ }),
234
+ brief.leadInsight === undefined ? null : /*#__PURE__*/ _jsx("p", {
235
+ className: "max-w-prose text-sm leading-relaxed",
236
+ children: brief.leadInsight
237
+ })
238
+ ]
239
+ }),
240
+ report === "" ? data : /*#__PURE__*/ _jsxs(_Fragment, {
241
+ children: [
242
+ /*#__PURE__*/ _jsx("div", {
243
+ className: "max-w-prose text-sm leading-relaxed whitespace-pre-wrap",
244
+ children: report
245
+ }),
246
+ /*#__PURE__*/ _jsxs("details", {
247
+ className: "group border-t border-border pt-4",
248
+ children: [
249
+ /*#__PURE__*/ _jsxs("summary", {
250
+ className: "cursor-pointer text-sm font-semibold text-muted-foreground hover:text-foreground",
251
+ children: [
252
+ "Sources & data (",
253
+ brief.sourceCount.toLocaleString(),
254
+ " sources ·",
255
+ " ",
256
+ brief.itemCount.toLocaleString(),
257
+ " items)"
258
+ ]
259
+ }),
260
+ /*#__PURE__*/ _jsx("div", {
261
+ className: "mt-5",
262
+ children: data
263
+ })
264
+ ]
265
+ })
266
+ ]
267
+ })
268
+ ]
269
+ });
270
+ }
@@ -0,0 +1,35 @@
1
+ import type { ReactNode } from "react";
2
+ import { type ButtonProps } from "./button.js";
3
+ export type RichEmptyStateAction = {
4
+ readonly label: string;
5
+ readonly onClick?: () => void;
6
+ /** Renders the action as a link instead of a button. */
7
+ readonly href?: string;
8
+ /** At most one `primary` per empty state — it is the one suggested way out. */
9
+ readonly variant?: "primary" | "secondary";
10
+ };
11
+ export type RichEmptyStateProps = {
12
+ readonly icon?: ReactNode;
13
+ /** What is not here, as a statement: "No skills installed yet". */
14
+ readonly title: string;
15
+ /** Why, and what this surface will look like once it is populated. */
16
+ readonly description: ReactNode;
17
+ /** Suggested next steps, rendered as buttons or links. */
18
+ readonly actions?: readonly RichEmptyStateAction[];
19
+ /** Extra controls (e.g. a menu) below the primary actions. */
20
+ readonly footer?: ReactNode;
21
+ /** Action button size. Defaults to `md`; `sm` matches a denser host chrome (e.g. a top bar). */
22
+ readonly actionSize?: ButtonProps["size"];
23
+ readonly className?: string;
24
+ };
25
+ /**
26
+ * The onboarding-grade empty state: icon, message, and suggested actions on a
27
+ * bordered surface of its own.
28
+ *
29
+ * It is not `EmptyState` — that is the quiet in-list "nothing matched" stack.
30
+ * This one is for a surface that is empty because the user has not started
31
+ * yet, where the empty state *is* the page and has to carry the way in. The
32
+ * icon stays decorative and hidden from assistive tech; the title carries the
33
+ * meaning.
34
+ */
35
+ export declare function RichEmptyState({ icon, title, description, actions, footer, actionSize, className, }: RichEmptyStateProps): import("react").JSX.Element;
@@ -0,0 +1,65 @@
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
+ function ActionControl({ action, size }) {
5
+ const variant = action.variant === "primary" ? "primary" : "secondary";
6
+ if (action.href !== undefined) {
7
+ return /*#__PURE__*/ _jsx(Button, {
8
+ asChild: true,
9
+ variant: variant,
10
+ size: size,
11
+ children: /*#__PURE__*/ _jsx("a", {
12
+ href: action.href,
13
+ children: action.label
14
+ })
15
+ });
16
+ }
17
+ return /*#__PURE__*/ _jsx(Button, {
18
+ type: "button",
19
+ variant: variant,
20
+ size: size,
21
+ onClick: action.onClick,
22
+ children: action.label
23
+ });
24
+ }
25
+ /**
26
+ * The onboarding-grade empty state: icon, message, and suggested actions on a
27
+ * bordered surface of its own.
28
+ *
29
+ * It is not `EmptyState` — that is the quiet in-list "nothing matched" stack.
30
+ * This one is for a surface that is empty because the user has not started
31
+ * yet, where the empty state *is* the page and has to carry the way in. The
32
+ * icon stays decorative and hidden from assistive tech; the title carries the
33
+ * meaning.
34
+ */ export function RichEmptyState({ icon, title, description, actions, footer, actionSize = "md", className }) {
35
+ return /*#__PURE__*/ _jsxs("div", {
36
+ "data-slot": "rich-empty-state",
37
+ className: cn("flex flex-col items-center justify-center rounded-[12px] border border-border bg-card px-6 py-10 text-center", className),
38
+ children: [
39
+ icon === undefined ? null : /*#__PURE__*/ _jsx("div", {
40
+ "aria-hidden": true,
41
+ className: "mb-4 grid size-12 place-items-center rounded-[12px] bg-muted text-muted-foreground",
42
+ children: icon
43
+ }),
44
+ /*#__PURE__*/ _jsx("h3", {
45
+ className: "text-[15px] font-bold tracking-[-0.01em]",
46
+ children: title
47
+ }),
48
+ /*#__PURE__*/ _jsx("p", {
49
+ className: "mt-2 max-w-md text-[13px] leading-relaxed text-muted-foreground",
50
+ children: description
51
+ }),
52
+ actions !== undefined && actions.length > 0 ? /*#__PURE__*/ _jsx("div", {
53
+ className: "mt-5 flex flex-wrap items-center justify-center gap-2",
54
+ children: actions.map((action)=>/*#__PURE__*/ _jsx(ActionControl, {
55
+ action: action,
56
+ size: actionSize
57
+ }, action.label))
58
+ }) : null,
59
+ footer === undefined ? null : /*#__PURE__*/ _jsx("div", {
60
+ className: "mt-4",
61
+ children: footer
62
+ })
63
+ ]
64
+ });
65
+ }
@@ -0,0 +1,23 @@
1
+ import { type ButtonProps } from "./button.js";
2
+ export type RunNowButtonProps = Omit<ButtonProps, "onClick" | "children"> & {
3
+ /** Resolve when the work is accepted, reject to show nothing changed. */
4
+ readonly onRun: () => void | Promise<void>;
5
+ readonly label?: string;
6
+ readonly pendingLabel?: string;
7
+ readonly doneLabel?: string;
8
+ /** How long the confirmation stays up, in ms. */
9
+ readonly confirmMs?: number;
10
+ };
11
+ /**
12
+ * Fire something off now — send the brief, run the schedule ahead of time.
13
+ *
14
+ * The three states exist because "did that work?" is the only question the user
15
+ * has after pressing it, and a button that snaps back to its resting label
16
+ * answers it with nothing. The confirmation is announced through `aria-live`,
17
+ * not just shown, and the label carries it — a separate toast for a
18
+ * button-sized action is more machinery than the action deserves.
19
+ *
20
+ * A rejected promise returns the button to rest rather than claiming success.
21
+ * The caller surfaces the reason; this only refuses to lie about it.
22
+ */
23
+ export declare function RunNowButton({ onRun, label, pendingLabel, doneLabel, confirmMs, disabled, ...props }: RunNowButtonProps): import("react").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { Button } from "./button.js";
4
+ /**
5
+ * Fire something off now — send the brief, run the schedule ahead of time.
6
+ *
7
+ * The three states exist because "did that work?" is the only question the user
8
+ * has after pressing it, and a button that snaps back to its resting label
9
+ * answers it with nothing. The confirmation is announced through `aria-live`,
10
+ * not just shown, and the label carries it — a separate toast for a
11
+ * button-sized action is more machinery than the action deserves.
12
+ *
13
+ * A rejected promise returns the button to rest rather than claiming success.
14
+ * The caller surfaces the reason; this only refuses to lie about it.
15
+ */ export function RunNowButton({ onRun, label = "Run now", pendingLabel = "Starting…", doneLabel = "Started", confirmMs = 2000, disabled, ...props }) {
16
+ const [state, setState] = useState("idle");
17
+ const timerRef = useRef(undefined);
18
+ // Clear on unmount: a timer that fires into an unmounted component is a
19
+ // React warning today and a leak in every version.
20
+ useEffect(()=>()=>window.clearTimeout(timerRef.current), []);
21
+ const run = async ()=>{
22
+ setState("pending");
23
+ try {
24
+ await onRun();
25
+ setState("done");
26
+ timerRef.current = window.setTimeout(()=>setState("idle"), confirmMs);
27
+ } catch {
28
+ setState("idle");
29
+ }
30
+ };
31
+ return /*#__PURE__*/ _jsx(Button, {
32
+ ...props,
33
+ disabled: disabled || state !== "idle",
34
+ onClick: ()=>{
35
+ void run();
36
+ },
37
+ children: /*#__PURE__*/ _jsx("span", {
38
+ "aria-live": "polite",
39
+ children: state === "pending" ? pendingLabel : state === "done" ? doneLabel : label
40
+ })
41
+ });
42
+ }
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from "react";
2
+ export type SectionProps = {
3
+ readonly title: ReactNode;
4
+ readonly description?: ReactNode;
5
+ /** Controls for this section: a filter, a view toggle, a primary action. */
6
+ readonly action?: ReactNode;
7
+ /** Item count shown beside the title. */
8
+ readonly count?: number;
9
+ /**
10
+ * Heading level. Defaults to `h2`. Set `h1` for the page's own header — this
11
+ * is deliberately one component and not a separate `PageHeader`, because the
12
+ * only real difference between the two is the document outline, and a second
13
+ * near-identical file is how two headers drift apart.
14
+ */
15
+ readonly as?: "h1" | "h2" | "h3";
16
+ readonly children: ReactNode;
17
+ readonly className?: string;
18
+ };
19
+ /** A titled block: heading, optional description and controls, then content. */
20
+ export declare function Section({ title, description, action, count, as, children, className }: SectionProps): import("react").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ const TITLE_CLASS = {
4
+ h1: "text-xl font-semibold tracking-tight",
5
+ h2: "text-sm font-semibold",
6
+ h3: "text-xs font-semibold"
7
+ };
8
+ /** A titled block: heading, optional description and controls, then content. */ export function Section({ title, description, action, count, as = "h2", children, className }) {
9
+ const Heading = as;
10
+ return /*#__PURE__*/ _jsxs("section", {
11
+ "data-slot": "section",
12
+ className: cn("flex flex-col gap-3", className),
13
+ children: [
14
+ /*#__PURE__*/ _jsxs("div", {
15
+ className: "flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4",
16
+ children: [
17
+ /*#__PURE__*/ _jsxs("div", {
18
+ className: "flex min-w-0 flex-col gap-1",
19
+ children: [
20
+ /*#__PURE__*/ _jsxs("div", {
21
+ className: "flex items-center gap-2",
22
+ children: [
23
+ /*#__PURE__*/ _jsx(Heading, {
24
+ className: TITLE_CLASS[as],
25
+ children: title
26
+ }),
27
+ count === undefined ? null : /*#__PURE__*/ _jsx("span", {
28
+ className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-mono text-xs text-muted-foreground",
29
+ children: count
30
+ })
31
+ ]
32
+ }),
33
+ description === undefined ? null : /*#__PURE__*/ _jsx("p", {
34
+ className: "text-sm leading-snug text-muted-foreground",
35
+ children: description
36
+ })
37
+ ]
38
+ }),
39
+ action === undefined ? null : /*#__PURE__*/ _jsx("div", {
40
+ className: "flex shrink-0 items-center gap-2",
41
+ children: action
42
+ })
43
+ ]
44
+ }),
45
+ children
46
+ ]
47
+ });
48
+ }
@@ -0,0 +1,2 @@
1
+ import type * as React from "react";
2
+ export declare function Select({ className, children, ...props }: React.ComponentProps<"select">): React.JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronDown } from "lucide-react";
3
+ import { cn } from "../lib/utils.js";
4
+ // Same border/focus-ring/disabled treatment as `Input`; the chevron is a
5
+ // decorative overlay since a native `<select>` cannot be given its own icon.
6
+ export function Select({ className, children, ...props }) {
7
+ return /*#__PURE__*/ _jsxs("div", {
8
+ className: "group relative",
9
+ children: [
10
+ /*#__PURE__*/ _jsx("select", {
11
+ "data-slot": "select",
12
+ className: cn("flex h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors", "selection:bg-primary selection:text-primary-foreground", "aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50", className),
13
+ ...props,
14
+ children: children
15
+ }),
16
+ /*#__PURE__*/ _jsx(ChevronDown, {
17
+ "aria-hidden": true,
18
+ className: "pointer-events-none absolute right-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground group-has-[:disabled]:opacity-50"
19
+ })
20
+ ]
21
+ });
22
+ }
@@ -0,0 +1,42 @@
1
+ export type SelectionCheckboxState = boolean | "indeterminate";
2
+ export type SelectionCheckboxProps = {
3
+ /**
4
+ * `"indeterminate"` is for a header checkbox driven by `selectAll`: some
5
+ * but not all rows are selected. A per-row cell only ever passes a plain
6
+ * boolean.
7
+ */
8
+ readonly checked: SelectionCheckboxState;
9
+ /**
10
+ * Fires on both a plain click and a cmd/ctrl-click — `useListSelection`'s
11
+ * `toggle` takes the same `shiftKey` modifier this passes through, so a
12
+ * row wires this straight to it without branching on the click type
13
+ * itself.
14
+ */
15
+ readonly onToggle: (event: {
16
+ readonly shiftKey: boolean;
17
+ }) => void;
18
+ /** Row label for the default accessible name, e.g. `Select "Q3 rollup"`. */
19
+ readonly rowLabel: string;
20
+ /**
21
+ * Overrides the default `Select ${rowLabel}` accessible name outright —
22
+ * for a consumer localizing this published library's one hardcoded
23
+ * English string, or phrasing the row label differently than "Select ".
24
+ */
25
+ readonly ariaLabel?: string;
26
+ readonly id?: string;
27
+ readonly className?: string;
28
+ };
29
+ /**
30
+ * A row's selection control: hidden until the row is hovered or focused, and
31
+ * always visible on a touch device — there is no hover to reveal it on, so
32
+ * `(hover: none)` forces it on. The consumer's row needs a `group` class
33
+ * ancestor (e.g. `<TableRow className="group">`) for the hover reveal to
34
+ * find; nothing here declares that ancestor itself since the row, not this
35
+ * cell, owns that boundary.
36
+ *
37
+ * Built as a `role="checkbox"` button rather than `Checkbox`'s native input:
38
+ * a native input's own focus ring and box model fight the opacity-driven
39
+ * reveal below, where the control needs to participate in hover/focus state
40
+ * that belongs to an ancestor it does not render.
41
+ */
42
+ export declare function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className }: SelectionCheckboxProps): import("react").JSX.Element;
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Check, Minus } from "lucide-react";
3
+ import { useId } from "react";
4
+ import { cn } from "../lib/utils.js";
5
+ /**
6
+ * A row's selection control: hidden until the row is hovered or focused, and
7
+ * always visible on a touch device — there is no hover to reveal it on, so
8
+ * `(hover: none)` forces it on. The consumer's row needs a `group` class
9
+ * ancestor (e.g. `<TableRow className="group">`) for the hover reveal to
10
+ * find; nothing here declares that ancestor itself since the row, not this
11
+ * cell, owns that boundary.
12
+ *
13
+ * Built as a `role="checkbox"` button rather than `Checkbox`'s native input:
14
+ * a native input's own focus ring and box model fight the opacity-driven
15
+ * reveal below, where the control needs to participate in hover/focus state
16
+ * that belongs to an ancestor it does not render.
17
+ */ export function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className }) {
18
+ const generatedId = useId();
19
+ const checkboxId = id ?? generatedId;
20
+ const isOn = checked !== false;
21
+ const isIndeterminate = checked === "indeterminate";
22
+ return /*#__PURE__*/ _jsxs("button", {
23
+ type: "button",
24
+ id: checkboxId,
25
+ role: "checkbox",
26
+ "aria-checked": isIndeterminate ? "mixed" : checked,
27
+ "aria-label": ariaLabel ?? `Select ${rowLabel}`,
28
+ onClick: (event)=>onToggle({
29
+ shiftKey: event.shiftKey
30
+ }),
31
+ onKeyDown: (event)=>{
32
+ if (event.key !== " " && event.key !== "Enter") return;
33
+ event.preventDefault();
34
+ onToggle({
35
+ shiftKey: event.shiftKey
36
+ });
37
+ },
38
+ className: cn("relative flex size-4 shrink-0 items-center justify-center rounded-sm border border-input bg-card shadow-xs transition-colors", "opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100 [@media(hover:none)]:opacity-100", isOn && "border-primary bg-primary opacity-100", // The pop plays only on landing fully checked — an indeterminate
39
+ // header checkbox flips between indeterminate and checked/unchecked
40
+ // as rows come and go, and it should not re-pop on every one of
41
+ // those incidental transitions.
42
+ checked === true && "motion-safe:[animation:corbits-selection-pop_220ms_var(--ease-out)_both]", className),
43
+ children: [
44
+ /*#__PURE__*/ _jsx(Check, {
45
+ "aria-hidden": true,
46
+ className: cn("size-3 text-primary-foreground transition-transform", checked === true ? "scale-100" : "scale-0")
47
+ }),
48
+ /*#__PURE__*/ _jsx(Minus, {
49
+ "aria-hidden": true,
50
+ className: cn("absolute size-3 text-primary-foreground transition-transform", isIndeterminate ? "scale-100" : "scale-0")
51
+ })
52
+ ]
53
+ });
54
+ }