@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,67 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronDownIcon, Grid3x3Icon, ListTreeIcon, TableIcon } from 'lucide-react';
3
+ import { useState } from 'react';
4
+ import { cn } from './lib/utils.js';
5
+ import { LumenSelector } from './lumen-selector.js';
6
+ import { Button } from './ui/button.js';
7
+ import { Label } from './ui/label.js';
8
+ import { Popover, PopoverContent, PopoverTrigger } from './ui/popover.js';
9
+ import { pick, subgroupLabel, subgroupOf, subgroupsOf, } from './view-model.js';
10
+ export const GROUP_NONE = '_none';
11
+ export const GROUP_PIVOT = '_pivot';
12
+ /** A view's grouping for the visit: by a field, as a pivot, and what each row opens. */
13
+ export function useGroup(domain, config) {
14
+ const groupable = pick(domain.fields.filter((f) => f.groupable), config.group.fields);
15
+ const [mode, setMode] = useState(config.shape === 'pivot' && config.pivot ? GROUP_PIVOT : (config.group.default ?? GROUP_NONE));
16
+ const groupField = groupable.find((f) => f.key === mode) ?? null;
17
+ // What each row opens: the view's pick, else the domain's first.
18
+ const subgroups = subgroupsOf(domain);
19
+ const [sub, setSub] = useState(() => subgroupOf(domain, config.subgroup)?.key ?? null);
20
+ // A saved grouping patched while the view is open (the assistant's card move) applies at once; the
21
+ // person's own unsaved pick stays while the saved one does not change.
22
+ const savedGroup = JSON.stringify([
23
+ config.shape === 'pivot' && !!config.pivot,
24
+ config.group.default,
25
+ config.subgroup ?? null,
26
+ ]);
27
+ // Taken during render, not in an effect, so a sheet never sees the old pick against the new
28
+ // saved one and saves it back (Sub-group saved from Edit view looped).
29
+ const [seenGroup, setSeenGroup] = useState(savedGroup);
30
+ if (seenGroup !== savedGroup) {
31
+ setSeenGroup(savedGroup);
32
+ setMode(config.shape === 'pivot' && config.pivot ? GROUP_PIVOT : (config.group.default ?? GROUP_NONE));
33
+ setSub(subgroupOf(domain, config.subgroup)?.key ?? null);
34
+ }
35
+ const pivot = mode === GROUP_PIVOT && !!config.pivot;
36
+ const grouping = groupable.length > 0 || !!config.pivot || subgroups.length > 0;
37
+ const button = grouping ? (_jsx(GroupButton, { groupable: groupable, pivot: !!config.pivot, mode: mode, onMode: setMode, subgroups: subgroups, opensLabel: subgroupLabel(domain), subgroup: sub, onSubgroup: setSub })) : null;
38
+ // What the person holds now, for a page that saves or shows it as changed (rule C).
39
+ const held = {
40
+ group: pivot || mode === GROUP_NONE || mode === GROUP_PIVOT ? null : mode,
41
+ pivot,
42
+ subgroup: sub,
43
+ };
44
+ const reset = () => {
45
+ setMode(config.shape === 'pivot' && config.pivot ? GROUP_PIVOT : (config.group.default ?? GROUP_NONE));
46
+ setSub(subgroupOf(domain, config.subgroup)?.key ?? null);
47
+ };
48
+ return { groupField: pivot ? null : groupField, pivot, sub, button, held, reset };
49
+ }
50
+ /**
51
+ * The Group button: says what is on ("Account › Tax lots") and opens two
52
+ * pickers, Group rows by (a field, or Pivot) and Each row opens (the domain's sub-groups), each
53
+ * with None.
54
+ */
55
+ export function GroupButton({ groupable, pivot, mode, onMode, subgroups, subgroup, onSubgroup, opensLabel = 'Each row opens', }) {
56
+ const group = mode === GROUP_PIVOT ? 'Pivot' : (groupable.find((f) => f.key === mode)?.label ?? null);
57
+ const opens = subgroups.find((s) => s.key === subgroup)?.label ?? null;
58
+ const on = [group, opens].filter(Boolean);
59
+ return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: cn('max-w-40 shrink-0', !on.length && 'text-muted-foreground'), "aria-label": "Group", children: [_jsx(ListTreeIcon, {}), _jsx("span", { className: "truncate", children: on.length ? on.join(' › ') : 'Group' }), _jsx(ChevronDownIcon, { className: "text-muted-foreground" })] }) }), _jsxs(PopoverContent, { align: "end", className: "flex w-60 flex-col gap-3", children: [(groupable.length > 0 || pivot) && (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "group-rows-by", children: "Group rows by" }), _jsx(LumenSelector, { id: "group-rows-by", label: "Group rows by", size: "sm", className: "w-full", options: [
60
+ { value: GROUP_NONE, label: 'None', icon: TableIcon },
61
+ ...groupable.map((f) => ({ value: f.key, label: f.label, icon: f.icon })),
62
+ ...(pivot ? [{ value: GROUP_PIVOT, label: 'Pivot', icon: Grid3x3Icon }] : []),
63
+ ], value: mode, onChange: onMode })] })), subgroups.length > 0 && (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "each-row-opens", children: opensLabel }), _jsx(LumenSelector, { id: "each-row-opens", label: opensLabel, size: "sm", className: "w-full", disabled: mode === GROUP_PIVOT, options: [
64
+ { value: GROUP_NONE, label: 'None' },
65
+ ...subgroups.map((s) => ({ value: s.key, label: s.label, icon: s.icon })),
66
+ ], value: subgroup ?? GROUP_NONE, onChange: (v) => onSubgroup(v === GROUP_NONE ? null : v) })] }))] })] }));
67
+ }
@@ -0,0 +1,77 @@
1
+ import { type ReactNode } from 'react';
2
+ /** Ten distinct colours. A variant, an asset or a start keeps one colour on every card and table. */
3
+ export declare const PALETTE: string[];
4
+ export declare const paletteAt: (i: number) => string;
5
+ export declare const benchColour: (i: number) => string;
6
+ /** `log` and `linear` (from 0) for growth of $1; `fit` is linear around the data (Performance). */
7
+ export type Scale = 'log' | 'linear' | 'fit';
8
+ export type Series = {
9
+ key: string;
10
+ label: string;
11
+ color: string;
12
+ hold: boolean;
13
+ dashed: boolean;
14
+ /** Filled under the line (a portfolio's value). */
15
+ area?: boolean;
16
+ points: [string, number][];
17
+ };
18
+ export declare const KEY_DATE_KINDS: readonly [{
19
+ readonly kind: 'fomc';
20
+ readonly label: 'FOMC';
21
+ readonly color: 'var(--key-date-fomc)';
22
+ }, {
23
+ readonly kind: 'cpi';
24
+ readonly label: 'CPI';
25
+ readonly color: 'var(--key-date-cpi)';
26
+ }, {
27
+ readonly kind: 'jobs';
28
+ readonly label: 'Jobs';
29
+ readonly color: 'var(--key-date-jobs)';
30
+ }];
31
+ export type KeyDateKind = (typeof KEY_DATE_KINDS)[number]['kind'];
32
+ /** A thin vertical line on the chart; its label shows in the tooltip. */
33
+ export type ChartMarker = {
34
+ date: string;
35
+ label: string;
36
+ color: string;
37
+ kind?: string;
38
+ };
39
+ export type KeyDateMarker = ChartMarker & {
40
+ kind: KeyDateKind;
41
+ };
42
+ /**
43
+ * A dashed, labelled line at `from` with the stretch before it shaded: the in-sample part ahead of
44
+ * an out-of-sample date, so the blind part reads plain (shade left of OOS). With `onDrag` the line drags along the dates and the
45
+ * day it lands on is handed back.
46
+ */
47
+ export type ChartBand = {
48
+ key: string;
49
+ from: string;
50
+ label: string;
51
+ color: string;
52
+ onDrag?: (date: string) => void;
53
+ /** The line's tag as a control (a button that opens a date picker) in place of its label. */
54
+ tag?: ReactNode;
55
+ };
56
+ /** The markers the chips turn on, from each kind's days. */
57
+ export declare function keyDateMarkers(on: KeyDateKind[], dates: Partial<Record<KeyDateKind, string[]>> | undefined): KeyDateMarker[];
58
+ /** FOMC / CPI / Jobs toggle chips, for a chart card's header. */
59
+ export declare function KeyDateChips({ on, setOn, }: {
60
+ on: KeyDateKind[];
61
+ setOn: (on: KeyDateKind[]) => void;
62
+ }): import("react").JSX.Element;
63
+ /**
64
+ * Growth of $1 (or any values) for a set of lines. `sinceYears` trims to the last N years (the
65
+ * expand view). `markers` are drawn as thin vertical lines, each on the first charted day at or
66
+ * after it. `format` labels the axis and tooltip (default `1.23×`); `ytd` marks this year's start.
67
+ */
68
+ export declare function GrowthChart({ lines, scale, className, sinceYears, markers, format, ytd, bands, }: {
69
+ lines: Series[];
70
+ scale: Scale;
71
+ className: string;
72
+ sinceYears?: number;
73
+ markers?: ChartMarker[];
74
+ format?: (v: number) => string;
75
+ ytd?: boolean;
76
+ bands?: ChartBand[];
77
+ }): import("react").JSX.Element;
@@ -0,0 +1,195 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // The growth chart every app shares (moved in from an app): Recharts in
3
+ // shadcn's ChartContainer. An app draws growth of $1 for variants and benchmarks with it;
4
+ // An app's Portfolio ▸ Performance draws a portfolio's value and money in (an area and a dashed
5
+ // line) or its time-weighted growth next to any symbol. Key dates (FOMC, CPI, Jobs) are thin markers,
6
+ // each turned on by its chip (all start off); the app fetches the days and hands them in.
7
+ import { useMemo, useState } from 'react';
8
+ import { Area, CartesianGrid, ComposedChart, Line, ReferenceArea, ReferenceLine, XAxis, YAxis, } from 'recharts';
9
+ import { cn } from './lib/utils.js';
10
+ import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, } from './ui/chart.js';
11
+ import { ToggleGroup, ToggleGroupItem } from './ui/toggle-group.js';
12
+ /** Ten distinct colours. A variant, an asset or a start keeps one colour on every card and table. */
13
+ export const PALETTE = Array.from({ length: 10 }, (_, i) => `var(--chart-${i + 1})`);
14
+ export const paletteAt = (i) => PALETTE[i % PALETTE.length] ?? '';
15
+ /** Benchmarks take the palette's second half, so they never share a colour with a run's lines. */
16
+ const BENCH_COLOURS = [5, 6, 7, 8, 9].map((i) => PALETTE[i] ?? '');
17
+ export const benchColour = (i) => BENCH_COLOURS[i % BENCH_COLOURS.length] ?? '';
18
+ const YTD_START = '2026-01-01';
19
+ /** Log ticks per decade: 1-2-3-5 up to about 300×, then one a decade so labels never collide. */
20
+ const decadeSteps = (decades) => (decades <= 2.5 ? [1, 2, 3, 5] : [1]);
21
+ /** Log axis ticks covering `lo` to `hi`: from the tick at or under the low to the one at or over the high. */
22
+ function logTicks(lo, hi) {
23
+ if (!(lo > 0) || !(hi >= lo))
24
+ return [0.5, 1, 2];
25
+ const from = Math.floor(Math.log10(lo));
26
+ const to = Math.ceil(Math.log10(hi));
27
+ const steps = decadeSteps(Math.log10(hi / lo));
28
+ const all = [];
29
+ for (let e = from; e <= to; e++) {
30
+ // Rounded so 0.1 × 3 reads 0.3, not 0.30000000000000004.
31
+ for (const m of steps)
32
+ all.push(Number((m * 10 ** e).toPrecision(6)));
33
+ }
34
+ const floor = all.filter((t) => t <= lo).pop() ?? all[0] ?? lo;
35
+ const ceil = all.find((t) => t >= hi) ?? all[all.length - 1] ?? hi;
36
+ return all.filter((t) => t >= floor && t <= ceil);
37
+ }
38
+ const MINUS = '−';
39
+ // ── Key dates ──
40
+ export const KEY_DATE_KINDS = [
41
+ { kind: 'fomc', label: 'FOMC', color: 'var(--key-date-fomc)' },
42
+ { kind: 'cpi', label: 'CPI', color: 'var(--key-date-cpi)' },
43
+ { kind: 'jobs', label: 'Jobs', color: 'var(--key-date-jobs)' },
44
+ ];
45
+ /** The markers the chips turn on, from each kind's days. */
46
+ export function keyDateMarkers(on, dates) {
47
+ return KEY_DATE_KINDS.filter((k) => on.includes(k.kind)).flatMap((k) => (dates?.[k.kind] ?? []).map((date) => ({ date, ...k })));
48
+ }
49
+ /** FOMC / CPI / Jobs toggle chips, for a chart card's header. */
50
+ export function KeyDateChips({ on, setOn, }) {
51
+ return (_jsx(ToggleGroup, { type: "multiple", variant: "outline", size: "sm", value: on, onValueChange: (v) => setOn(v), "aria-label": "Key dates", children: KEY_DATE_KINDS.map((k) => (_jsxs(ToggleGroupItem, { value: k.kind, "aria-label": `${k.label} days`, children: [_jsx("span", { className: "h-3 w-0.5 rounded-full", style: { background: k.color } }), k.label] }, k.kind))) }));
52
+ }
53
+ // ── The chart ──
54
+ /** `1.23×`: growth of $1, the default axis and tooltip. */
55
+ const times = (v) => `${v < 0 ? MINUS : ''}${Math.abs(v).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}×`;
56
+ /** Axis ticks stay short (`0.5×`, `20×`, `10K×`) so they fit the axis. */
57
+ const shortTimes = (v) => `${v < 0 ? MINUS : ''}${Math.abs(v).toLocaleString('en-US', {
58
+ maximumFractionDigits: 2,
59
+ ...(Math.abs(v) >= 1000 ? { notation: 'compact' } : {}),
60
+ })}×`;
61
+ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
62
+ /**
63
+ * Growth of $1 (or any values) for a set of lines. `sinceYears` trims to the last N years (the
64
+ * expand view). `markers` are drawn as thin vertical lines, each on the first charted day at or
65
+ * after it. `format` labels the axis and tooltip (default `1.23×`); `ytd` marks this year's start.
66
+ */
67
+ export function GrowthChart({ lines, scale, className, sinceYears, markers = [], format = times, ytd = true, bands = [], }) {
68
+ const config = Object.fromEntries(lines.map((l) => [l.key, { label: l.label, color: l.color }]));
69
+ const { rows, ticks, xTicks, monthly, dates, ytdStart } = useMemo(() => {
70
+ let from = '';
71
+ if (sinceYears) {
72
+ const end = lines
73
+ .flatMap((l) => l.points.map((p) => p[0]))
74
+ .sort()
75
+ .pop();
76
+ if (end) {
77
+ const d = new Date(`${end}T00:00:00Z`);
78
+ d.setUTCFullYear(d.getUTCFullYear() - sinceYears);
79
+ from = d.toISOString().slice(0, 10);
80
+ }
81
+ }
82
+ const byDate = new Map();
83
+ let lo = Number.POSITIVE_INFINITY;
84
+ let hi = 0;
85
+ for (const l of lines) {
86
+ for (const [d, g] of l.points) {
87
+ if (d < from)
88
+ continue;
89
+ const row = byDate.get(d) ?? { date: d };
90
+ row[l.key] = g;
91
+ byDate.set(d, row);
92
+ lo = Math.min(lo, g);
93
+ hi = Math.max(hi, g);
94
+ }
95
+ }
96
+ const rows = [...byDate.values()].sort((a, b) => String(a.date).localeCompare(String(b.date)));
97
+ const ticks = logTicks(lo, hi);
98
+ const dates = rows.map((r) => String(r.date));
99
+ // Under about two years the axis marks months (each quarter's past a year), else years.
100
+ const first = dates[0] ?? '';
101
+ const last = dates[dates.length - 1] ?? '';
102
+ const days = first ? (Date.parse(last) - Date.parse(first)) / 86_400_000 : 0;
103
+ const monthly = days < 400;
104
+ const step = days < 120 ? 1 : 3;
105
+ const xTicks = monthly
106
+ ? dates.filter((d, i, a) => i > 0 &&
107
+ d.slice(0, 7) !== a[i - 1]?.slice(0, 7) &&
108
+ (Number(d.slice(5, 7)) - 1) % step === 0)
109
+ : dates.filter((d, i, a) => d.slice(0, 4) !== a[i - 1]?.slice(0, 4));
110
+ return {
111
+ rows,
112
+ ticks,
113
+ xTicks,
114
+ monthly,
115
+ dates,
116
+ ytdStart: ytd ? dates.find((d) => d >= YTD_START) : undefined,
117
+ };
118
+ }, [lines, sinceYears, ytd]);
119
+ // A thinned curve skips days, so each marker snaps to the next charted day; days outside it drop.
120
+ const { snapped, eventsOn } = useMemo(() => {
121
+ const snapped = [];
122
+ const eventsOn = new Map();
123
+ const first = dates[0];
124
+ if (!first)
125
+ return { snapped, eventsOn };
126
+ let i = 0;
127
+ for (const m of [...markers].sort((a, b) => a.date.localeCompare(b.date))) {
128
+ if (m.date < first)
129
+ continue;
130
+ while (i < dates.length && (dates[i] ?? '') < m.date)
131
+ i++;
132
+ const x = dates[i];
133
+ if (!x)
134
+ break;
135
+ snapped.push({ ...m, x });
136
+ eventsOn.set(x, [...(eventsOn.get(x) ?? []), m.label]);
137
+ }
138
+ return { snapped, eventsOn };
139
+ }, [markers, dates]);
140
+ // A band's line drags: a press near it picks it up, the move shows where it goes, release hands it back.
141
+ const [drag, setDrag] = useState(null);
142
+ const snap = (d) => dates.find((x) => x >= d);
143
+ const near = (label) => {
144
+ const i = dates.indexOf(String(label));
145
+ if (i < 0)
146
+ return null;
147
+ const reach = Math.max(2, Math.round(dates.length / 40));
148
+ return (bands.find((b) => {
149
+ const at = snap(b.from);
150
+ return b.onDrag && at !== undefined && Math.abs(dates.indexOf(at) - i) <= reach;
151
+ }) ?? null);
152
+ };
153
+ const label = (s) => s && typeof s === 'object' && 'activeLabel' in s
154
+ ? s.activeLabel
155
+ : null;
156
+ const last = dates[dates.length - 1];
157
+ return (_jsx(ChartContainer, { config: config, className: cn(className, drag && 'cursor-col-resize select-none'), children: _jsxs(ComposedChart, { data: rows, margin: { left: 0, right: 8 }, onMouseDown: (s) => {
158
+ const b = near(label(s));
159
+ if (b)
160
+ setDrag({ key: b.key, at: String(label(s)) });
161
+ }, onMouseMove: (s) => {
162
+ const at = label(s);
163
+ if (drag && at)
164
+ setDrag({ ...drag, at: String(at) });
165
+ }, onMouseUp: () => {
166
+ if (!drag)
167
+ return;
168
+ bands.find((b) => b.key === drag.key)?.onDrag?.(drag.at);
169
+ setDrag(null);
170
+ }, onMouseLeave: () => setDrag(null), children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: "date", ticks: xTicks, tickFormatter: (d) => monthly ? (MONTHS[Number(d.slice(5, 7)) - 1] ?? '') : d.slice(0, 4), tickLine: false, axisLine: false }), scale === 'log' ? (_jsx(YAxis, { scale: "log", domain: [ticks[0] ?? 0.5, ticks[ticks.length - 1] ?? 10], ticks: ticks, tickFormatter: format === times ? shortTimes : format, tickLine: false, axisLine: false, width: 40, allowDataOverflow: true })) : (_jsx(YAxis, { domain: scale === 'fit' ? ['auto', 'auto'] : [0, 'auto'], tickFormatter: format, tickLine: false, axisLine: false, width: scale === 'fit' ? 56 : 40 })), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { indicator: "line", labelFormatter: (label) => {
171
+ const events = eventsOn.get(String(label));
172
+ return events ? `${label} · ${events.join(', ')}` : label;
173
+ }, formatter: (value, name) => (_jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx("span", { className: "h-0.5 w-3 shrink-0 rounded-full", style: { background: `var(--color-${name})` } }), _jsx("span", { className: "flex-1 text-muted-foreground", children: config[name]?.label }), _jsx("span", { className: "num", children: format(Number(value)) })] })) }) }), _jsx(ChartLegend, { content: _jsx(ChartLegendContent, {}) }), ytdStart && (_jsx(ReferenceLine, { x: ytdStart, stroke: "var(--muted-foreground)", strokeDasharray: "3 3", label: { value: '2026 YTD', position: 'insideTopRight', fontSize: 11 } })), bands.map((b) => {
174
+ const x = drag?.key === b.key ? drag.at : snap(b.from);
175
+ if (!x || !last)
176
+ return null;
177
+ return [
178
+ _jsx(ReferenceArea, { x1: dates[0], x2: x, fill: b.color, fillOpacity: 0.07, ifOverflow: "hidden" }, `${b.key}-area`),
179
+ _jsx(ReferenceLine, { x: x, stroke: b.color, strokeWidth: drag?.key === b.key ? 2 : 1.25, strokeDasharray: "4 3", label: b.tag && drag?.key !== b.key
180
+ ? (p) => (_jsx(BandTag, { x: p.viewBox?.x ?? 0, y: p.viewBox?.y ?? 0, left: dates.indexOf(x) > dates.length * 0.7, children: b.tag }))
181
+ : {
182
+ value: drag?.key === b.key ? `${b.label} ${x}` : b.label,
183
+ // Late in the chart the label sits left of its line, so it stays on the chart.
184
+ position: dates.indexOf(x) > dates.length * 0.7 ? 'insideTopRight' : 'insideTopLeft',
185
+ fontSize: 11,
186
+ fill: b.color,
187
+ } }, `${b.key}-line`),
188
+ ];
189
+ }), snapped.map((m) => (_jsx(ReferenceLine, { x: m.x, stroke: m.color, strokeOpacity: 0.45, strokeWidth: 1, ifOverflow: "extendDomain" }, `${m.kind ?? m.label}-${m.date}`))), lines.map((l) => l.area ? (_jsx(Area, { dataKey: l.key, type: "linear", stroke: `var(--color-${l.key})`, fill: `var(--color-${l.key})`, fillOpacity: 0.12, strokeWidth: l.hold ? 1.5 : 2.2, dot: false, connectNulls: true, isAnimationActive: false }, l.key)) : (_jsx(Line, { dataKey: l.key, stroke: `var(--color-${l.key})`, strokeWidth: l.hold ? 1.5 : 2.2, strokeDasharray: l.dashed ? '5 3' : undefined, dot: false, connectNulls: true, isAnimationActive: false }, l.key)))] }) }));
190
+ }
191
+ /** A band's tag beside its line: HTML in the chart, kept out of the line's drag. */
192
+ function BandTag({ x, y, left, children, }) {
193
+ const w = 168;
194
+ return (_jsx("foreignObject", { x: left ? x - w - 4 : x + 4, y: y + 2, width: w, height: 28, children: _jsx("div", { className: cn('flex', left && 'justify-end'), onMouseDown: (e) => e.stopPropagation(), onMouseUp: (e) => e.stopPropagation(), children: children }) }));
195
+ }
@@ -0,0 +1,15 @@
1
+ /** The visual viewport while a phone's keyboard covers part of the layout viewport. */
2
+ export interface KeyboardFit {
3
+ /** Top of what is visible, from the layout viewport's top (iOS pans the page to the focused field). */
4
+ top: number;
5
+ /** Height of what is visible above the keyboard. */
6
+ height: number;
7
+ /** Layout viewport below the visible part: the keyboard. */
8
+ below: number;
9
+ }
10
+ /**
11
+ * Where a fixed modal must sit while the keyboard is open. iOS shrinks the visual viewport but not
12
+ * the layout viewport fixed elements use, so a modal centred or bottom-anchored there runs under the
13
+ * keyboard or off the top. Undefined while no keyboard is up.
14
+ */
15
+ export declare function useKeyboardFit(): KeyboardFit | undefined;
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Where a fixed modal must sit while the keyboard is open. iOS shrinks the visual viewport but not
4
+ * the layout viewport fixed elements use, so a modal centred or bottom-anchored there runs under the
5
+ * keyboard or off the top. Undefined while no keyboard is up.
6
+ */
7
+ export function useKeyboardFit() {
8
+ const [fit, setFit] = React.useState();
9
+ React.useEffect(() => {
10
+ const vv = window.visualViewport;
11
+ if (!vv)
12
+ return;
13
+ const update = () => {
14
+ // clientHeight is the layout viewport; on iOS innerHeight follows the visual one.
15
+ const layout = document.documentElement.clientHeight;
16
+ const below = Math.max(0, layout - vv.height - vv.offsetTop);
17
+ const covered = layout - vv.height > 120;
18
+ setFit(covered ? { top: vv.offsetTop, height: vv.height, below } : undefined);
19
+ };
20
+ update();
21
+ vv.addEventListener('resize', update);
22
+ vv.addEventListener('scroll', update);
23
+ return () => {
24
+ vv.removeEventListener('resize', update);
25
+ vv.removeEventListener('scroll', update);
26
+ };
27
+ }, []);
28
+ return fit;
29
+ }
@@ -0,0 +1 @@
1
+ export declare function useIsMobile(): boolean;
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ const MOBILE_BREAKPOINT = 768;
3
+ export function useIsMobile() {
4
+ const [isMobile, setIsMobile] = React.useState(undefined);
5
+ React.useEffect(() => {
6
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
7
+ const onChange = () => {
8
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
9
+ };
10
+ mql.addEventListener('change', onChange);
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
+ return () => mql.removeEventListener('change', onChange);
13
+ }, []);
14
+ return !!isMobile;
15
+ }
@@ -0,0 +1,21 @@
1
+ import type { ComponentProps, ComponentType } from 'react';
2
+ import { Button } from './ui/button.js';
3
+ export type IconButtonProps = Omit<ComponentProps<typeof Button>, 'size' | 'children'> & {
4
+ /** A lucide icon, or any icon component that takes `className` (a view's own icon). */
5
+ icon: ComponentType<{
6
+ className?: string;
7
+ }>;
8
+ /** `icon-sm` (headers, toolbars) by default; `icon-xs` in table rows and chips, `icon` in forms. */
9
+ size?: 'icon-xs' | 'icon-sm' | 'icon' | 'icon-lg';
10
+ /** The action's name: the tooltip and the accessible name. */
11
+ label: string;
12
+ /** Opens this route instead of running `onClick`. */
13
+ to?: string;
14
+ /** Opens this outside address in a new tab instead of running `onClick`. */
15
+ href?: string;
16
+ /** Spins the icon while the action runs. */
17
+ busy?: boolean;
18
+ /** A toggle that is on (pinned, followed): its icon fills in the app colour. */
19
+ pressed?: boolean;
20
+ };
21
+ export declare function IconButton({ icon: Icon, label, to, href, busy, pressed, variant, size, disabled, className, ...props }: IconButtonProps): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // The shell's one button: icon only, its name in a tooltip and accessible name.
3
+ import { Link } from '@tanstack/react-router';
4
+ import { cn } from './lib/utils.js';
5
+ import { Button } from './ui/button.js';
6
+ import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip.js';
7
+ /**
8
+ * Every button: icon only, its name in a tooltip and accessible name, never
9
+ * text. Headers use it as is; rows, toolbars and forms too. Works as the child of a `DialogTrigger`, `PopoverTrigger` or `DropdownMenuTrigger` (`asChild`).
10
+ */
11
+ /** Layout classes that place the button among its siblings, not style it. */
12
+ const PLACE = /^(?:[a-z0-9-]+:)*-?(?:m[trblxy]?-|self-|order-|shrink|grow|sticky|left-|align-|hidden$|block$)/;
13
+ export function IconButton({ icon: Icon, label, to, href, busy, pressed, variant = 'ghost', size = 'icon-sm', disabled, className, ...props }) {
14
+ // Disabled, the button sits in a tooltip wrapper: its place in the layout (margins, self, sticky,
15
+ // visibility) moves to the wrapper so nothing shifts while it is disabled.
16
+ const place = disabled ? (className ?? '').split(/\s+/).filter((c) => PLACE.test(c)) : [];
17
+ const own = disabled ? (className ?? '').split(/\s+/).filter((c) => !PLACE.test(c)) : [];
18
+ const icon = (_jsx(Icon, { className: cn(busy && 'animate-spin', pressed && 'fill-current text-primary') }));
19
+ const link = !disabled && (to || href);
20
+ const button = (_jsx(Button, { variant: variant, size: size, "aria-label": label, "aria-pressed": pressed, disabled: disabled, asChild: !!link, className: disabled ? cn(own) : className, ...props, children: !link ? (icon) : to ? (_jsx(Link, { to: to, children: icon })) : (_jsx("a", { href: href, target: "_blank", rel: "noreferrer", children: icon })) }));
21
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: disabled ? _jsx("span", { className: cn('inline-flex', place), children: button }) : button }), _jsx(TooltipContent, { children: label })] }));
22
+ }
@@ -0,0 +1,14 @@
1
+ import type { ViewIcon } from '@columenlabs/lumen-ui-types/views';
2
+ import type { NavIcon } from './nav.js';
3
+ export declare function IconPicker({ value, onChange, fallback, label, resetLabel, variant, id, }: {
4
+ value: ViewIcon | null | undefined;
5
+ /** null puts the default back. */
6
+ onChange: (icon: ViewIcon | null) => void;
7
+ /** What shows with no pick. */
8
+ fallback: NavIcon;
9
+ /** The trigger's tooltip. */
10
+ label: string;
11
+ resetLabel?: string;
12
+ variant?: 'ghost' | 'outline';
13
+ id?: string;
14
+ }): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { RotateCcwIcon, SearchIcon, ShapesIcon, SmileIcon } from 'lucide-react';
3
+ import { useState } from 'react';
4
+ import { IconButton } from './icon-button.js';
5
+ import { Button } from './ui/button.js';
6
+ import { EmojiPicker, EmojiPickerContent, EmojiPickerSearch } from './ui/emoji-picker.js';
7
+ import { InputGroup, InputGroupAddon, InputGroupInput } from './ui/input-group.js';
8
+ import { Popover, PopoverContent, PopoverTrigger } from './ui/popover.js';
9
+ import { Separator } from './ui/separator.js';
10
+ import { ToggleGroup, ToggleGroupItem } from './ui/toggle-group.js';
11
+ import { VIEW_ICONS, iconOf, lucideIconNames } from './view-icons.js';
12
+ /** Icon matches shown at once; search narrows the rest of lucide's set. */
13
+ const ICON_MATCHES = 120;
14
+ export function IconPicker({ value, onChange, fallback, label, resetLabel = 'Use the default icon', variant, id, }) {
15
+ const [open, setOpen] = useState(false);
16
+ const [kind, setKind] = useState('icons');
17
+ const [q, setQ] = useState('');
18
+ const Icon = iconOf(value, fallback);
19
+ const pick = (icon) => {
20
+ onChange(icon);
21
+ setOpen(false);
22
+ };
23
+ const term = q.trim().toLowerCase().replace(/\s+/g, '-');
24
+ const quick = Object.keys(VIEW_ICONS);
25
+ const names = (term
26
+ ? lucideIconNames.filter((n) => n.includes(term))
27
+ : [...quick, ...lucideIconNames.filter((n) => !quick.includes(n))]).slice(0, ICON_MATCHES);
28
+ return (_jsxs(Popover, { open: open, onOpenChange: (o) => {
29
+ setOpen(o);
30
+ if (o)
31
+ setQ('');
32
+ }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(IconButton, { id: id, icon: Icon, label: label, variant: variant }) }), _jsxs(PopoverContent, { align: "start", collisionPadding: 8, className: "flex w-80 max-w-[calc(100vw-1rem)] flex-col gap-0 p-0", children: [_jsx("div", { className: "p-2", children: _jsxs(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, className: "w-full", "aria-label": "Icons or emoji", value: kind, onValueChange: (v) => v && setKind(v), children: [_jsxs(ToggleGroupItem, { value: "icons", className: "flex-1", children: [_jsx(ShapesIcon, {}), "Icons"] }), _jsxs(ToggleGroupItem, { value: "emoji", className: "flex-1", children: [_jsx(SmileIcon, {}), "Emoji"] })] }) }), _jsx(Separator, {}), kind === 'icons' ? (_jsxs("div", { className: "flex h-80 flex-col", children: [_jsx("div", { className: "border-b p-2", children: _jsxs(InputGroup, { children: [_jsx(InputGroupAddon, { children: _jsx(SearchIcon, {}) }), _jsx(InputGroupInput, { "aria-label": "Search icons", placeholder: `Search ${lucideIconNames.length.toLocaleString()} icons`, value: q, onChange: (e) => setQ(e.target.value) })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-2", children: names.length ? (_jsx("div", { className: "grid grid-cols-8 gap-1", children: names.map((n) => {
33
+ const I = iconOf(n, fallback);
34
+ return (_jsx(Button, { variant: value === n ? 'secondary' : 'ghost', size: "icon-sm", title: n, "aria-label": `Icon ${n}`, "aria-pressed": value === n, onClick: () => pick(n), children: _jsx(I, {}) }, n));
35
+ }) })) : (_jsx("p", { className: "py-8 text-center text-sm text-muted-foreground", children: "No icon found." })) })] })) : (_jsxs(EmojiPicker, { className: "h-80 w-full", columns: 9, onEmojiSelect: ({ emoji }) => pick(emoji), children: [_jsx(EmojiPickerSearch, { placeholder: "Search emoji" }), _jsx(EmojiPickerContent, {})] })), _jsx(Separator, {}), _jsx(IconButton, { icon: RotateCcwIcon, label: resetLabel, className: "m-1", disabled: !value, onClick: () => pick(null) })] })] }));
36
+ }
@@ -0,0 +1,83 @@
1
+ export { type CustomViewConfig, LumenTable, type LumenTableProps, LumenView, type LumenViewProps, type ViewCellValue, type ViewChipTone, type ViewDomain, type ViewField, type ViewFieldGroup, type ViewSource, CHIP_TONE, RUN_STATE_TONE, FORMULAS_GROUP, ViewValueCell, rollUp, } from './lumen-view.js';
2
+ export { LumenLayout, type LumenLayoutCard, type LumenLayoutProps, type LumenLayoutViewProps, domainKeys, followRows, } from './lumen-layout.js';
3
+ export { LumenViewCard, type LumenViewCardProps } from './lumen-view-card.js';
4
+ export { LumenSelector, LumenSelectorList, type LumenSelectorListHandle, type LumenSelectorProps, type LumenOption, type LumenOptionGroup, SelectorRowContent, } from './lumen-selector.js';
5
+ export { ColorChip, ColorDot, colorIcon } from './color-dot.js';
6
+ export { EditCell, EditInputCell } from './view-edit-cell.js';
7
+ export { type CardOnScreen, cardsOnScreen, kindName, onScreen, pointAtCard, spotlight, } from './spotlight.js';
8
+ export { LinkDot, LinkPicker, type LinkColor, linkVar } from './lumen-links.js';
9
+ export { CardSegments, CardSwitch } from './card-switch.js';
10
+ export { PageInput, PageInputs } from './page-inputs.js';
11
+ export { type LumenTab, LumenTabs } from './lumen-tabs.js';
12
+ export { LumenTabDrawer, TAB_NAME_MAX } from './lumen-tab-drawer.js';
13
+ export { IconPicker } from './icon-picker.js';
14
+ export { LumenEChart, type EChartOption, type ChartTokens, axisStyle, baseOption, useChartTokens, } from './lumen-echart.js';
15
+ export { LumenGeoMap } from './lumen-geo-map.js';
16
+ export { LumenWidget, type CardControls, type CardLink, LumenSavedWidget } from './lumen-widget.js';
17
+ export { LumenConfirm, type LumenConfirmProps } from './confirm.js';
18
+ export { COVERAGE_LEVELS, CoverageCell, type CoverageJobState, type CoverageLevel, CoverageLegend, coverageClass, coverageLabel, coverageLevel, } from './coverage-cells.js';
19
+ export { type CoverageBlock, type CoverageData, type CoverageFact, type CoveragePick, type CoverageRow, type CoverageSummary, LumenCoverageMap, type LumenCoverageMapProps, blockName, blockOf, } from './coverage-map.js';
20
+ export { type CalendarFact, LumenCoverageCalendarMap, type LumenCoverageCalendarMapProps, } from './coverage-calendar-map.js';
21
+ export { DetailsTable, type DetailsTableProps, FactsTable } from './details-table.js';
22
+ export { DrawerSection, type DrawerSectionProps } from './drawer-section.js';
23
+ export { LumenDrawer, type LumenDrawerProps } from './lumen-drawer.js';
24
+ export { LumenCode, type LumenCodeProps, langOfPath } from './lumen-code.js';
25
+ export { LumenCodeEditor, type LumenCodeEditorProps, type LumenCodeLang, } from './lumen-code-editor.js';
26
+ export { RenameField, type RenameFieldProps } from './rename-field.js';
27
+ export { ResetButton, type ResetButtonProps } from './reset-button.js';
28
+ export { type ChartBand, type ChartMarker, GrowthChart, KEY_DATE_KINDS, KeyDateChips, type KeyDateKind, type KeyDateMarker, PALETTE, type Scale, type Series, benchColour, keyDateMarkers, paletteAt, } from './growth-chart.js';
29
+ export { FoldGroup, NavGroup, type NavItem, type SectionItem, type SectionSubItem, SidebarSection, } from './nav.js';
30
+ export { type OverviewPageProps, type OverviewStage, type OverviewTone, type OverviewWidget, OverviewList, OverviewPage, ToneDot, } from './overview.js';
31
+ export { type Crumb, IconButton, type IconButtonProps, Page, PageBody, PageHeader, type PageHeaderProps, } from './page.js';
32
+ export { DEFAULT_PREFS, type Prefs, TIME_ZONES, type Theme, type TimeZoneId, getPrefs, resolvedTheme, setPref, timeZoneLabel, usePrefs, ASSISTANT_WIDTH, } from './prefs.js';
33
+ export { KIT, type KitConfig, configureLumen } from './kit-config.js';
34
+ export { ConnectionBlock, ErrorLine, IntegrationRow, KeyTestLine, type KeyTestResult, TestBadge, errorText, useClock, } from './integrations.js';
35
+ export { LumenPager } from './pager.js';
36
+ export { createDeskQueryClient } from './query-client.js';
37
+ export { refreshHeaders, withRefreshToken } from './refresh.js';
38
+ export { type LumenLoad, LumenError, LumenLazy, LumenLoadFooter, LumenLoadTime, LumenProgress, LumenRefresh, LumenSkeleton, LumenStates, marketTrades, useLumenLoad, useLumenSource, useLiveLoad, } from './lumen-source.js';
39
+ export { LumenBoundary } from './lumen-boundary.js';
40
+ export { LumenPageState } from './lumen-page-state.js';
41
+ export { type FormIssue, goToIssue, SaveButton } from './form-issues.js';
42
+ export { LumenForm, type LumenFormField, lumenFormIssues, type LumenFormValue, type LumenFormValues, } from './lumen-form.js';
43
+ export { RouteError, isStaleBuildError, reloadForNewBuild, reportError, watchNewBuilds, } from './route-error.js';
44
+ export { LumenSettings, SettingRow, SettingsActions, SettingsFooter, type SettingsSectionDef, } from './settings.js';
45
+ export { LumenCard } from './summary-cards.js';
46
+ export { WhatsNew } from './whats-new.js';
47
+ export { type LumenApp, LumenAppSwitcher } from './app-switcher.js';
48
+ export { type BriefActions, type BriefChange, type ScheduleChange, type ScheduleItem, type ScheduleRun, SchedulesSection, } from './schedules.js';
49
+ export { NotificationsSettings } from './notifications.js';
50
+ export { type TasksConfig, TasksButton, TasksProvider, useTasks } from './tasks.js';
51
+ export { type McpServer, McpSettings } from './mcp-settings.js';
52
+ export { lumenToast } from './lumen-toast.js';
53
+ export { LumenShell, type LumenShellProps, type ShellUser, StatusItem, firstName, initials, maskAmount, useAmountsHidden, useApp, useShell, } from './shell.js';
54
+ export { apiJson } from './api-fetch.js';
55
+ export { cn } from './lib/utils.js';
56
+ export { type HealthProbe, StatusHealth } from './status-health.js';
57
+ export { TimeZoneSelect } from './status.js';
58
+ export { type AccountFacts, type JoinReaders, JoinsProvider, SourceStatusItems, YOURS_COLOR, editedCells, editedKey, frameColor, useFrameEdits, useJoins, usedJoinKeys, } from './joins.js';
59
+ export { OriginLegend, originsOf } from './origin-legend.js';
60
+ export { type ConnectedDomain, ConnectOffer, connectionFields, linkGroupId } from './connections.js';
61
+ export { FRESHNESS, type Freshness, FreshnessIcon, FreshnessMark, freshnessLine, freshnessOf, } from './freshness.js';
62
+ export { SourcesLine, useSourceHealth } from './source-health.js';
63
+ export { TILE_WIDTH, Tile, TileChip, type TileOption, TileRow, TileToggleGroup, } from './tile.js';
64
+ export { type FilterField, type FilterToken, LumenTokenizedFilter, type LumenFilterState, useLumenFilter, } from './lumen-filter.js';
65
+ export { useIsMobile } from './hooks/use-mobile.js';
66
+ export { VIEW_ICONS, iconOf, viewIcon } from './view-icons.js';
67
+ export { type AppViewState, useAppView } from './app-views.js';
68
+ export { type ScreenMap, type ScreenOutcome, type ScreenStatus, type WorkspaceOnScreen, type AssistantMove, type AssistantMoveHandlers, type AssistantPage, type AssistantPageType, AssistantScreenProvider, useAssistantMoves, useAssistantPages, useAssistantScreen, useAssistantWorkspace, } from './assistant-screen.js';
69
+ export { type AssistantActions, AssistantContext, type AssistantNote, type AssistantRef, type AssistantSlot, useAssistant, AssistantPendingContext, useAssistantName, useAssistantNote, useAssistantPending, } from './assistant-context.js';
70
+ export { WORKSPACES_KEY, frameKey, workspacesListQuery } from './workspace-keys.js';
71
+ export { type WorkspaceApp, type WorkspaceDraftProps, WorkspacePage, type WorkspacesConfig, WorkspacesProvider, WorkspacesSection, } from './workspaces.js';
72
+ export { CHART_INDICATORS, type ChartIndicator, type IndicatorValues, LINE_COLOR, computeIndicators, formulaIndicator, lineColor, resolveIndicator, runFormula, } from './market/indicators.js';
73
+ export { IndicatorPicker } from './market/indicator-picker.js';
74
+ export { type OptionChainField, type OptionChainRow, OptionChainTable, } from './market/option-chain.js';
75
+ export { MarketProvider, type MarketStatus, useHasMarket, useMarket, useMarketDaily, useMarketFeed, } from './market/live.js';
76
+ export { MarketBadge, MarketLines, MarketTiles, MarketToday, MarketWidget, } from './market/market-widget.js';
77
+ export { CHART_TYPES, type ChartBar, type ChartType, PriceChart, heikinAshi, } from './market/price-chart.js';
78
+ export { LumenChart, type LumenChartOptions, type MineIndicators, SymbolChartProvider, useLumenChart, useSymbolChart, } from './market/lumen-chart.js';
79
+ export { SymbolSheetProvider } from './symbol-sheet.js';
80
+ export { useSymbolSheet } from './symbol-sheet-context.js';
81
+ export { NoteLine, type NoteLineProps } from './note-line.js';
82
+ export { type LumenFinderConfig, type LumenFinderPageType, type LumenFinderRow, type LumenFinderScope, LumenFinderButton, useLumenFinder, useLumenFinderRows, useOpenLumenFinder, } from './lumen-finder.js';
83
+ export { type AssistantChat, LumenAssistantButton, LumenAssistantChats, LumenAssistantPanel, assistantOpen, tabState, useAssistantHotkey, } from './assistant-panel.js';