@corbits/react-ui 0.1.0 → 0.3.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 +257 -19
  35. package/dist/test/render-hook.js +11 -12
  36. package/dist/theme.css +92 -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 +21 -4
  45. package/dist/ui/avatar.js +40 -49
  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 +85 -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 +96 -535
@@ -9,34 +9,38 @@ import { cn } from "../lib/utils.js";
9
9
  * Everything below the header is already guarded by a presence check at its own
10
10
  * render site, so validating it here would only reject briefs this component
11
11
  * draws correctly.
12
- */ export function parseResearchArtifact(json) {
12
+ */
13
+ export function parseResearchArtifact(json) {
13
14
  let raw;
14
15
  try {
15
16
  raw = JSON.parse(json);
16
- } catch {
17
+ }
18
+ catch {
17
19
  return null;
18
20
  }
19
- if (typeof raw !== "object" || raw === null) return null;
21
+ if (typeof raw !== "object" || raw === null)
22
+ return null;
20
23
  const candidate = raw;
21
- if (typeof candidate.topic !== "string" || typeof candidate.sourceCount !== "number" || typeof candidate.itemCount !== "number") {
24
+ if (typeof candidate.topic !== "string" ||
25
+ typeof candidate.sourceCount !== "number" ||
26
+ typeof candidate.itemCount !== "number") {
22
27
  return null;
23
28
  }
24
29
  const body = typeof candidate.body === "string" ? candidate.body : undefined;
25
- return body === undefined ? {
26
- brief: candidate
27
- } : {
28
- brief: candidate,
29
- body
30
- };
30
+ return body === undefined
31
+ ? { brief: candidate }
32
+ : { brief: candidate, body };
31
33
  }
32
34
  /**
33
35
  * A citation list can name the same URL twice — one item that clustered under
34
36
  * two sources. Both the numbering the reader sees and any export they take must
35
37
  * agree on the count, so dedupe once, here, at the point of render.
36
- */ function dedupeByUrl(sources) {
38
+ */
39
+ function dedupeByUrl(sources) {
37
40
  const seen = new Set();
38
- return sources.filter((source)=>{
39
- if (seen.has(source.url)) return false;
41
+ return sources.filter((source) => {
42
+ if (seen.has(source.url))
43
+ return false;
40
44
  seen.add(source.url);
41
45
  return true;
42
46
  });
@@ -60,211 +64,26 @@ import { cn } from "../lib/utils.js";
60
64
  * ships no Markdown dependency, and adding ~40kB of parser that a consumer then
61
65
  * owns is not something a component should decide on their behalf. Wrap the
62
66
  * `body` slot in your own renderer if you have one.
63
- */ export function ResearchBody({ brief, body, className }) {
67
+ */
68
+ export function ResearchBody({ brief, body, className, }) {
64
69
  const report = body?.trim() ?? "";
65
70
  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)=>{
71
+ const data = (_jsxs("div", { className: "flex flex-col gap-6", children: [brief.clusters === undefined || brief.clusters.length === 0 ? null : (_jsxs("section", { className: "flex flex-col gap-3", "aria-label": "Themes", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Themes" }), brief.clusters.map((cluster) => (_jsxs("article", { className: "flex flex-col gap-2 rounded-lg border border-border p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("h4", { className: "text-sm leading-snug font-semibold", children: cluster.title }), cluster.sources === undefined ||
72
+ cluster.sources.length === 0 ? null : (_jsx("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 text-xs text-muted-foreground", children: cluster.sources.join(", ") }))] }), cluster.summary === undefined ? null : (_jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: cluster.summary })), cluster.items === undefined ||
73
+ cluster.items.length === 0 ? null : (_jsx("ul", { className: "flex flex-col gap-1.5", children: cluster.items.map((item) => {
74
+ const href = toSafeHref(item.url);
75
+ return (_jsx("li", { className: "min-w-0", children: href === undefined ? (_jsx("span", { className: "text-xs leading-snug text-muted-foreground", children: item.title ?? item.url })) : (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "text-xs leading-snug text-primary-emphasis underline-offset-2 hover:underline", children: item.title ?? item.url })) }, item.url));
76
+ }) }))] }, cluster.id)))] })), brief.quotes === undefined || brief.quotes.length === 0 ? null : (_jsxs("section", { className: "flex flex-col gap-3", "aria-label": "Quotes", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Quotes" }), brief.quotes.map((quote) => {
129
77
  const href = toSafeHref(quote.url);
130
- return(// <figure>/<figcaption> rather than a div and a span: a quotation
78
+ return (
79
+ // <figure>/<figcaption> rather than a div and a span: a quotation
131
80
  // with an attribution is exactly what the element pair is for, and
132
81
  // it is what makes the attribution reachable from the quote for a
133
82
  // 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)=>{
83
+ _jsxs("figure", { className: "border-l-2 border-border pl-4", children: [_jsx("blockquote", { className: "text-sm leading-relaxed italic", children: quote.quote }), _jsxs("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: [quote.author === undefined ? "" : `${quote.author} · `, quote.source, href === undefined ? null : (_jsxs(_Fragment, { children: [" · ", _jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "text-primary-emphasis underline-offset-2 hover:underline", children: "source" })] }))] })] }, `${quote.source}-${quote.quote.slice(0, 32)}`));
84
+ })] })), citations.length === 0 ? null : (_jsxs("section", { className: "flex flex-col gap-3", "aria-label": "Citations", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Citations" }), _jsx("ol", { className: "flex flex-col gap-2", children: citations.map((citation, index) => {
176
85
  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
- });
86
+ return (_jsxs("li", { className: "flex items-baseline gap-3 text-xs", children: [_jsx("span", { className: "w-6 shrink-0 text-right font-mono text-muted-foreground tabular-nums", children: index + 1 }), _jsxs("span", { className: "min-w-0 leading-relaxed", children: [href === undefined ? (_jsx("span", { className: "break-words", children: citation.title ?? citation.url })) : (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "break-words text-primary-emphasis underline-offset-2 hover:underline", children: citation.title ?? citation.url })), _jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", citation.source] })] })] }, citation.url));
87
+ }) })] }))] }));
88
+ return (_jsxs("div", { className: cn("flex flex-col gap-6", className), children: [_jsxs("header", { className: "flex flex-col gap-1.5", children: [_jsx("h2", { className: "text-base leading-snug font-semibold", children: brief.topic }), _jsxs("p", { className: "font-mono text-xs text-muted-foreground tabular-nums", children: [brief.sourceCount.toLocaleString(), " sources \u00B7", " ", brief.itemCount.toLocaleString(), " items", brief.range === undefined ? "" : ` · ${brief.range}`] }), brief.leadInsight === undefined ? null : (_jsx("p", { className: "max-w-prose text-sm leading-relaxed", children: brief.leadInsight }))] }), report === "" ? (data) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "max-w-prose text-sm leading-relaxed whitespace-pre-wrap", children: report }), _jsxs("details", { className: "group border-t border-border pt-4", children: [_jsxs("summary", { className: "cursor-pointer text-sm font-semibold text-muted-foreground hover:text-foreground", children: ["Sources & data (", brief.sourceCount.toLocaleString(), " sources \u00B7", " ", brief.itemCount.toLocaleString(), " items)"] }), _jsx("div", { className: "mt-5", children: data })] })] }))] }));
270
89
  }
@@ -1,26 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "../lib/utils.js";
3
3
  import { Button } from "./button.js";
4
- function ActionControl({ action, size }) {
4
+ function ActionControl({ action, size, }) {
5
5
  const variant = action.variant === "primary" ? "primary" : "secondary";
6
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
- });
7
+ return (_jsx(Button, { asChild: true, variant: variant, size: size, children: _jsx("a", { href: action.href, children: action.label }) }));
16
8
  }
17
- return /*#__PURE__*/ _jsx(Button, {
18
- type: "button",
19
- variant: variant,
20
- size: size,
21
- onClick: action.onClick,
22
- children: action.label
23
- });
9
+ return (_jsx(Button, { type: "button", variant: variant, size: size, onClick: action.onClick, children: action.label }));
24
10
  }
25
11
  /**
26
12
  * The onboarding-grade empty state: icon, message, and suggested actions on a
@@ -31,35 +17,7 @@ function ActionControl({ action, size }) {
31
17
  * yet, where the empty state *is* the page and has to carry the way in. The
32
18
  * icon stays decorative and hidden from assistive tech; the title carries the
33
19
  * 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
- });
20
+ */
21
+ export function RichEmptyState({ icon, title, description, actions, footer, actionSize = "md", className, }) {
22
+ return (_jsxs("div", { "data-slot": "rich-empty-state", className: cn("flex flex-col items-center justify-center rounded-[12px] border border-border bg-card px-6 py-10 text-center", className), children: [icon === undefined ? null : (_jsx("div", { "aria-hidden": true, className: "mb-4 grid size-12 place-items-center rounded-[12px] bg-muted text-muted-foreground", children: icon })), _jsx("h3", { className: "text-[15px] font-bold tracking-[-0.01em]", children: title }), _jsx("p", { className: "mt-2 max-w-md text-[13px] leading-relaxed text-muted-foreground", children: description }), actions !== undefined && actions.length > 0 ? (_jsx("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-2", children: actions.map((action) => (_jsx(ActionControl, { action: action, size: actionSize }, action.label))) })) : null, footer === undefined ? null : _jsx("div", { className: "mt-4", children: footer })] }));
65
23
  }
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import { useEffect, useRef, useState } from "react";
3
4
  import { Button } from "./button.js";
@@ -12,31 +13,29 @@ import { Button } from "./button.js";
12
13
  *
13
14
  * A rejected promise returns the button to rest rather than claiming success.
14
15
  * 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
+ */
17
+ export function RunNowButton({ onRun, label = "Run now", pendingLabel = "Starting…", doneLabel = "Started", confirmMs = 2000, disabled, ...props }) {
16
18
  const [state, setState] = useState("idle");
17
19
  const timerRef = useRef(undefined);
18
20
  // Clear on unmount: a timer that fires into an unmounted component is a
19
21
  // React warning today and a leak in every version.
20
- useEffect(()=>()=>window.clearTimeout(timerRef.current), []);
21
- const run = async ()=>{
22
+ useEffect(() => () => window.clearTimeout(timerRef.current), []);
23
+ const run = async () => {
22
24
  setState("pending");
23
25
  try {
24
26
  await onRun();
25
27
  setState("done");
26
- timerRef.current = window.setTimeout(()=>setState("idle"), confirmMs);
27
- } catch {
28
+ timerRef.current = window.setTimeout(() => setState("idle"), confirmMs);
29
+ }
30
+ catch {
28
31
  setState("idle");
29
32
  }
30
33
  };
31
- return /*#__PURE__*/ _jsx(Button, {
32
- ...props,
33
- disabled: disabled || state !== "idle",
34
- onClick: ()=>{
34
+ return (_jsx(Button, { ...props, disabled: disabled || state !== "idle", onClick: () => {
35
35
  void run();
36
- },
37
- children: /*#__PURE__*/ _jsx("span", {
38
- "aria-live": "polite",
39
- children: state === "pending" ? pendingLabel : state === "done" ? doneLabel : label
40
- })
41
- });
36
+ }, children: _jsx("span", { "aria-live": "polite", children: state === "pending"
37
+ ? pendingLabel
38
+ : state === "done"
39
+ ? doneLabel
40
+ : label }) }));
42
41
  }
@@ -17,4 +17,4 @@ export type SectionProps = {
17
17
  readonly className?: string;
18
18
  };
19
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;
20
+ export declare function Section({ title, description, action, count, as, children, className, }: SectionProps): import("react").JSX.Element;
@@ -3,46 +3,10 @@ import { cn } from "../lib/utils.js";
3
3
  const TITLE_CLASS = {
4
4
  h1: "text-xl font-semibold tracking-tight",
5
5
  h2: "text-sm font-semibold",
6
- h3: "text-xs font-semibold"
6
+ h3: "text-xs font-semibold",
7
7
  };
8
- /** A titled block: heading, optional description and controls, then content. */ export function Section({ title, description, action, count, as = "h2", children, className }) {
8
+ /** A titled block: heading, optional description and controls, then content. */
9
+ export function Section({ title, description, action, count, as = "h2", children, className, }) {
9
10
  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
- });
11
+ return (_jsxs("section", { "data-slot": "section", className: cn("flex flex-col gap-3", className), children: [_jsxs("div", { className: "flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Heading, { className: TITLE_CLASS[as], children: title }), count === undefined ? null : (_jsx("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-mono text-xs text-muted-foreground", children: count }))] }), description === undefined ? null : (_jsx("p", { className: "text-sm leading-snug text-muted-foreground", children: description }))] }), action === undefined ? null : (_jsx("div", { className: "flex shrink-0 items-center gap-2", children: action }))] }), children] }));
48
12
  }
package/dist/ui/select.js CHANGED
@@ -4,19 +4,5 @@ import { cn } from "../lib/utils.js";
4
4
  // Same border/focus-ring/disabled treatment as `Input`; the chevron is a
5
5
  // decorative overlay since a native `<select>` cannot be given its own icon.
6
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
- });
7
+ return (_jsxs("div", { className: "group relative", children: [_jsx("select", { "data-slot": "select", 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), ...props, children: children }), _jsx(ChevronDown, { "aria-hidden": true, className: "pointer-events-none absolute right-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground group-has-[:disabled]:opacity-50" })] }));
22
8
  }
@@ -39,4 +39,4 @@ export type SelectionCheckboxProps = {
39
39
  * reveal below, where the control needs to participate in hover/focus state
40
40
  * that belongs to an ancestor it does not render.
41
41
  */
42
- export declare function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className }: SelectionCheckboxProps): import("react").JSX.Element;
42
+ export declare function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className, }: SelectionCheckboxProps): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { Check, Minus } from "lucide-react";
3
4
  import { useId } from "react";
@@ -14,41 +15,22 @@ import { cn } from "../lib/utils.js";
14
15
  * a native input's own focus ring and box model fight the opacity-driven
15
16
  * reveal below, where the control needs to participate in hover/focus state
16
17
  * that belongs to an ancestor it does not render.
17
- */ export function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className }) {
18
+ */
19
+ export function SelectionCheckbox({ checked, onToggle, rowLabel, ariaLabel, id, className, }) {
18
20
  const generatedId = useId();
19
21
  const checkboxId = id ?? generatedId;
20
22
  const isOn = checked !== false;
21
23
  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;
24
+ return (_jsxs("button", { type: "button", id: checkboxId, role: "checkbox", "aria-checked": isIndeterminate ? "mixed" : checked, "aria-label": ariaLabel ?? `Select ${rowLabel}`, onClick: (event) => onToggle({ shiftKey: event.shiftKey }), onKeyDown: (event) => {
25
+ if (event.key !== " " && event.key !== "Enter")
26
+ return;
33
27
  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
28
+ onToggle({ shiftKey: event.shiftKey });
29
+ }, 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",
30
+ // The pop plays only on landing fully checked — an indeterminate
39
31
  // header checkbox flips between indeterminate and checked/unchecked
40
32
  // as rows come and go, and it should not re-pop on every one of
41
33
  // 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
- });
34
+ checked === true &&
35
+ "motion-safe:[animation:corbits-selection-pop_220ms_var(--ease-out)_both]", className), children: [_jsx(Check, { "aria-hidden": true, className: cn("size-3 text-primary-foreground transition-transform", checked === true ? "scale-100" : "scale-0") }), _jsx(Minus, { "aria-hidden": true, className: cn("absolute size-3 text-primary-foreground transition-transform", isIndeterminate ? "scale-100" : "scale-0") })] }));
54
36
  }
@@ -15,54 +15,11 @@ import { Button } from "./button.js";
15
15
  * The save control is disabled when there is nothing to save, and the panel
16
16
  * says why in text as well: a greyed-out button with no explanation is the most
17
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
- });
18
+ */
19
+ export function SettingsPanel({ title, description, onSave, dirty = false, saving = false, error = null, savedAt = null, onReset, children, className, }) {
20
+ return (_jsxs("section", { "data-slot": "settings-panel", "aria-label": title, className: cn("flex flex-col gap-4 rounded-lg border border-border bg-card p-5 text-card-foreground", className), children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("h3", { className: "text-sm font-semibold", children: title }), description === undefined ? null : (_jsx("p", { className: "text-sm leading-snug text-muted-foreground", children: description }))] }), children, error === null ? null : (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })), onSave === undefined ? null : (_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t border-border pt-4", children: [_jsx(Button, { size: "sm", onClick: onSave, disabled: !dirty || saving, children: saving ? "Saving…" : "Save changes" }), onReset === undefined || !dirty ? null : (_jsx(Button, { variant: "ghost", size: "sm", onClick: onReset, disabled: saving, children: "Discard" })), _jsx("p", { "aria-live": "polite", className: "ml-auto text-xs text-muted-foreground", children: dirty
21
+ ? "Unsaved changes"
22
+ : savedAt === null
23
+ ? ""
24
+ : `Saved ${savedAt}` })] }))] }));
68
25
  }
@@ -11,10 +11,7 @@ import { cn } from "../lib/utils.js";
11
11
  * that would still leave `text-transparent` + `background-clip: text` with
12
12
  * nothing to paint. Under `prefers-reduced-motion` and `forced-colors` this
13
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
- });
14
+ */
15
+ export function ShimmerText({ children, role, className }) {
16
+ return (_jsx("span", { role: role, 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), children: children }));
20
17
  }