@corbits/react-ui 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/LICENSE +174 -499
  2. package/README.md +228 -85
  3. package/dist/blocks/login/auth-layout.d.ts +1 -1
  4. package/dist/blocks/login/auth-layout.js +3 -33
  5. package/dist/blocks/login/login-form.js +6 -119
  6. package/dist/hooks/use-command-palette-navigation.js +18 -17
  7. package/dist/hooks/use-controllable-state.d.ts +2 -2
  8. package/dist/hooks/use-controllable-state.js +23 -15
  9. package/dist/hooks/use-delayed-autofocus.js +10 -11
  10. package/dist/hooks/use-dismissable-popover.d.ts +2 -2
  11. package/dist/hooks/use-dismissable-popover.js +21 -29
  12. package/dist/hooks/use-list-selection.js +54 -85
  13. package/dist/hooks/use-prefers-reduced-motion.js +8 -4
  14. package/dist/hooks/use-scroll-current-into-view.js +5 -7
  15. package/dist/index.d.ts +0 -11
  16. package/dist/index.js +10 -19
  17. package/dist/lib/artifact.js +16 -35
  18. package/dist/lib/chart-geometry.js +33 -20
  19. package/dist/lib/chart-palette.js +17 -31
  20. package/dist/lib/chat-message.js +32 -13
  21. package/dist/lib/chat-parts.js +2 -1
  22. package/dist/lib/csv.js +42 -26
  23. package/dist/lib/metrics.js +30 -48
  24. package/dist/lib/now-item.js +26 -25
  25. package/dist/lib/relative-time.js +14 -29
  26. package/dist/lib/theme.js +32 -30
  27. package/dist/lib/url.js +9 -8
  28. package/dist/lib/workflow-display-flow.js +37 -33
  29. package/dist/lib/workflow-registry.d.ts +0 -41
  30. package/dist/lib/workflow-registry.js +13 -23
  31. package/dist/lib/workflow-run-error.js +16 -20
  32. package/dist/lib/workflow-run-progress.js +94 -52
  33. package/dist/lib/workflow-run.js +17 -13
  34. package/dist/styles.css +11 -11
  35. package/dist/test/render-hook.js +11 -12
  36. package/dist/theme.css +51 -14
  37. package/dist/ui/agent-turn.d.ts +1 -1
  38. package/dist/ui/agent-turn.js +4 -37
  39. package/dist/ui/animated-number.js +15 -28
  40. package/dist/ui/approval-card.js +5 -90
  41. package/dist/ui/artifact-body.js +47 -125
  42. package/dist/ui/artifact-notice.d.ts +1 -1
  43. package/dist/ui/artifact-notice.js +9 -16
  44. package/dist/ui/avatar.d.ts +1 -1
  45. package/dist/ui/avatar.js +11 -48
  46. package/dist/ui/badge.js +4 -12
  47. package/dist/ui/bar-chart.d.ts +1 -1
  48. package/dist/ui/bar-chart.js +22 -74
  49. package/dist/ui/block-card.js +7 -45
  50. package/dist/ui/boot-screen.d.ts +1 -1
  51. package/dist/ui/boot-screen.js +4 -33
  52. package/dist/ui/bulk-action-bar.d.ts +1 -1
  53. package/dist/ui/bulk-action-bar.js +14 -33
  54. package/dist/ui/button.js +10 -24
  55. package/dist/ui/card.js +5 -25
  56. package/dist/ui/category-bars.js +14 -59
  57. package/dist/ui/chart-frame.d.ts +2 -2
  58. package/dist/ui/chart-frame.js +6 -85
  59. package/dist/ui/chat-composer.js +4 -16
  60. package/dist/ui/chat-dock-timing.js +7 -3
  61. package/dist/ui/chat-input.js +20 -100
  62. package/dist/ui/chat-panel.d.ts +1 -1
  63. package/dist/ui/chat-panel.js +7 -41
  64. package/dist/ui/chat-thread.js +13 -31
  65. package/dist/ui/checkbox.js +10 -52
  66. package/dist/ui/command-palette.js +71 -246
  67. package/dist/ui/compare-body.d.ts +1 -1
  68. package/dist/ui/compare-body.js +28 -98
  69. package/dist/ui/confirm-button.js +8 -15
  70. package/dist/ui/corbits-mark.js +7 -16
  71. package/dist/ui/csv-table.d.ts +1 -1
  72. package/dist/ui/csv-table.js +8 -50
  73. package/dist/ui/dialog.d.ts +1 -1
  74. package/dist/ui/dialog.js +32 -62
  75. package/dist/ui/dictation-status-line.js +7 -25
  76. package/dist/ui/dither-canvas.d.ts +1 -1
  77. package/dist/ui/dither-canvas.js +31 -56
  78. package/dist/ui/embed-body.d.ts +1 -1
  79. package/dist/ui/embed-body.js +7 -56
  80. package/dist/ui/empty-state.d.ts +1 -1
  81. package/dist/ui/empty-state.js +3 -26
  82. package/dist/ui/file-input.js +17 -37
  83. package/dist/ui/filter-bar.d.ts +1 -1
  84. package/dist/ui/filter-bar.js +11 -71
  85. package/dist/ui/filter-chip.d.ts +1 -1
  86. package/dist/ui/filter-chip.js +7 -15
  87. package/dist/ui/gate-block.d.ts +2 -1
  88. package/dist/ui/gate-block.js +4 -29
  89. package/dist/ui/horizontal-stepper.d.ts +1 -1
  90. package/dist/ui/horizontal-stepper.js +18 -45
  91. package/dist/ui/input.js +1 -6
  92. package/dist/ui/inspector-shell.d.ts +5 -5
  93. package/dist/ui/inspector-shell.js +14 -81
  94. package/dist/ui/intake-form.d.ts +1 -1
  95. package/dist/ui/intake-form.js +21 -97
  96. package/dist/ui/kind-card-grid.js +9 -31
  97. package/dist/ui/library-page-header.js +4 -32
  98. package/dist/ui/list-detail.js +5 -35
  99. package/dist/ui/live-run-banner.d.ts +1 -1
  100. package/dist/ui/live-run-banner.js +5 -15
  101. package/dist/ui/live-status-line.js +6 -24
  102. package/dist/ui/menu.js +7 -24
  103. package/dist/ui/message-bubble.d.ts +3 -3
  104. package/dist/ui/message-bubble.js +5 -44
  105. package/dist/ui/mic-button.d.ts +1 -1
  106. package/dist/ui/mic-button.js +15 -21
  107. package/dist/ui/mic-permission-dialog.js +5 -41
  108. package/dist/ui/notifications-bell.js +9 -43
  109. package/dist/ui/now-cards.d.ts +2 -2
  110. package/dist/ui/now-cards.js +16 -94
  111. package/dist/ui/page-panel.js +11 -11
  112. package/dist/ui/page-shell.js +4 -11
  113. package/dist/ui/parts-renderer.js +51 -149
  114. package/dist/ui/profile-card.js +7 -111
  115. package/dist/ui/progress-checklist.js +11 -49
  116. package/dist/ui/provider-mark.d.ts +2 -2
  117. package/dist/ui/provider-mark.js +9 -16
  118. package/dist/ui/quick-reply-chips.js +6 -14
  119. package/dist/ui/quote-card.js +12 -32
  120. package/dist/ui/reasoning-block.d.ts +1 -1
  121. package/dist/ui/reasoning-block.js +14 -33
  122. package/dist/ui/research-body.js +32 -213
  123. package/dist/ui/rich-empty-state.js +6 -48
  124. package/dist/ui/run-now-button.js +14 -15
  125. package/dist/ui/section.d.ts +1 -1
  126. package/dist/ui/section.js +4 -40
  127. package/dist/ui/select.js +1 -15
  128. package/dist/ui/selection-checkbox.d.ts +1 -1
  129. package/dist/ui/selection-checkbox.js +11 -29
  130. package/dist/ui/settings-panel.js +7 -50
  131. package/dist/ui/shimmer-text.js +3 -6
  132. package/dist/ui/sidebar-item-row.d.ts +1 -1
  133. package/dist/ui/sidebar-item-row.js +9 -39
  134. package/dist/ui/sidebar-panel.js +15 -40
  135. package/dist/ui/sidebar.js +15 -76
  136. package/dist/ui/skeleton.js +3 -7
  137. package/dist/ui/sortable-table.js +29 -72
  138. package/dist/ui/sparkline.js +14 -38
  139. package/dist/ui/stat-grid.js +17 -52
  140. package/dist/ui/status-dot.d.ts +1 -1
  141. package/dist/ui/status-dot.js +8 -20
  142. package/dist/ui/step-list.d.ts +1 -0
  143. package/dist/ui/step-list.js +11 -50
  144. package/dist/ui/switch.js +4 -16
  145. package/dist/ui/table.js +7 -32
  146. package/dist/ui/tabs.js +39 -54
  147. package/dist/ui/textarea.js +10 -19
  148. package/dist/ui/theme-provider.js +54 -78
  149. package/dist/ui/theme-toggle.js +13 -16
  150. package/dist/ui/thinking-indicator.js +3 -15
  151. package/dist/ui/thinking-label.js +16 -35
  152. package/dist/ui/thinking-mark.d.ts +1 -1
  153. package/dist/ui/thinking-mark.js +4 -145
  154. package/dist/ui/time-series-chart.js +43 -174
  155. package/dist/ui/toast.js +12 -20
  156. package/dist/ui/token-mosaic.js +5 -43
  157. package/dist/ui/tool-block.js +18 -70
  158. package/dist/ui/tool-narrative.d.ts +1 -1
  159. package/dist/ui/tool-narrative.js +10 -57
  160. package/dist/ui/tool-picker.js +57 -145
  161. package/dist/ui/tooltip.d.ts +1 -1
  162. package/dist/ui/tooltip.js +4 -33
  163. package/dist/ui/top-bar.js +14 -72
  164. package/dist/ui/trace-waterfall.d.ts +1 -1
  165. package/dist/ui/trace-waterfall.js +21 -93
  166. package/dist/ui/typing-indicator.d.ts +1 -1
  167. package/dist/ui/typing-indicator.js +4 -18
  168. package/dist/ui/view-toggle.js +10 -29
  169. package/dist/ui/voice-waveform.js +3 -11
  170. package/package.json +94 -535
@@ -1,15 +1,19 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { useEffect, useId, useMemo, useRef } from "react";
3
4
  import { cn } from "../lib/utils.js";
4
5
  import { useCommandPaletteNavigation } from "../hooks/use-command-palette-navigation.js";
5
6
  import { Dialog, DialogContent, DialogTitle } from "./dialog.js";
6
7
  function flatten(groups) {
7
- return groups.flatMap((group)=>group.items);
8
+ return groups.flatMap((group) => group.items);
8
9
  }
9
10
  function statusMessage(loading, error, hasQuery) {
10
- if (error !== undefined) return error;
11
- if (loading) return "Searching…";
12
- if (hasQuery) return "No matches found.";
11
+ if (error !== undefined)
12
+ return error;
13
+ if (loading)
14
+ return "Searching…";
15
+ if (hasQuery)
16
+ return "No matches found.";
13
17
  return "Type to search.";
14
18
  }
15
19
  /**
@@ -17,28 +21,25 @@ function statusMessage(loading, error, hasQuery) {
17
21
  * the flattened result order, the keyboard driver over it, and the ids that
18
22
  * wire the input to the listbox. Shared so the dialog and the inline surface
19
23
  * can never drift into two different keyboard contracts.
20
- */ function usePaletteMachine(groups, onSelect, onClose) {
24
+ */
25
+ function usePaletteMachine(groups, onSelect, onClose) {
21
26
  const baseId = useId();
22
27
  const listRef = useRef(null);
23
- const items = useMemo(()=>flatten(groups), [
24
- groups
25
- ]);
26
- const select = (id)=>{
28
+ const items = useMemo(() => flatten(groups), [groups]);
29
+ const select = (id) => {
27
30
  onSelect(id);
28
31
  onClose();
29
32
  };
30
33
  const navigation = useCommandPaletteNavigation({
31
34
  items,
32
35
  onSelect: select,
33
- onClose
36
+ onClose,
34
37
  });
35
- useEffect(()=>{
36
- listRef.current?.querySelector('[data-active="true"]')?.scrollIntoView({
37
- block: "nearest"
38
- });
39
- }, [
40
- navigation.activeId
41
- ]);
38
+ useEffect(() => {
39
+ listRef.current
40
+ ?.querySelector('[data-active="true"]')
41
+ ?.scrollIntoView({ block: "nearest" });
42
+ }, [navigation.activeId]);
42
43
  return {
43
44
  baseId,
44
45
  groups,
@@ -47,106 +48,28 @@ function statusMessage(loading, error, hasQuery) {
47
48
  setActiveId: navigation.setActiveId,
48
49
  onKeyDown: navigation.onKeyDown,
49
50
  select,
50
- optionId: (id)=>`${baseId}-option-${id}`,
51
- listRef
51
+ optionId: (id) => `${baseId}-option-${id}`,
52
+ listRef,
52
53
  };
53
54
  }
54
- function PaletteInput({ machine, inputRef, query, onQueryChange, loading, placeholder, className }) {
55
- return /*#__PURE__*/ _jsx("input", {
56
- ref: inputRef,
57
- type: "text",
58
- role: "combobox",
59
- "data-slot": "command-palette-input",
60
- autoComplete: "off",
61
- spellCheck: false,
62
- placeholder: placeholder,
63
- "aria-label": "Search",
64
- "aria-expanded": true,
65
- "aria-controls": `${machine.baseId}-list`,
66
- "aria-activedescendant": machine.activeId === undefined ? undefined : machine.optionId(machine.activeId),
67
- "aria-autocomplete": "list",
68
- "aria-busy": loading,
69
- value: query,
70
- onChange: (event)=>onQueryChange(event.target.value),
71
- className: className
72
- });
55
+ function PaletteInput({ machine, inputRef, query, onQueryChange, loading, placeholder, className, }) {
56
+ return (_jsx("input", { ref: inputRef, type: "text", role: "combobox", "data-slot": "command-palette-input", autoComplete: "off", spellCheck: false, placeholder: placeholder, "aria-label": "Search", "aria-expanded": true, "aria-controls": `${machine.baseId}-list`, "aria-activedescendant": machine.activeId === undefined
57
+ ? undefined
58
+ : machine.optionId(machine.activeId), "aria-autocomplete": "list", "aria-busy": loading, value: query, onChange: (event) => onQueryChange(event.target.value), className: className }));
73
59
  }
74
- function PaletteResults({ machine, query, loading, error, className }) {
60
+ function PaletteResults({ machine, query, loading, error, className, }) {
75
61
  const message = statusMessage(loading, error, query.trim().length > 0);
76
- return /*#__PURE__*/ _jsx("ul", {
77
- ref: machine.listRef,
78
- id: `${machine.baseId}-list`,
79
- role: "listbox",
80
- "aria-label": "Results",
81
- className: className,
82
- children: machine.items.length === 0 ? /*#__PURE__*/ _jsx("li", {
83
- role: "presentation",
84
- className: "px-2 py-8 text-center text-sm text-muted-foreground",
85
- children: message
86
- }) : machine.groups.map((group)=>group.items.length === 0 ? null : /*#__PURE__*/ _jsxs("li", {
87
- role: "presentation",
88
- children: [
89
- /*#__PURE__*/ _jsx("p", {
90
- className: "px-2 py-1.5 text-[11px] font-semibold tracking-[0.1em] text-muted-foreground uppercase",
91
- children: group.heading
92
- }),
93
- /*#__PURE__*/ _jsx("ul", {
94
- role: "presentation",
95
- children: group.items.map((item)=>/*#__PURE__*/ _jsxs("li", {
96
- id: machine.optionId(item.id),
97
- role: "option",
98
- "aria-selected": item.id === machine.activeId,
99
- "data-active": item.id === machine.activeId,
100
- onPointerMove: ()=>machine.setActiveId(item.id),
101
- onClick: ()=>machine.select(item.id),
102
- className: cn("flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-sm", item.id === machine.activeId && "bg-muted"),
103
- children: [
104
- item.icon === undefined ? null : /*#__PURE__*/ _jsx("span", {
105
- className: "grid size-4 shrink-0 place-items-center text-muted-foreground [&_svg]:size-4",
106
- "aria-hidden": true,
107
- children: item.icon
108
- }),
109
- /*#__PURE__*/ _jsxs("span", {
110
- className: "min-w-0 flex-1",
111
- children: [
112
- /*#__PURE__*/ _jsx("span", {
113
- className: "block truncate",
114
- children: item.title
115
- }),
116
- item.subtitle === undefined ? null : /*#__PURE__*/ _jsx("span", {
117
- className: "block truncate text-xs text-muted-foreground",
118
- children: item.subtitle
119
- })
120
- ]
121
- })
122
- ]
123
- }, item.id))
124
- })
125
- ]
126
- }, group.id))
127
- });
62
+ return (_jsx("ul", { ref: machine.listRef, id: `${machine.baseId}-list`, role: "listbox", "aria-label": "Results", className: className, children: machine.items.length === 0 ? (_jsx("li", { role: "presentation", className: "px-2 py-8 text-center text-sm text-muted-foreground", children: message })) : (machine.groups.map((group) => group.items.length === 0 ? null : (_jsxs("li", { role: "presentation", children: [_jsx("p", { className: "px-2 py-1.5 text-[11px] font-semibold tracking-[0.1em] text-muted-foreground uppercase", children: group.heading }), _jsx("ul", { role: "presentation", children: group.items.map((item) => (_jsxs("li", { id: machine.optionId(item.id), role: "option", "aria-selected": item.id === machine.activeId, "data-active": item.id === machine.activeId, onPointerMove: () => machine.setActiveId(item.id), onClick: () => machine.select(item.id), className: cn("flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-sm", item.id === machine.activeId && "bg-muted"), children: [item.icon === undefined ? null : (_jsx("span", { className: "grid size-4 shrink-0 place-items-center text-muted-foreground [&_svg]:size-4", "aria-hidden": true, children: item.icon })), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate", children: item.title }), item.subtitle === undefined ? null : (_jsx("span", { className: "block truncate text-xs text-muted-foreground", children: item.subtitle }))] })] }, item.id))) })] }, group.id)))) }));
128
63
  }
129
- function PaletteLoadMore({ hasMore, onLoadMore, loading }) {
130
- if (!hasMore || onLoadMore === undefined) return null;
131
- return /*#__PURE__*/ _jsx("div", {
132
- className: "border-t border-border p-2",
133
- children: /*#__PURE__*/ _jsx("button", {
134
- type: "button",
135
- "data-slot": "command-palette-load-more",
136
- onClick: onLoadMore,
137
- disabled: loading,
138
- className: "w-full rounded-md px-2.5 py-2 text-center text-xs font-medium text-muted-foreground hover:bg-muted disabled:opacity-50",
139
- children: loading ? "Loading…" : "Load more"
140
- })
141
- });
64
+ function PaletteLoadMore({ hasMore, onLoadMore, loading, }) {
65
+ if (!hasMore || onLoadMore === undefined)
66
+ return null;
67
+ return (_jsx("div", { className: "border-t border-border p-2", children: _jsx("button", { type: "button", "data-slot": "command-palette-load-more", onClick: onLoadMore, disabled: loading, className: "w-full rounded-md px-2.5 py-2 text-center text-xs font-medium text-muted-foreground hover:bg-muted disabled:opacity-50", children: loading ? "Loading…" : "Load more" }) }));
142
68
  }
143
69
  function PaletteFooter({ footer }) {
144
- if (footer === undefined) return null;
145
- return /*#__PURE__*/ _jsx("div", {
146
- "data-slot": "command-palette-footer",
147
- className: "border-t border-border px-3 py-2 text-xs text-muted-foreground",
148
- children: footer
149
- });
70
+ if (footer === undefined)
71
+ return null;
72
+ return (_jsx("div", { "data-slot": "command-palette-footer", className: "border-t border-border px-3 py-2 text-xs text-muted-foreground", children: footer }));
150
73
  }
151
74
  /**
152
75
  * The palette as a modal dialog: a search input over consumer-supplied,
@@ -159,62 +82,15 @@ function PaletteFooter({ footer }) {
159
82
  * `corbits-fade-out` keyframes), which already collapses under
160
83
  * `prefers-reduced-motion` at the theme level — a palette opened many times a
161
84
  * day earns no scale or position animation on top of that.
162
- */ export function CommandPalette({ open, onOpenChange, query, onQueryChange, groups, onSelect, loading = false, error, hasMore = false, onLoadMore, placeholder = "Search or jump to…", inputAccessory, footer, className }) {
85
+ */
86
+ export function CommandPalette({ open, onOpenChange, query, onQueryChange, groups, onSelect, loading = false, error, hasMore = false, onLoadMore, placeholder = "Search or jump to…", inputAccessory, footer, className, }) {
163
87
  const inputRef = useRef(null);
164
- const machine = usePaletteMachine(groups, onSelect, ()=>onOpenChange(false));
165
- useEffect(()=>{
166
- if (open) inputRef.current?.focus();
167
- }, [
168
- open
169
- ]);
170
- return /*#__PURE__*/ _jsx(Dialog, {
171
- open: open,
172
- onOpenChange: onOpenChange,
173
- children: /*#__PURE__*/ _jsxs(DialogContent, {
174
- className: cn("max-w-xl gap-0 p-0 [&_[aria-label=Close]]:hidden", className),
175
- onKeyDown: machine.onKeyDown,
176
- children: [
177
- /*#__PURE__*/ _jsx(DialogTitle, {
178
- className: "sr-only",
179
- children: "Search"
180
- }),
181
- /*#__PURE__*/ _jsxs("div", {
182
- className: "flex h-12 shrink-0 items-center gap-2 border-b border-border px-4",
183
- children: [
184
- inputAccessory === undefined ? null : /*#__PURE__*/ _jsx("div", {
185
- "data-slot": "command-palette-input-accessory",
186
- className: "shrink-0",
187
- children: inputAccessory
188
- }),
189
- /*#__PURE__*/ _jsx(PaletteInput, {
190
- machine: machine,
191
- inputRef: inputRef,
192
- query: query,
193
- onQueryChange: onQueryChange,
194
- loading: loading,
195
- placeholder: placeholder,
196
- className: "h-full min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
197
- })
198
- ]
199
- }),
200
- /*#__PURE__*/ _jsx(PaletteResults, {
201
- machine: machine,
202
- query: query,
203
- loading: loading,
204
- error: error,
205
- className: "max-h-96 min-h-0 flex-1 overflow-y-auto p-2"
206
- }),
207
- /*#__PURE__*/ _jsx(PaletteLoadMore, {
208
- hasMore: hasMore,
209
- onLoadMore: onLoadMore,
210
- loading: loading
211
- }),
212
- /*#__PURE__*/ _jsx(PaletteFooter, {
213
- footer: footer
214
- })
215
- ]
216
- })
217
- });
88
+ const machine = usePaletteMachine(groups, onSelect, () => onOpenChange(false));
89
+ useEffect(() => {
90
+ if (open)
91
+ inputRef.current?.focus();
92
+ }, [open]);
93
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: cn("max-w-xl gap-0 p-0 [&_[aria-label=Close]]:hidden", className), onKeyDown: machine.onKeyDown, children: [_jsx(DialogTitle, { className: "sr-only", children: "Search" }), _jsxs("div", { className: "flex h-12 shrink-0 items-center gap-2 border-b border-border px-4", children: [inputAccessory === undefined ? null : (_jsx("div", { "data-slot": "command-palette-input-accessory", className: "shrink-0", children: inputAccessory })), _jsx(PaletteInput, { machine: machine, inputRef: inputRef, query: query, onQueryChange: onQueryChange, loading: loading, placeholder: placeholder, className: "h-full min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground" })] }), _jsx(PaletteResults, { machine: machine, query: query, loading: loading, error: error, className: "max-h-96 min-h-0 flex-1 overflow-y-auto p-2" }), _jsx(PaletteLoadMore, { hasMore: hasMore, onLoadMore: onLoadMore, loading: loading }), _jsx(PaletteFooter, { footer: footer })] }) }));
218
94
  }
219
95
  /**
220
96
  * The palette as an inline, non-modal surface: the field sits wherever the
@@ -233,85 +109,30 @@ function PaletteFooter({ footer }) {
233
109
  * the morph itself are the consumer's: this component paints the panel and
234
110
  * owns the keyboard and dismissal contract, and exposes `data-slot`
235
111
  * hooks (`command-palette-inline`, `-field`, `-results`) for the rest.
236
- */ export function CommandPaletteInline({ open, onOpenChange, query, onQueryChange, groups, onSelect, loading = false, error, hasMore = false, onLoadMore, placeholder = "Search or jump to…", inputAccessory, leading, footer, className }) {
112
+ */
113
+ export function CommandPaletteInline({ open, onOpenChange, query, onQueryChange, groups, onSelect, loading = false, error, hasMore = false, onLoadMore, placeholder = "Search or jump to…", inputAccessory, leading, footer, className, }) {
237
114
  const inputRef = useRef(null);
238
115
  const rootRef = useRef(null);
239
- const machine = usePaletteMachine(groups, onSelect, ()=>onOpenChange(false));
240
- useEffect(()=>{
241
- if (open) inputRef.current?.focus();
242
- }, [
243
- open
244
- ]);
116
+ const machine = usePaletteMachine(groups, onSelect, () => onOpenChange(false));
117
+ useEffect(() => {
118
+ if (open)
119
+ inputRef.current?.focus();
120
+ }, [open]);
245
121
  // Without an overlay to catch it, a press anywhere else is the dismissal.
246
122
  // `pointerdown` rather than `click` so the field collapses as the user
247
123
  // reaches for whatever they meant to press, not after it has been pressed.
248
- useEffect(()=>{
249
- if (!open) return;
124
+ useEffect(() => {
125
+ if (!open)
126
+ return;
250
127
  function onPointerDown(event) {
251
- if (rootRef.current?.contains(event.target) === true) return;
128
+ if (rootRef.current?.contains(event.target) === true)
129
+ return;
252
130
  onOpenChange(false);
253
131
  }
254
132
  document.addEventListener("pointerdown", onPointerDown);
255
- return ()=>document.removeEventListener("pointerdown", onPointerDown);
256
- }, [
257
- open,
258
- onOpenChange
259
- ]);
260
- return /*#__PURE__*/ _jsxs("div", {
261
- ref: rootRef,
262
- "data-slot": "command-palette-inline",
263
- "data-open": open,
264
- className: cn("relative", className),
265
- children: [
266
- /*#__PURE__*/ _jsxs("div", {
267
- "data-slot": "command-palette-inline-field",
268
- onKeyDown: machine.onKeyDown,
269
- children: [
270
- leading,
271
- open ? /*#__PURE__*/ _jsxs(_Fragment, {
272
- children: [
273
- inputAccessory === undefined ? null : /*#__PURE__*/ _jsx("div", {
274
- "data-slot": "command-palette-input-accessory",
275
- className: "shrink-0",
276
- children: inputAccessory
277
- }),
278
- /*#__PURE__*/ _jsx(PaletteInput, {
279
- machine: machine,
280
- inputRef: inputRef,
281
- query: query,
282
- onQueryChange: onQueryChange,
283
- loading: loading,
284
- placeholder: placeholder,
285
- className: "min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
286
- })
287
- ]
288
- }) : null
289
- ]
290
- }),
291
- open ? /*#__PURE__*/ _jsxs("div", {
292
- "data-slot": "command-palette-inline-results",
293
- onKeyDown: machine.onKeyDown,
294
- className: "absolute top-full right-0 z-50 mt-1 flex w-96 max-w-[80vw] flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-lg",
295
- children: [
296
- /*#__PURE__*/ _jsx(PaletteResults, {
297
- machine: machine,
298
- query: query,
299
- loading: loading,
300
- error: error,
301
- className: "max-h-96 min-h-0 flex-1 overflow-y-auto p-2"
302
- }),
303
- /*#__PURE__*/ _jsx(PaletteLoadMore, {
304
- hasMore: hasMore,
305
- onLoadMore: onLoadMore,
306
- loading: loading
307
- }),
308
- /*#__PURE__*/ _jsx(PaletteFooter, {
309
- footer: footer
310
- })
311
- ]
312
- }) : null
313
- ]
314
- });
133
+ return () => document.removeEventListener("pointerdown", onPointerDown);
134
+ }, [open, onOpenChange]);
135
+ return (_jsxs("div", { ref: rootRef, "data-slot": "command-palette-inline", "data-open": open, className: cn("relative", className), children: [_jsxs("div", { "data-slot": "command-palette-inline-field", onKeyDown: machine.onKeyDown, children: [leading, open ? (_jsxs(_Fragment, { children: [inputAccessory === undefined ? null : (_jsx("div", { "data-slot": "command-palette-input-accessory", className: "shrink-0", children: inputAccessory })), _jsx(PaletteInput, { machine: machine, inputRef: inputRef, query: query, onQueryChange: onQueryChange, loading: loading, placeholder: placeholder, className: "min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground" })] })) : null] }), open ? (_jsxs("div", { "data-slot": "command-palette-inline-results", onKeyDown: machine.onKeyDown, className: "absolute top-full right-0 z-50 mt-1 flex w-96 max-w-[80vw] flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-lg", children: [_jsx(PaletteResults, { machine: machine, query: query, loading: loading, error: error, className: "max-h-96 min-h-0 flex-1 overflow-y-auto p-2" }), _jsx(PaletteLoadMore, { hasMore: hasMore, onLoadMore: onLoadMore, loading: loading }), _jsx(PaletteFooter, { footer: footer })] })) : null] }));
315
136
  }
316
137
  /**
317
138
  * Binds ⌘K / Ctrl-K to a toggle.
@@ -322,20 +143,24 @@ function PaletteFooter({ footer }) {
322
143
  * browser keep its own ⌘K where the user is typing into a text field —
323
144
  * stealing it there is how a palette becomes the thing that eats your search
324
145
  * box.
325
- */ export function useCommandShortcut(onToggle, enabled = true) {
326
- useEffect(()=>{
327
- if (!enabled) return;
328
- const onKeyDown = (event)=>{
329
- if (event.key.toLowerCase() !== "k" || !(event.metaKey || event.ctrlKey) || event.repeat) return;
146
+ */
147
+ export function useCommandShortcut(onToggle, enabled = true) {
148
+ useEffect(() => {
149
+ if (!enabled)
150
+ return;
151
+ const onKeyDown = (event) => {
152
+ if (event.key.toLowerCase() !== "k" ||
153
+ !(event.metaKey || event.ctrlKey) ||
154
+ event.repeat)
155
+ return;
330
156
  const target = event.target;
331
- if (target?.isContentEditable === true || /^(input|textarea|select)$/i.test(target?.tagName ?? "")) return;
157
+ if (target?.isContentEditable === true ||
158
+ /^(input|textarea|select)$/i.test(target?.tagName ?? ""))
159
+ return;
332
160
  event.preventDefault();
333
161
  onToggle();
334
162
  };
335
163
  document.addEventListener("keydown", onKeyDown);
336
- return ()=>document.removeEventListener("keydown", onKeyDown);
337
- }, [
338
- onToggle,
339
- enabled
340
- ]);
164
+ return () => document.removeEventListener("keydown", onKeyDown);
165
+ }, [onToggle, enabled]);
341
166
  }
@@ -46,7 +46,7 @@ export declare function CompareBody({ comparison, className, }: {
46
46
  readonly className?: string;
47
47
  }): import("react").JSX.Element;
48
48
  /** The JSON-string entry point, for when the payload arrives as artifact content. */
49
- export declare function CompareBodyFromJson({ content, pending }: {
49
+ export declare function CompareBodyFromJson({ content, pending, }: {
50
50
  readonly content: string;
51
51
  readonly pending?: boolean;
52
52
  }): import("react").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Check } from "lucide-react";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { ArtifactNotice } from "./artifact-notice.js";
5
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
5
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "./table.js";
6
6
  /**
7
7
  * Reads a `Comparison` out of a JSON string, or returns `null`.
8
8
  *
@@ -11,17 +11,25 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from ".
11
11
  * showing the raw text rather than rendering half a comparison. It checks the
12
12
  * fields it will actually index and nothing else — over-validating a viewer's
13
13
  * input rejects payloads it could have drawn perfectly well.
14
- */ export function parseComparison(json) {
14
+ */
15
+ export function parseComparison(json) {
15
16
  let raw;
16
17
  try {
17
18
  raw = JSON.parse(json);
18
- } catch {
19
+ }
20
+ catch {
19
21
  return null;
20
22
  }
21
- if (typeof raw !== "object" || raw === null) return null;
23
+ if (typeof raw !== "object" || raw === null)
24
+ return null;
22
25
  const variants = raw.variants;
23
- if (!Array.isArray(variants) || variants.length === 0) return null;
24
- const ok = variants.every((variant)=>typeof variant === "object" && variant !== null && typeof variant.id === "string" && typeof variant.label === "string" && typeof variant.content === "string");
26
+ if (!Array.isArray(variants) || variants.length === 0)
27
+ return null;
28
+ const ok = variants.every((variant) => typeof variant === "object" &&
29
+ variant !== null &&
30
+ typeof variant.id === "string" &&
31
+ typeof variant.label === "string" &&
32
+ typeof variant.content === "string");
25
33
  return ok ? raw : null;
26
34
  }
27
35
  /**
@@ -37,104 +45,26 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from ".
37
45
  * Columns collapse to a stack below `md`. Side-by-side is the whole idea, but a
38
46
  * 320px viewport showing two 140px columns compares nothing; stacked variants
39
47
  * with their labels intact at least remain readable.
40
- */ export function CompareBody({ comparison, className }) {
48
+ */
49
+ export function CompareBody({ comparison, className, }) {
41
50
  const { variants, winnerId, rationale, criteria } = comparison;
42
- return /*#__PURE__*/ _jsxs("div", {
43
- className: cn("flex flex-col gap-6", className),
44
- children: [
45
- rationale === undefined ? null : /*#__PURE__*/ _jsx("p", {
46
- className: "max-w-prose text-sm leading-relaxed text-muted-foreground",
47
- children: rationale
48
- }),
49
- /*#__PURE__*/ _jsx("div", {
50
- className: "grid gap-4 md:grid-cols-2",
51
- children: variants.map((variant)=>{
51
+ return (_jsxs("div", { className: cn("flex flex-col gap-6", className), children: [rationale === undefined ? null : (_jsx("p", { className: "max-w-prose text-sm leading-relaxed text-muted-foreground", children: rationale })), _jsx("div", { className: "grid gap-4 md:grid-cols-2", children: variants.map((variant) => {
52
52
  const won = variant.id === winnerId;
53
- return /*#__PURE__*/ _jsxs("section", {
54
- "aria-label": won ? `${variant.label} — selected` : variant.label,
55
- className: cn("flex flex-col gap-3 rounded-lg border p-4", won ? "border-primary-emphasis bg-card" : "border-border"),
56
- children: [
57
- /*#__PURE__*/ _jsxs("div", {
58
- className: "flex items-center gap-2",
59
- children: [
60
- /*#__PURE__*/ _jsx("h3", {
61
- className: "text-sm font-semibold",
62
- children: variant.label
63
- }),
64
- won ? /*#__PURE__*/ _jsxs("span", {
65
- className: "inline-flex items-center gap-1 rounded-sm bg-muted px-1.5 py-0.5 text-xs font-medium text-primary-emphasis",
66
- children: [
67
- /*#__PURE__*/ _jsx(Check, {
68
- className: "size-3",
69
- "aria-hidden": true
70
- }),
71
- "Selected"
72
- ]
73
- }) : null
74
- ]
75
- }),
76
- /*#__PURE__*/ _jsx("p", {
77
- className: "text-sm leading-relaxed whitespace-pre-wrap",
78
- children: variant.content
79
- })
80
- ]
81
- }, variant.id);
82
- })
83
- }),
84
- criteria === undefined || criteria.length === 0 ? null : /*#__PURE__*/ _jsxs(Table, {
85
- "aria-label": "Scores by criterion",
86
- children: [
87
- /*#__PURE__*/ _jsx(TableHeader, {
88
- children: /*#__PURE__*/ _jsxs(TableRow, {
89
- children: [
90
- /*#__PURE__*/ _jsx(TableHead, {
91
- scope: "col",
92
- children: "Criterion"
93
- }),
94
- variants.map((variant)=>/*#__PURE__*/ _jsx(TableHead, {
95
- scope: "col",
96
- className: "text-right",
97
- children: variant.label
98
- }, variant.id))
99
- ]
100
- })
101
- }),
102
- /*#__PURE__*/ _jsx(TableBody, {
103
- children: criteria.map((criterion)=>/*#__PURE__*/ _jsxs(TableRow, {
104
- children: [
105
- /*#__PURE__*/ _jsx(TableCell, {
106
- className: "font-medium",
107
- children: criterion.label
108
- }),
109
- variants.map((variant)=>{
110
- const score = criterion.scores[variant.id];
111
- return /*#__PURE__*/ _jsx(TableCell, {
112
- className: "text-right font-mono tabular-nums",
113
- children: score === undefined ? "—" : score.toLocaleString()
114
- }, variant.id);
115
- })
116
- ]
117
- }, criterion.label))
118
- })
119
- ]
120
- })
121
- ]
122
- });
53
+ return (_jsxs("section", { "aria-label": won ? `${variant.label} — selected` : variant.label, className: cn("flex flex-col gap-3 rounded-lg border p-4", won ? "border-primary-emphasis bg-card" : "border-border"), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: variant.label }), won ? (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-sm bg-muted px-1.5 py-0.5 text-xs font-medium text-primary-emphasis", children: [_jsx(Check, { className: "size-3", "aria-hidden": true }), "Selected"] })) : null] }), _jsx("p", { className: "text-sm leading-relaxed whitespace-pre-wrap", children: variant.content })] }, variant.id));
54
+ }) }), criteria === undefined || criteria.length === 0 ? null : (_jsxs(Table, { "aria-label": "Scores by criterion", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { scope: "col", children: "Criterion" }), variants.map((variant) => (_jsx(TableHead, { scope: "col", className: "text-right", children: variant.label }, variant.id)))] }) }), _jsx(TableBody, { children: criteria.map((criterion) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: criterion.label }), variants.map((variant) => {
55
+ const score = criterion.scores[variant.id];
56
+ return (_jsx(TableCell, { className: "text-right font-mono tabular-nums", children: score === undefined ? "—" : score.toLocaleString() }, variant.id));
57
+ })] }, criterion.label))) })] }))] }));
123
58
  }
124
- /** The JSON-string entry point, for when the payload arrives as artifact content. */ export function CompareBodyFromJson({ content, pending }) {
125
- if (content.trim() === "") return /*#__PURE__*/ _jsx(ArtifactNotice, {
126
- pending: pending
127
- });
59
+ /** The JSON-string entry point, for when the payload arrives as artifact content. */
60
+ export function CompareBodyFromJson({ content, pending, }) {
61
+ if (content.trim() === "")
62
+ return _jsx(ArtifactNotice, { pending: pending });
128
63
  const comparison = parseComparison(content);
129
64
  if (comparison === null) {
130
65
  // The payload is not a comparison. Showing it verbatim beats an error: the
131
66
  // content is still the artifact, and the user can read it.
132
- return /*#__PURE__*/ _jsx("pre", {
133
- className: "overflow-auto rounded-md border border-border bg-muted p-3 font-mono text-xs",
134
- children: content
135
- });
67
+ return (_jsx("pre", { className: "overflow-auto rounded-md border border-border bg-muted p-3 font-mono text-xs", children: content }));
136
68
  }
137
- return /*#__PURE__*/ _jsx(CompareBody, {
138
- comparison: comparison
139
- });
69
+ return _jsx(CompareBody, { comparison: comparison });
140
70
  }
@@ -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,30 +13,22 @@ import { Button } from "./button.js";
12
13
  * It disarms on blur as well as on a timer. Leaving a primed destructive button
13
14
  * behind when the user's attention has moved elsewhere is how the second click
14
15
  * lands by accident, which is the exact thing this exists to prevent.
15
- */ export function ConfirmButton({ onConfirm, children, confirmLabel = "Click again to confirm", resetMs = 3000, defaultArmed = false, variant = "outline", ...props }) {
16
+ */
17
+ export function ConfirmButton({ onConfirm, children, confirmLabel = "Click again to confirm", resetMs = 3000, defaultArmed = false, variant = "outline", ...props }) {
16
18
  const [armed, setArmed] = useState(defaultArmed);
17
19
  const timerRef = useRef(undefined);
18
- const disarm = ()=>{
20
+ const disarm = () => {
19
21
  window.clearTimeout(timerRef.current);
20
22
  setArmed(false);
21
23
  };
22
- useEffect(()=>()=>window.clearTimeout(timerRef.current), []);
23
- return /*#__PURE__*/ _jsx(Button, {
24
- ...props,
25
- variant: armed ? "destructive" : variant,
26
- onBlur: disarm,
27
- onClick: ()=>{
24
+ useEffect(() => () => window.clearTimeout(timerRef.current), []);
25
+ return (_jsx(Button, { ...props, variant: armed ? "destructive" : variant, onBlur: disarm, onClick: () => {
28
26
  if (!armed) {
29
27
  setArmed(true);
30
- timerRef.current = window.setTimeout(()=>setArmed(false), resetMs);
28
+ timerRef.current = window.setTimeout(() => setArmed(false), resetMs);
31
29
  return;
32
30
  }
33
31
  disarm();
34
32
  onConfirm();
35
- },
36
- children: /*#__PURE__*/ _jsx("span", {
37
- "aria-live": "polite",
38
- children: armed ? confirmLabel : children
39
- })
40
- });
33
+ }, children: _jsx("span", { "aria-live": "polite", children: armed ? confirmLabel : children }) }));
41
34
  }