@columenlabs/lumen-ui 0.0.0-stage → 0.2.1

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 (301) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +12 -0
  3. package/README.md +4 -2
  4. package/dist/add-column.d.ts +23 -0
  5. package/dist/add-column.js +62 -0
  6. package/dist/api-fetch.d.ts +9 -0
  7. package/dist/api-fetch.js +60 -0
  8. package/dist/app-switcher.d.ts +21 -0
  9. package/dist/app-switcher.js +12 -0
  10. package/dist/app-views.d.ts +18 -0
  11. package/dist/app-views.js +41 -0
  12. package/dist/assistant-context.d.ts +72 -0
  13. package/dist/assistant-context.js +30 -0
  14. package/dist/assistant-panel.d.ts +58 -0
  15. package/dist/assistant-panel.js +244 -0
  16. package/dist/assistant-screen.d.ts +124 -0
  17. package/dist/assistant-screen.js +252 -0
  18. package/dist/card-switch.d.ts +16 -0
  19. package/dist/card-switch.js +16 -0
  20. package/dist/color-dot.d.ts +14 -0
  21. package/dist/color-dot.js +19 -0
  22. package/dist/confirm.d.ts +18 -0
  23. package/dist/confirm.js +5 -0
  24. package/dist/connections.d.ts +45 -0
  25. package/dist/connections.js +275 -0
  26. package/dist/coverage-calendar-map.d.ts +27 -0
  27. package/dist/coverage-calendar-map.js +84 -0
  28. package/dist/coverage-cells.d.ts +55 -0
  29. package/dist/coverage-cells.js +85 -0
  30. package/dist/coverage-map.d.ts +112 -0
  31. package/dist/coverage-map.js +428 -0
  32. package/dist/details-table.d.ts +20 -0
  33. package/dist/details-table.js +13 -0
  34. package/dist/drawer-section.d.ts +27 -0
  35. package/dist/drawer-section.js +23 -0
  36. package/dist/floating-head.d.ts +2 -0
  37. package/dist/floating-head.js +57 -0
  38. package/dist/fold.d.ts +16 -0
  39. package/dist/fold.js +70 -0
  40. package/dist/form-issues.d.ts +32 -0
  41. package/dist/form-issues.js +60 -0
  42. package/dist/formula-input.d.ts +17 -0
  43. package/dist/formula-input.js +104 -0
  44. package/dist/frame-script.d.ts +7 -0
  45. package/dist/frame-script.js +103 -0
  46. package/dist/freshness.d.ts +24 -0
  47. package/dist/freshness.js +99 -0
  48. package/dist/group-button.d.ts +33 -0
  49. package/dist/group-button.js +67 -0
  50. package/dist/growth-chart.d.ts +77 -0
  51. package/dist/growth-chart.js +195 -0
  52. package/dist/hooks/use-keyboard-fit.d.ts +15 -0
  53. package/dist/hooks/use-keyboard-fit.js +29 -0
  54. package/dist/hooks/use-mobile.d.ts +1 -0
  55. package/dist/hooks/use-mobile.js +15 -0
  56. package/dist/icon-button.d.ts +21 -0
  57. package/dist/icon-button.js +22 -0
  58. package/dist/icon-picker.d.ts +14 -0
  59. package/dist/icon-picker.js +36 -0
  60. package/dist/index.d.ts +83 -0
  61. package/dist/index.js +90 -0
  62. package/dist/integrations.d.ts +57 -0
  63. package/dist/integrations.js +73 -0
  64. package/dist/joins.d.ts +128 -0
  65. package/dist/joins.js +767 -0
  66. package/dist/kit-config.d.ts +13 -0
  67. package/dist/kit-config.js +15 -0
  68. package/dist/lib/utils.d.ts +2 -0
  69. package/dist/lib/utils.js +5 -0
  70. package/dist/lumen-boundary.d.ts +7 -0
  71. package/dist/lumen-boundary.js +10 -0
  72. package/dist/lumen-code-editor-impl.d.ts +2 -0
  73. package/dist/lumen-code-editor-impl.js +16 -0
  74. package/dist/lumen-code-editor.d.ts +11 -0
  75. package/dist/lumen-code-editor.js +11 -0
  76. package/dist/lumen-code.d.ts +22 -0
  77. package/dist/lumen-code.js +75 -0
  78. package/dist/lumen-drawer.d.ts +30 -0
  79. package/dist/lumen-drawer.js +27 -0
  80. package/dist/lumen-echart.d.ts +58 -0
  81. package/dist/lumen-echart.js +129 -0
  82. package/dist/lumen-filter.d.ts +75 -0
  83. package/dist/lumen-filter.js +546 -0
  84. package/dist/lumen-finder.d.ts +62 -0
  85. package/dist/lumen-finder.js +275 -0
  86. package/dist/lumen-form.d.ts +34 -0
  87. package/dist/lumen-form.js +81 -0
  88. package/dist/lumen-geo-map.d.ts +9 -0
  89. package/dist/lumen-geo-map.js +135 -0
  90. package/dist/lumen-grid.d.ts +49 -0
  91. package/dist/lumen-grid.js +203 -0
  92. package/dist/lumen-layout.d.ts +150 -0
  93. package/dist/lumen-layout.js +661 -0
  94. package/dist/lumen-links.d.ts +96 -0
  95. package/dist/lumen-links.js +213 -0
  96. package/dist/lumen-page-state.d.ts +20 -0
  97. package/dist/lumen-page-state.js +15 -0
  98. package/dist/lumen-selector.d.ts +139 -0
  99. package/dist/lumen-selector.js +308 -0
  100. package/dist/lumen-source.d.ts +137 -0
  101. package/dist/lumen-source.js +327 -0
  102. package/dist/lumen-tab-drawer.d.ts +27 -0
  103. package/dist/lumen-tab-drawer.js +26 -0
  104. package/dist/lumen-tabs.d.ts +21 -0
  105. package/dist/lumen-tabs.js +6 -0
  106. package/dist/lumen-toast.d.ts +11 -0
  107. package/dist/lumen-toast.js +16 -0
  108. package/dist/lumen-trend.d.ts +18 -0
  109. package/dist/lumen-trend.js +157 -0
  110. package/dist/lumen-view-card.d.ts +35 -0
  111. package/dist/lumen-view-card.js +50 -0
  112. package/dist/lumen-view.d.ts +172 -0
  113. package/dist/lumen-view.js +645 -0
  114. package/dist/lumen-widget-charts.d.ts +57 -0
  115. package/dist/lumen-widget-charts.js +69 -0
  116. package/dist/lumen-widget.d.ts +720 -0
  117. package/dist/lumen-widget.js +609 -0
  118. package/dist/market/compare-chart.d.ts +34 -0
  119. package/dist/market/compare-chart.js +292 -0
  120. package/dist/market/indicator-picker.d.ts +29 -0
  121. package/dist/market/indicator-picker.js +125 -0
  122. package/dist/market/indicators.d.ts +63 -0
  123. package/dist/market/indicators.js +314 -0
  124. package/dist/market/live.d.ts +120 -0
  125. package/dist/market/live.js +444 -0
  126. package/dist/market/lumen-chart.d.ts +88 -0
  127. package/dist/market/lumen-chart.js +645 -0
  128. package/dist/market/market-widget.d.ts +45 -0
  129. package/dist/market/market-widget.js +346 -0
  130. package/dist/market/option-chain.d.ts +19 -0
  131. package/dist/market/option-chain.js +53 -0
  132. package/dist/market/price-chart.d.ts +128 -0
  133. package/dist/market/price-chart.js +619 -0
  134. package/dist/mcp-settings.d.ts +20 -0
  135. package/dist/mcp-settings.js +65 -0
  136. package/dist/nav.d.ts +77 -0
  137. package/dist/nav.js +73 -0
  138. package/dist/note-line.d.ts +13 -0
  139. package/dist/note-line.js +5 -0
  140. package/dist/notifications.d.ts +9 -0
  141. package/dist/notifications.js +166 -0
  142. package/dist/origin-legend.d.ts +14 -0
  143. package/dist/origin-legend.js +38 -0
  144. package/dist/overview.d.ts +84 -0
  145. package/dist/overview.js +105 -0
  146. package/dist/page-inputs.d.ts +14 -0
  147. package/dist/page-inputs.js +16 -0
  148. package/dist/page.d.ts +32 -0
  149. package/dist/page.js +45 -0
  150. package/dist/pager.d.ts +10 -0
  151. package/dist/pager.js +14 -0
  152. package/dist/prefs.d.ts +71 -0
  153. package/dist/prefs.js +126 -0
  154. package/dist/query-client.d.ts +7 -0
  155. package/dist/query-client.js +19 -0
  156. package/dist/refresh.d.ts +4 -0
  157. package/dist/refresh.js +19 -0
  158. package/dist/rename-field.d.ts +11 -0
  159. package/dist/rename-field.js +26 -0
  160. package/dist/reset-button.d.ts +9 -0
  161. package/dist/reset-button.js +10 -0
  162. package/dist/route-error.d.ts +42 -0
  163. package/dist/route-error.js +229 -0
  164. package/dist/schedules.d.ts +88 -0
  165. package/dist/schedules.js +221 -0
  166. package/dist/settings.d.ts +51 -0
  167. package/dist/settings.js +181 -0
  168. package/dist/shell.d.ts +99 -0
  169. package/dist/shell.js +137 -0
  170. package/dist/source-health.d.ts +12 -0
  171. package/dist/source-health.js +58 -0
  172. package/dist/sourced-card.d.ts +10 -0
  173. package/dist/sourced-card.js +69 -0
  174. package/dist/spotlight.d.ts +23 -0
  175. package/dist/spotlight.js +138 -0
  176. package/dist/status-health.d.ts +16 -0
  177. package/dist/status-health.js +41 -0
  178. package/dist/status.d.ts +9 -0
  179. package/dist/status.js +12 -0
  180. package/dist/summary-cards.d.ts +56 -0
  181. package/dist/summary-cards.js +270 -0
  182. package/dist/symbol-sheet-context.d.ts +4 -0
  183. package/dist/symbol-sheet-context.js +5 -0
  184. package/dist/symbol-sheet.d.ts +9 -0
  185. package/dist/symbol-sheet.js +24 -0
  186. package/dist/tasks.d.ts +29 -0
  187. package/dist/tasks.js +278 -0
  188. package/dist/theme.css +606 -0
  189. package/dist/tile.d.ts +37 -0
  190. package/dist/tile.js +23 -0
  191. package/dist/ui/alert-dialog.d.ts +18 -0
  192. package/dist/ui/alert-dialog.js +41 -0
  193. package/dist/ui/alert.d.ts +10 -0
  194. package/dist/ui/alert.js +27 -0
  195. package/dist/ui/avatar.d.ts +11 -0
  196. package/dist/ui/avatar.js +22 -0
  197. package/dist/ui/badge.d.ts +9 -0
  198. package/dist/ui/badge.js +27 -0
  199. package/dist/ui/breadcrumb.d.ts +11 -0
  200. package/dist/ui/breadcrumb.js +27 -0
  201. package/dist/ui/button-group.d.ts +11 -0
  202. package/dist/ui/button-group.js +27 -0
  203. package/dist/ui/button.d.ts +10 -0
  204. package/dist/ui/button.js +38 -0
  205. package/dist/ui/card.d.ts +11 -0
  206. package/dist/ui/card.js +24 -0
  207. package/dist/ui/chart.d.ts +44 -0
  208. package/dist/ui/chart.js +117 -0
  209. package/dist/ui/checkbox.d.ts +4 -0
  210. package/dist/ui/checkbox.js +8 -0
  211. package/dist/ui/collapsible.d.ts +5 -0
  212. package/dist/ui/collapsible.js +12 -0
  213. package/dist/ui/combobox.d.ts +24 -0
  214. package/dist/ui/combobox.js +60 -0
  215. package/dist/ui/command.d.ts +18 -0
  216. package/dist/ui/command.js +34 -0
  217. package/dist/ui/dialog.d.ts +23 -0
  218. package/dist/ui/dialog.js +50 -0
  219. package/dist/ui/dropdown-menu.d.ts +29 -0
  220. package/dist/ui/dropdown-menu.js +50 -0
  221. package/dist/ui/emoji-picker.d.ts +6 -0
  222. package/dist/ui/emoji-picker.js +28 -0
  223. package/dist/ui/empty.d.ts +11 -0
  224. package/dist/ui/empty.js +33 -0
  225. package/dist/ui/field.d.ts +24 -0
  226. package/dist/ui/field.js +65 -0
  227. package/dist/ui/hover-card.d.ts +6 -0
  228. package/dist/ui/hover-card.js +13 -0
  229. package/dist/ui/input-group.d.ts +16 -0
  230. package/dist/ui/input-group.js +56 -0
  231. package/dist/ui/input.d.ts +3 -0
  232. package/dist/ui/input.js +6 -0
  233. package/dist/ui/item.d.ts +23 -0
  234. package/dist/ui/item.js +67 -0
  235. package/dist/ui/kbd.d.ts +3 -0
  236. package/dist/ui/kbd.js +9 -0
  237. package/dist/ui/label.d.ts +4 -0
  238. package/dist/ui/label.js +7 -0
  239. package/dist/ui/popover.d.ts +10 -0
  240. package/dist/ui/popover.js +25 -0
  241. package/dist/ui/progress.d.ts +4 -0
  242. package/dist/ui/progress.js +9 -0
  243. package/dist/ui/resizable.d.ts +7 -0
  244. package/dist/ui/resizable.js +13 -0
  245. package/dist/ui/scroll-area.d.ts +5 -0
  246. package/dist/ui/scroll-area.js +10 -0
  247. package/dist/ui/select.d.ts +15 -0
  248. package/dist/ui/select.js +36 -0
  249. package/dist/ui/separator.d.ts +4 -0
  250. package/dist/ui/separator.js +7 -0
  251. package/dist/ui/sheet.d.ts +14 -0
  252. package/dist/ui/sheet.js +54 -0
  253. package/dist/ui/sidebar.d.ts +69 -0
  254. package/dist/ui/sidebar.js +206 -0
  255. package/dist/ui/skeleton.d.ts +2 -0
  256. package/dist/ui/skeleton.js +6 -0
  257. package/dist/ui/slider.d.ts +4 -0
  258. package/dist/ui/slider.js +9 -0
  259. package/dist/ui/sonner.d.ts +3 -0
  260. package/dist/ui/sonner.js +24 -0
  261. package/dist/ui/spinner.d.ts +2 -0
  262. package/dist/ui/spinner.js +7 -0
  263. package/dist/ui/switch.d.ts +6 -0
  264. package/dist/ui/switch.js +7 -0
  265. package/dist/ui/table.d.ts +10 -0
  266. package/dist/ui/table.js +31 -0
  267. package/dist/ui/tabs.d.ts +7 -0
  268. package/dist/ui/tabs.js +22 -0
  269. package/dist/ui/textarea.d.ts +3 -0
  270. package/dist/ui/textarea.js +6 -0
  271. package/dist/ui/toggle-group.d.ts +10 -0
  272. package/dist/ui/toggle-group.js +22 -0
  273. package/dist/ui/toggle.d.ts +9 -0
  274. package/dist/ui/toggle.js +25 -0
  275. package/dist/ui/tooltip.d.ts +7 -0
  276. package/dist/ui/tooltip.js +16 -0
  277. package/dist/view-edit-cell.d.ts +49 -0
  278. package/dist/view-edit-cell.js +198 -0
  279. package/dist/view-editor.d.ts +52 -0
  280. package/dist/view-editor.js +1081 -0
  281. package/dist/view-formulas.d.ts +61 -0
  282. package/dist/view-formulas.js +247 -0
  283. package/dist/view-icons.d.ts +20 -0
  284. package/dist/view-icons.js +70 -0
  285. package/dist/view-model.d.ts +390 -0
  286. package/dist/view-model.js +316 -0
  287. package/dist/whats-new.d.ts +3 -0
  288. package/dist/whats-new.js +53 -0
  289. package/dist/widget-controls.d.ts +9 -0
  290. package/dist/widget-controls.js +26 -0
  291. package/dist/widget-data.d.ts +4 -0
  292. package/dist/widget-data.js +32 -0
  293. package/dist/widget-settings.d.ts +516 -0
  294. package/dist/widget-settings.js +290 -0
  295. package/dist/widget-thumbs.d.ts +3 -0
  296. package/dist/widget-thumbs.js +29 -0
  297. package/dist/workspace-keys.d.ts +12 -0
  298. package/dist/workspace-keys.js +13 -0
  299. package/dist/workspaces.d.ts +57 -0
  300. package/dist/workspaces.js +473 -0
  301. package/package.json +82 -4
@@ -0,0 +1,244 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // LumenAssistant's frame (was the assistant panel's): what any app's AI assistant sits
3
+ // in, with nothing of the assistant itself. Docked right of the page on desktop (drag its left edge
4
+ // for ASSISTANT_WIDTH, never past half the window), a full-height sheet on phone; the header's ✦
5
+ // button with an unread dot; ⌘J; and the chat history menu grouped by when each chat was last used.
6
+ // Open or closed and the width are each browser tab's own (sessionStorage). The
7
+ // product fills the body (the product's assistant: its chat, composer, cards and spend).
8
+ import { HistoryIcon, SparklesIcon } from 'lucide-react';
9
+ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
10
+ import { useIsMobile } from './hooks/use-mobile.js';
11
+ import { cn } from './lib/utils.js';
12
+ import { LumenBoundary } from './lumen-boundary.js';
13
+ import { LumenSelector } from './lumen-selector.js';
14
+ import { ASSISTANT_WIDTH, usePrefs } from './prefs.js';
15
+ import { Button } from './ui/button.js';
16
+ import { Kbd } from './ui/kbd.js';
17
+ import { Sheet, SheetContent, SheetDescription, SheetTitle } from './ui/sheet.js';
18
+ import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip.js';
19
+ /** A value kept for this browser tab (sessionStorage), read with `use` and set with `write`. */
20
+ export function tabState(key, fallback, parse) {
21
+ const listeners = new Set();
22
+ const read = () => {
23
+ try {
24
+ const raw = sessionStorage.getItem(key);
25
+ return raw == null ? fallback : (parse(raw) ?? fallback);
26
+ }
27
+ catch {
28
+ return fallback;
29
+ }
30
+ };
31
+ let state = typeof sessionStorage === 'undefined' ? fallback : read();
32
+ const write = (next) => {
33
+ state = next;
34
+ try {
35
+ sessionStorage.setItem(key, String(next));
36
+ }
37
+ catch { }
38
+ for (const l of listeners)
39
+ l();
40
+ };
41
+ const use = () => useSyncExternalStore((l) => {
42
+ listeners.add(l);
43
+ return () => listeners.delete(l);
44
+ }, () => state, () => fallback);
45
+ return { write, use, get: () => state };
46
+ }
47
+ /** Whether the assistant is open in this tab. */
48
+ export const assistantOpen = tabState('assistant_open', false, (raw) => raw === '1');
49
+ /** Dragged width in px, within ASSISTANT_WIDTH; 0 is the shell's default. */
50
+ const assistantWidth = tabState('assistant_width', 0, (raw) => {
51
+ const n = Number(raw);
52
+ return Number.isFinite(n) && n >= ASSISTANT_WIDTH.min
53
+ ? Math.min(Math.round(n), ASSISTANT_WIDTH.max)
54
+ : 0;
55
+ });
56
+ /** ⌘J (Ctrl+J) calls `toggle` anywhere in the app. */
57
+ export function useAssistantHotkey(toggle) {
58
+ const ref = useRef(toggle);
59
+ ref.current = toggle;
60
+ useEffect(() => {
61
+ const onKey = (e) => {
62
+ if (e.key.toLowerCase() === 'j' && (e.metaKey || e.ctrlKey)) {
63
+ e.preventDefault();
64
+ ref.current();
65
+ }
66
+ };
67
+ window.addEventListener('keydown', onKey);
68
+ return () => window.removeEventListener('keydown', onKey);
69
+ }, []);
70
+ }
71
+ /** The page header's ✦ button: shown while the assistant is closed, with a dot when a reply came. */
72
+ export function LumenAssistantButton({ name, open, unread, onOpen, className, }) {
73
+ if (open)
74
+ return null;
75
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "icon-sm", className: cn('relative shrink-0', className), "aria-label": name, onClick: onOpen, children: [_jsx(SparklesIcon, { className: "text-edge" }), unread && (_jsx("span", { "aria-hidden": true, "data-slot": "assistant-unread", className: "absolute -top-0.5 -right-0.5 size-2 rounded-full bg-primary" }))] }) }), _jsxs(TooltipContent, { children: [name, " ", _jsx(Kbd, { children: "\u2318J" })] })] }));
76
+ }
77
+ /**
78
+ * Docked right of the page on desktop (nothing while closed); a full-height sheet on phone, with
79
+ * `closedBar` under the page while it is closed. `children` draws the body; it is told which.
80
+ */
81
+ export function LumenAssistantPanel({ name, open, onOpenChange, closedBar, children, }) {
82
+ const phone = useIsMobile();
83
+ if (phone)
84
+ return (_jsxs(_Fragment, { children: [!open && closedBar, _jsx(Sheet, { open: open, onOpenChange: onOpenChange, children: _jsxs(SheetContent, { side: "right", showCloseButton: false, className: "gap-0 p-0 data-[side=right]:w-full data-[side=right]:sm:max-w-none", children: [_jsx(SheetTitle, { className: "sr-only", children: name }), _jsxs(SheetDescription, { className: "sr-only", children: ["Chat with ", name, " about your data."] }), _jsx(LumenBoundary, { where: "assistant", what: "panel", children: children(true) })] }) })] }));
85
+ if (!open)
86
+ return null;
87
+ return _jsx(Docked, { name: name, children: children(false) });
88
+ }
89
+ function Docked({ name, children }) {
90
+ const saved = assistantWidth.use();
91
+ const [drag, setDrag] = useState(null);
92
+ const width = drag ?? saved;
93
+ const ref = useRef(null);
94
+ // Side sheets open left of the assistant, never over it: they read its width
95
+ // from `--assistant-docked` (ui/sheet.tsx), which is 0 while it is closed or on phone.
96
+ useEffect(() => {
97
+ const el = ref.current;
98
+ if (!el)
99
+ return;
100
+ const root = document.documentElement;
101
+ const set = () => root.style.setProperty('--assistant-docked', `${el.offsetWidth}px`);
102
+ set();
103
+ const watch = new ResizeObserver(set);
104
+ watch.observe(el);
105
+ return () => {
106
+ watch.disconnect();
107
+ root.style.removeProperty('--assistant-docked');
108
+ };
109
+ }, []);
110
+ return (_jsxs("aside", { ref: ref, "aria-label": name, "data-assistant-docked": "",
111
+ // Its width is the dragged one (or the shell's default), within ASSISTANT_WIDTH and half the window.
112
+ style: width ? { width } : undefined, className: "pointer-events-auto sticky top-0 h-svh w-(--shell-assistant-width) max-w-[50vw] min-w-80 shrink-0 border-l bg-background", children: [_jsx(ResizeEdge, { name: name, width: width || 416, onWidth: (w) => {
113
+ setDrag(null);
114
+ assistantWidth.write(w);
115
+ }, onDrag: setDrag }), _jsx(LumenBoundary, { where: "assistant", what: "panel", children: children })] }));
116
+ }
117
+ /**
118
+ * The panel's left edge: drag it, or focus it and use the arrow keys, to set the width; a double
119
+ * click goes back to the default. While dragging only `onDrag` hears it.
120
+ */
121
+ function ResizeEdge({ name, width, onDrag, onWidth, }) {
122
+ const clamp = (w) => Math.round(Math.max(ASSISTANT_WIDTH.min, Math.min(w, ASSISTANT_WIDTH.max, Math.floor(window.innerWidth / 2))));
123
+ // The panel's width on screen (the default one is CSS's).
124
+ const now = (el) => Math.round(el.parentElement?.getBoundingClientRect().width ?? width);
125
+ const [dragging, setDragging] = useState(false);
126
+ useEffect(() => {
127
+ if (!dragging)
128
+ return;
129
+ document.body.style.cursor = 'col-resize';
130
+ document.body.style.userSelect = 'none';
131
+ return () => {
132
+ document.body.style.cursor = '';
133
+ document.body.style.userSelect = '';
134
+ };
135
+ }, [dragging]);
136
+ return (_jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-label": `Resize ${name}`, "aria-valuemin": ASSISTANT_WIDTH.min, "aria-valuemax": ASSISTANT_WIDTH.max, "aria-valuenow": width, tabIndex: 0, "data-slot": "assistant-resize", "data-dragging": dragging ? '' : undefined, className: "absolute inset-y-0 -left-1 z-20 w-2 cursor-col-resize outline-none after:absolute after:inset-y-0 after:left-1/2 after:w-0.5 after:-translate-x-1/2 after:transition-colors hover:after:bg-ring focus-visible:after:bg-ring data-dragging:after:bg-ring", onPointerDown: (e) => {
137
+ if (e.button !== 0)
138
+ return;
139
+ e.preventDefault();
140
+ const el = e.currentTarget;
141
+ el.setPointerCapture(e.pointerId);
142
+ const x0 = e.clientX;
143
+ const w0 = now(el);
144
+ let last = w0;
145
+ setDragging(true);
146
+ const move = (m) => {
147
+ last = clamp(w0 + x0 - m.clientX);
148
+ onDrag(last);
149
+ };
150
+ const up = () => {
151
+ el.removeEventListener('pointermove', move);
152
+ el.removeEventListener('pointerup', up);
153
+ el.removeEventListener('pointercancel', up);
154
+ setDragging(false);
155
+ onWidth(last);
156
+ };
157
+ el.addEventListener('pointermove', move);
158
+ el.addEventListener('pointerup', up);
159
+ el.addEventListener('pointercancel', up);
160
+ }, onDoubleClick: () => onWidth(0), onKeyDown: (e) => {
161
+ const step = e.shiftKey ? 64 : 16;
162
+ if (e.key === 'ArrowLeft')
163
+ onWidth(clamp(now(e.currentTarget) + step));
164
+ else if (e.key === 'ArrowRight')
165
+ onWidth(clamp(now(e.currentTarget) - step));
166
+ else
167
+ return;
168
+ e.preventDefault();
169
+ } }));
170
+ }
171
+ /** The history groups, newest first. */
172
+ const GROUPS = ['Today', 'Yesterday', 'This week', 'Last week', 'Last month'];
173
+ /** Under this age a chat's time reads relative ("12m ago"), past it the clock, day or date. */
174
+ const RELATIVE_MS = 6 * 3600_000;
175
+ const DAY_MS = 86_400_000;
176
+ /** `2026-10-14` in the person's time zone. */
177
+ const dayOf = (at, timeZone) => new Intl.DateTimeFormat('en-CA', {
178
+ timeZone,
179
+ year: 'numeric',
180
+ month: '2-digit',
181
+ day: '2-digit',
182
+ }).format(at);
183
+ /** Which group a chat last used at `at` falls in, by calendar days in `timeZone`; weeks start Monday. */
184
+ function groupOf(at, now, timeZone) {
185
+ const days = Math.round((Date.parse(dayOf(now, timeZone)) - Date.parse(dayOf(at, timeZone))) / DAY_MS);
186
+ const weekday = new Date(dayOf(now, timeZone)).getUTCDay(); // 0 Sunday
187
+ const sinceMonday = (weekday + 6) % 7;
188
+ if (days <= 0)
189
+ return 'Today';
190
+ if (days === 1)
191
+ return 'Yesterday';
192
+ if (days <= sinceMonday)
193
+ return 'This week';
194
+ if (days <= sinceMonday + 7)
195
+ return 'Last week';
196
+ return 'Last month';
197
+ }
198
+ /** `now`, `12m ago`, `3h ago`, then `06:15` (today, yesterday), `Thu` (the weeks) or `28 Sep`. */
199
+ function whenOf(at, now, group, timeZone) {
200
+ const ago = now.getTime() - at.getTime();
201
+ if (ago < 60_000)
202
+ return 'now';
203
+ if (ago < 3600_000)
204
+ return `${Math.floor(ago / 60_000)}m ago`;
205
+ if (ago < RELATIVE_MS)
206
+ return `${Math.floor(ago / 3600_000)}h ago`;
207
+ if (group === 'Today' || group === 'Yesterday')
208
+ return new Intl.DateTimeFormat('en-GB', {
209
+ timeZone,
210
+ hour: '2-digit',
211
+ minute: '2-digit',
212
+ }).format(at);
213
+ if (group === 'This week' || group === 'Last week')
214
+ return new Intl.DateTimeFormat('en-US', { timeZone, weekday: 'short' }).format(at);
215
+ return new Intl.DateTimeFormat('en-GB', { timeZone, day: 'numeric', month: 'short' }).format(at);
216
+ }
217
+ /**
218
+ * Chats, a dropdown from the panel header: chats grouped by when they were
219
+ * last used, searched by title. A row is read ahead when highlighted, so picking it opens at once.
220
+ */
221
+ export function LumenAssistantChats({ chats, value, note, disabled, onOpen, onRefresh, onHighlight, }) {
222
+ const { timeZone } = usePrefs();
223
+ const now = new Date();
224
+ const byId = new Map((chats ?? []).map((c) => [c.id, c]));
225
+ const groups = GROUPS.map((group) => ({
226
+ label: group,
227
+ options: (chats ?? []).flatMap((c) => {
228
+ const at = new Date(c.updated_at);
229
+ const g = groupOf(at, now, timeZone);
230
+ return g === group
231
+ ? [{ value: c.id, label: c.title, detail: whenOf(at, now, g, timeZone) }]
232
+ : [];
233
+ }),
234
+ })).filter((g) => g.options.length > 0);
235
+ return (_jsx(LumenSelector, { trigger: "icon", icon: HistoryIcon, label: "Chats", search: true, align: "end", groups: groups, empty: chats === null ? 'Loading…' : 'No chats yet', header: note && _jsx("p", { className: "px-2 pt-1 text-xs text-muted-foreground", children: note }), value: value, disabled: disabled, onOpenChange: (o) => o && onRefresh?.(), onHighlight: (id) => {
236
+ const c = byId.get(id);
237
+ if (c)
238
+ onHighlight?.(c);
239
+ }, onChange: (id) => {
240
+ const c = byId.get(id);
241
+ if (c)
242
+ onOpen(c);
243
+ } }));
244
+ }
@@ -0,0 +1,124 @@
1
+ import type { IndicatorSpec } from '@columenlabs/lumen-ui-types/chart-formula';
2
+ import { type ReactNode } from 'react';
3
+ import { type CardOnScreen } from './spotlight.js';
4
+ /** What one part of the screen does for the assistant; every handler is optional. */
5
+ export interface AssistantMoveHandlers {
6
+ /** Opens a row's drawer by its title; returns why not when no row matches. */
7
+ open?: (title: string) => string | null;
8
+ /** Opens a symbol's chart, or several to compare, with the indicators the assistant asked for. */
9
+ chart?: (symbols: string | string[], indicators?: IndicatorSpec[]) => void;
10
+ /** Opens Settings at a section. */
11
+ settings?: (section: string) => void;
12
+ /** The Settings sections, for the page map. */
13
+ sections?: {
14
+ id: string;
15
+ title: string;
16
+ }[];
17
+ /** Rows that `open` can find, by title (the first few), for the page note. */
18
+ rows?: () => string[];
19
+ /**
20
+ * What the part shows now, in a line, told to the assistant after a call that ends on it (a view: rows
21
+ * shown, columns empty everywhere, each source's state), so it reports what is there, not what
22
+ * it asked for.
23
+ */
24
+ read?: () => string;
25
+ /** True while the person is editing here: moves wait for their tap. */
26
+ editing?: () => boolean;
27
+ snapshot?: () => unknown;
28
+ restore?: (snap: unknown) => void;
29
+ }
30
+ /** One move, as the server checked it. */
31
+ export type AssistantMove = {
32
+ go: string;
33
+ } | {
34
+ settings: string;
35
+ row?: string;
36
+ } | {
37
+ chart: string | string[];
38
+ indicators?: IndicatorSpec[];
39
+ } | {
40
+ open: string;
41
+ } | {
42
+ point: string;
43
+ };
44
+ export type ScreenStatus = 'done' | 'failed' | 'waiting' | 'undone';
45
+ export interface ScreenOutcome {
46
+ status: ScreenStatus;
47
+ text?: string;
48
+ }
49
+ /**
50
+ * What a sidebar page stands for (a symbol, a saved view, a basket): a nav section says it for its
51
+ * items (`pageType`), the @ picker groups by it and LumenFinder looks up its group in `pageTypes`.
52
+ */
53
+ export type AssistantPageType = string;
54
+ /** One page the nav names: its sidebar section and what it stands for. */
55
+ export interface AssistantPage {
56
+ path: string;
57
+ title: string;
58
+ section?: string;
59
+ type?: AssistantPageType;
60
+ /** The item it sits under (a screener's symbol). */
61
+ parent?: string;
62
+ }
63
+ /**
64
+ * The workspace on screen, by id:
65
+ * The assistant addresses workspaces and tabs by these ids, so a rename never loses them.
66
+ */
67
+ export interface WorkspaceOnScreen {
68
+ id: string;
69
+ name: string;
70
+ tabs: {
71
+ id: string;
72
+ name: string;
73
+ source: string;
74
+ }[];
75
+ /** The tab open now. */
76
+ open: string | null;
77
+ }
78
+ /** What the browser can do now, sent with each message while Screen is on. */
79
+ export interface ScreenMap {
80
+ routes: string[];
81
+ pages: AssistantPage[];
82
+ settings: {
83
+ id: string;
84
+ title: string;
85
+ }[];
86
+ cards: CardOnScreen[];
87
+ workspace?: WorkspaceOnScreen;
88
+ }
89
+ interface ScreenApi {
90
+ register: (slot: string, h: AssistantMoveHandlers | null) => void;
91
+ pages: (key: string, items: AssistantPage[] | null) => void;
92
+ /** The workspace page says which workspace and tabs are on screen while it shows. */
93
+ workspace: (w: WorkspaceOnScreen | null) => void;
94
+ /** Runs a call's moves (one call at a time, in order); `force` runs them even mid-edit. */
95
+ run: (id: string, moves: AssistantMove[], force?: boolean) => Promise<ScreenOutcome>;
96
+ undo: (id: string) => Promise<ScreenOutcome>;
97
+ canUndo: (id: string) => boolean;
98
+ map: () => ScreenMap;
99
+ /** What the page note says the assistant can open and point at. */
100
+ can: () => {
101
+ cards: string[];
102
+ rows: string[];
103
+ } | null;
104
+ }
105
+ export declare const useAssistantScreen: () => ScreenApi | null;
106
+ /** The workspace page tells the assistant which workspace and tab are on screen, by id. */
107
+ export declare function useAssistantWorkspace(w: WorkspaceOnScreen | null): void;
108
+ /** A part of the screen takes moves while it is mounted. */
109
+ export declare function useAssistantMoves(slot: string, handlers: AssistantMoveHandlers): void;
110
+ /**
111
+ * The nav's pages, for the page map the assistant reads and the @ picker: each keeps its sidebar section
112
+ * and what it stands for, so two "SPY" pages read as Trades ▸ SPY and Screeners ▸ SPY.
113
+ */
114
+ export declare function useAssistantPages(items: {
115
+ title: string;
116
+ to: string;
117
+ type?: AssistantPageType;
118
+ parent?: string;
119
+ }[], section?: string, type?: AssistantPageType): void;
120
+ /** Holds what each part of the screen takes, and runs the assistant's moves over them. */
121
+ export declare function AssistantScreenProvider({ children }: {
122
+ children: ReactNode;
123
+ }): import("react").JSX.Element;
124
+ export {};
@@ -0,0 +1,252 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRouter } from '@tanstack/react-router';
3
+ import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, } from 'react';
4
+ import { cardsOnScreen } from './spotlight.js';
5
+ /** How long one screen move may take before it fails. */
6
+ const MOVE_MS = 10_000;
7
+ const ScreenCtx = createContext(null);
8
+ export const useAssistantScreen = () => useContext(ScreenCtx);
9
+ /** The workspace page tells the assistant which workspace and tab are on screen, by id. */
10
+ export function useAssistantWorkspace(w) {
11
+ const set = useContext(ScreenCtx)?.workspace;
12
+ const key = w ? JSON.stringify(w) : '';
13
+ useEffect(() => {
14
+ if (!set || !key)
15
+ return;
16
+ set(JSON.parse(key));
17
+ return () => set(null);
18
+ }, [set, key]);
19
+ }
20
+ /** A part of the screen takes moves while it is mounted. */
21
+ export function useAssistantMoves(slot, handlers) {
22
+ const s = useContext(ScreenCtx);
23
+ // The latest handlers, read when a move runs: no re-register on every render.
24
+ const ref = useRef(handlers);
25
+ ref.current = handlers;
26
+ const register = s?.register;
27
+ const sections = JSON.stringify(handlers.sections ?? null);
28
+ useEffect(() => {
29
+ if (!register)
30
+ return;
31
+ const live = ref.current;
32
+ const proxy = {
33
+ open: live.open &&
34
+ ((t) => (ref.current.open ? ref.current.open(t) : 'This page has no rows to open')),
35
+ chart: live.chart && ((x, i) => ref.current.chart?.(x, i)),
36
+ settings: live.settings && ((x) => ref.current.settings?.(x)),
37
+ sections: JSON.parse(sections) ?? undefined,
38
+ rows: live.rows && (() => ref.current.rows?.() ?? []),
39
+ read: live.read && (() => ref.current.read?.() ?? ''),
40
+ editing: () => ref.current.editing?.() ?? false,
41
+ snapshot: live.snapshot && (() => ref.current.snapshot?.()),
42
+ restore: live.restore && ((x) => ref.current.restore?.(x)),
43
+ };
44
+ register(slot, proxy);
45
+ return () => register(slot, null);
46
+ }, [register, slot, sections]);
47
+ }
48
+ /**
49
+ * The nav's pages, for the page map the assistant reads and the @ picker: each keeps its sidebar section
50
+ * and what it stands for, so two "SPY" pages read as Trades ▸ SPY and Screeners ▸ SPY.
51
+ */
52
+ export function useAssistantPages(items, section, type) {
53
+ const s = useContext(ScreenCtx);
54
+ const key = useId();
55
+ const pages = JSON.stringify(items.map((i) => ({
56
+ path: i.to,
57
+ title: i.title,
58
+ section,
59
+ type: i.type ?? type,
60
+ parent: i.parent,
61
+ })));
62
+ const set = s?.pages;
63
+ useEffect(() => {
64
+ if (!set)
65
+ return;
66
+ set(key, JSON.parse(pages));
67
+ return () => set(key, null);
68
+ }, [set, key, pages]);
69
+ }
70
+ const sleep = (ms) => new Promise((done) => setTimeout(done, ms));
71
+ /** Polls until `cond` holds, up to `ms`. */
72
+ async function waitFor(cond, ms = 3000) {
73
+ for (let t = 0; t <= ms; t += 100) {
74
+ const v = cond();
75
+ if (v)
76
+ return v;
77
+ await sleep(100);
78
+ }
79
+ return null;
80
+ }
81
+ /** Scrolls to an element and pulses it for 2 s. */
82
+ function pulse(el) {
83
+ el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'smooth' });
84
+ el.dataset.assistantPoint = '';
85
+ setTimeout(() => delete el.dataset.assistantPoint, 2000);
86
+ }
87
+ const here = () => `${window.location.pathname}${window.location.search}${window.location.hash}`;
88
+ const cardOf = (id) => document.querySelector(`[data-slot="lumen-card"][data-card="${CSS.escape(id)}"]`);
89
+ /** Holds what each part of the screen takes, and runs the assistant's moves over them. */
90
+ export function AssistantScreenProvider({ children }) {
91
+ const router = useRouter();
92
+ const slots = useRef(new Map());
93
+ const navPages = useRef(new Map());
94
+ const shownWorkspace = useRef(null);
95
+ const snaps = useRef(new Map());
96
+ // One call at a time, in the order they came.
97
+ const chain = useRef(Promise.resolve());
98
+ const register = useCallback((slot, h) => {
99
+ if (h)
100
+ slots.current.set(slot, h);
101
+ else
102
+ slots.current.delete(slot);
103
+ }, []);
104
+ const pages = useCallback((key, items) => {
105
+ if (items)
106
+ navPages.current.set(key, items);
107
+ else
108
+ navPages.current.delete(key);
109
+ }, []);
110
+ const editing = useCallback(() => [...slots.current.values()].some((h) => h.editing?.()) ||
111
+ !!document.querySelector('[role="alertdialog"]'), []);
112
+ const settled = useCallback(() => waitFor(() => router.state.status === 'idle' && !router.state.isLoading, 3000), [router]);
113
+ const snapshot = useCallback(() => ({
114
+ href: here(),
115
+ slots: [...slots.current].flatMap(([slot, h]) => h.snapshot ? [[slot, h.snapshot()]] : []),
116
+ }), []);
117
+ /** One move; returns why it failed, or null. */
118
+ const one = useCallback(async (m) => {
119
+ if ('go' in m) {
120
+ const [path] = m.go.split(/[?#]/);
121
+ // A search (Try screener's inputs) goes as an href, which the router parses like a link.
122
+ await router.navigate((/[?#]/.test(m.go) ? { href: m.go } : { to: m.go }));
123
+ await settled();
124
+ const last = router.state.matches[router.state.matches.length - 1];
125
+ if (!last || last.routeId === '__root__' || last.status === 'notFound')
126
+ return `no page ${path} on this desk`;
127
+ // The new page registers its views; give it a moment to mount.
128
+ await sleep(150);
129
+ return null;
130
+ }
131
+ if ('settings' in m) {
132
+ const h = slots.current.get('settings');
133
+ if (!h?.settings)
134
+ return 'Settings is not on this desk';
135
+ h.settings(m.settings);
136
+ if (!m.row)
137
+ return null;
138
+ const want = m.row.toLowerCase();
139
+ const row = await waitFor(() => [
140
+ ...document.querySelectorAll('[data-slot="settings"] [data-slot="setting-row"]'),
141
+ ].find((el) => el.querySelector('label')?.textContent?.trim().toLowerCase().includes(want)));
142
+ if (!row)
143
+ return `no row "${m.row}" in that section`;
144
+ pulse(row);
145
+ return null;
146
+ }
147
+ if ('chart' in m) {
148
+ const h = slots.current.get('chart');
149
+ if (!h?.chart)
150
+ return 'this desk has no chart';
151
+ h.chart(m.chart, m.indicators);
152
+ return null;
153
+ }
154
+ if ('open' in m) {
155
+ const h = await waitFor(() => [...slots.current.values()].find((x) => x.open));
156
+ if (!h?.open)
157
+ return 'no view with rows on this page';
158
+ return h.open(m.open);
159
+ }
160
+ const id = m.point.replace(/^card:/, '');
161
+ const el = await waitFor(() => cardOf(id));
162
+ if (!el)
163
+ return `no card "${id}" on this page`;
164
+ pulse(el);
165
+ return null;
166
+ }, [router, settled]);
167
+ const run = useCallback((id, moves, force = false) => {
168
+ const go = async () => {
169
+ if (!force && editing())
170
+ return { status: 'waiting' };
171
+ snaps.current.set(id, snapshot());
172
+ for (const m of moves) {
173
+ // A move that never settles (a navigation that hangs) fails on its own, so it can't
174
+ // hold every later move and Undo behind it.
175
+ let timer;
176
+ const late = new Promise((done) => {
177
+ timer = setTimeout(() => done(`the move took over ${MOVE_MS / 1000} s`), MOVE_MS);
178
+ });
179
+ const wrong = await Promise.race([one(m).catch((e) => e.message), late]);
180
+ clearTimeout(timer);
181
+ if (wrong)
182
+ return { status: 'failed', text: wrong.slice(0, 200) };
183
+ }
184
+ // What the page shows after the moves: the view that took them reads itself once its rows
185
+ // are in (up to 3 s), so the assistant hears rows and sources, not only "done".
186
+ const view = await waitFor(() => [...slots.current.values()].find((h) => h.read));
187
+ const text = view?.read ? await waitFor(() => view.read?.() || null, 3000) : null;
188
+ return text ? { status: 'done', text: text.slice(0, 1200) } : { status: 'done' };
189
+ };
190
+ const next = chain.current.then(go, go);
191
+ chain.current = next.catch(() => undefined);
192
+ return next;
193
+ }, [editing, snapshot, one]);
194
+ const undo = useCallback((id) => {
195
+ const go = async () => {
196
+ const snap = snaps.current.get(id);
197
+ if (!snap)
198
+ return { status: 'failed', text: 'nothing to undo' };
199
+ snaps.current.delete(id);
200
+ if (here() !== snap.href) {
201
+ router.history.push(snap.href);
202
+ await settled();
203
+ await sleep(150);
204
+ }
205
+ for (const [slot, value] of snap.slots) {
206
+ const h = await waitFor(() => slots.current.get(slot), 1000);
207
+ h?.restore?.(value);
208
+ }
209
+ return { status: 'undone' };
210
+ };
211
+ const next = chain.current.then(go, go);
212
+ chain.current = next.catch(() => undefined);
213
+ return next;
214
+ }, [router, settled]);
215
+ const api = useMemo(() => ({
216
+ register,
217
+ pages,
218
+ workspace: (w) => {
219
+ shownWorkspace.current = w;
220
+ },
221
+ run,
222
+ undo,
223
+ canUndo: (id) => snaps.current.has(id),
224
+ map: () => {
225
+ const routes = [
226
+ ...new Set(Object.values(router.routesById)
227
+ .map((r) => (r.fullPath ?? '').replace(/(.)\/$/, '$1'))
228
+ .filter((p) => p.startsWith('/'))),
229
+ ];
230
+ const seen = new Set();
231
+ const named = [...navPages.current.values()].flat().filter((p) => {
232
+ if (seen.has(p.path))
233
+ return false;
234
+ seen.add(p.path);
235
+ return true;
236
+ });
237
+ return {
238
+ routes,
239
+ pages: named.slice(0, 300),
240
+ settings: slots.current.get('settings')?.sections ?? [],
241
+ cards: cardsOnScreen().slice(0, 60),
242
+ ...(shownWorkspace.current ? { workspace: shownWorkspace.current } : {}),
243
+ };
244
+ },
245
+ can: () => {
246
+ const cards = cardsOnScreen().map((c) => `${c.id} (${c.title})`);
247
+ const rows = [...slots.current.values()].flatMap((h) => h.rows?.() ?? []).slice(0, 30);
248
+ return { cards, rows };
249
+ },
250
+ }), [register, pages, run, undo, router]);
251
+ return _jsx(ScreenCtx.Provider, { value: api, children: children });
252
+ }
@@ -0,0 +1,16 @@
1
+ export declare function CardSwitch({ label, checked, onCheckedChange, }: {
2
+ label: string;
3
+ checked: boolean;
4
+ onCheckedChange: (on: boolean) => void;
5
+ }): import("react").JSX.Element;
6
+ /** A one-of-a-few choice for a card's header (`tools`), such as the live basket Orders card's
7
+ * Live | Shadow: a segmented control that never goes empty. */
8
+ export declare function CardSegments<T extends string>({ label, value, options, onValueChange, }: {
9
+ label: string;
10
+ value: T;
11
+ options: {
12
+ value: T;
13
+ label: string;
14
+ }[];
15
+ onValueChange: (v: T) => void;
16
+ }): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // A labelled on/off switch for a card's header (`tools`) or toolbar: one
3
+ // control for a mode a card turns on, like Tune's Advanced, instead of a pair of buttons in its body.
4
+ import { useId } from 'react';
5
+ import { Label } from './ui/label.js';
6
+ import { Switch } from './ui/switch.js';
7
+ import { ToggleGroup, ToggleGroupItem } from './ui/toggle-group.js';
8
+ export function CardSwitch({ label, checked, onCheckedChange, }) {
9
+ const id = useId();
10
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: id, checked: checked, onCheckedChange: onCheckedChange, size: "sm" }), _jsx(Label, { htmlFor: id, className: "text-xs", children: label })] }));
11
+ }
12
+ /** A one-of-a-few choice for a card's header (`tools`), such as the live basket Orders card's
13
+ * Live | Shadow: a segmented control that never goes empty. */
14
+ export function CardSegments({ label, value, options, onValueChange, }) {
15
+ return (_jsx(ToggleGroup, { type: "single", size: "sm", variant: "outline", spacing: 0, value: value, onValueChange: (v) => v && onValueChange(v), "aria-label": label, children: options.map((o) => (_jsx(ToggleGroupItem, { value: o.value, className: "px-2.5 text-xs", children: o.label }, o.value))) }));
16
+ }
@@ -0,0 +1,14 @@
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ export declare function ColorDot({ color, className }: {
3
+ color: string;
4
+ className?: string;
5
+ }): import("react").JSX.Element;
6
+ /** The dot as an icon component, for an `LumenOption.icon`; one per colour, so it keeps its identity. */
7
+ export declare function colorIcon(color: string): ComponentType<{
8
+ className?: string;
9
+ }>;
10
+ /** A value as a chip in its colour (a label): a tint behind, the colour as text. */
11
+ export declare function ColorChip({ color, children }: {
12
+ color?: string;
13
+ children: ReactNode;
14
+ }): import("react").JSX.Element;