@columenlabs/lumen-ui 0.1.1 → 0.1.3

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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/assistant-panel.js +1 -1
  3. package/dist/coverage-calendar-map.js +1 -1
  4. package/dist/coverage-cells.d.ts +2 -2
  5. package/dist/coverage-map.js +4 -10
  6. package/dist/details-table.js +1 -5
  7. package/dist/form-issues.js +1 -1
  8. package/dist/frame-script.js +1 -1
  9. package/dist/growth-chart.d.ts +9 -9
  10. package/dist/joins.d.ts +4 -3
  11. package/dist/joins.js +1 -1
  12. package/dist/lumen-code.js +1 -5
  13. package/dist/lumen-drawer.js +1 -3
  14. package/dist/lumen-filter.js +5 -13
  15. package/dist/lumen-grid.js +2 -4
  16. package/dist/lumen-layout.js +7 -4
  17. package/dist/lumen-links.d.ts +1 -1
  18. package/dist/lumen-selector.js +1 -1
  19. package/dist/lumen-source.js +2 -4
  20. package/dist/lumen-tab-drawer.js +1 -1
  21. package/dist/lumen-toast.d.ts +4 -4
  22. package/dist/lumen-view-card.js +1 -1
  23. package/dist/lumen-view.js +2 -4
  24. package/dist/lumen-widget.d.ts +277 -171
  25. package/dist/lumen-widget.js +31 -20
  26. package/dist/market/indicator-picker.js +1 -1
  27. package/dist/market/live.d.ts +1 -1
  28. package/dist/market/lumen-chart.js +1 -1
  29. package/dist/market/price-chart.d.ts +10 -10
  30. package/dist/nav.js +1 -1
  31. package/dist/notifications.js +1 -3
  32. package/dist/overview.js +1 -1
  33. package/dist/page.js +1 -1
  34. package/dist/pager.js +1 -1
  35. package/dist/prefs.d.ts +25 -25
  36. package/dist/schedules.js +1 -1
  37. package/dist/settings.js +1 -3
  38. package/dist/shell.js +1 -1
  39. package/dist/sourced-card.d.ts +10 -0
  40. package/dist/sourced-card.js +69 -0
  41. package/dist/summary-cards.js +2 -6
  42. package/dist/symbol-sheet.js +1 -1
  43. package/dist/tasks.d.ts +1 -1
  44. package/dist/tasks.js +1 -1
  45. package/dist/tile.js +2 -4
  46. package/dist/ui/badge.d.ts +1 -1
  47. package/dist/ui/button.d.ts +2 -2
  48. package/dist/ui/chart.d.ts +2 -2
  49. package/dist/ui/chart.js +2 -5
  50. package/dist/ui/field.d.ts +1 -1
  51. package/dist/ui/input-group.d.ts +2 -2
  52. package/dist/ui/item.d.ts +3 -3
  53. package/dist/ui/sidebar.d.ts +1 -1
  54. package/dist/ui/sidebar.js +1 -1
  55. package/dist/ui/toggle.d.ts +1 -1
  56. package/dist/view-edit-cell.js +1 -4
  57. package/dist/view-editor.d.ts +1 -1
  58. package/dist/view-editor.js +188 -47
  59. package/dist/view-formulas.d.ts +1 -1
  60. package/dist/view-model.d.ts +15 -15
  61. package/dist/whats-new.js +1 -1
  62. package/dist/widget-data.d.ts +4 -0
  63. package/dist/widget-data.js +32 -0
  64. package/dist/widget-settings.d.ts +219 -139
  65. package/dist/widget-settings.js +4 -4
  66. package/dist/widget-thumbs.d.ts +1 -1
  67. package/dist/widget-thumbs.js +1 -1
  68. package/dist/workspace-keys.d.ts +1 -1
  69. package/dist/workspaces.js +1 -1
  70. package/package.json +5 -5
@@ -1,5 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { OUTLOOK_INDEXES, marketWindows } from '@columenlabs/lumen-ui-types/market';
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { OUTLOOK_INDEXES, marketWindows, } from '@columenlabs/lumen-ui-types/market';
3
3
  // LumenSavedWidget (as blocks; renamed): a page (LumenLayout) is an ordered list of cards, each a third, half, two thirds or the full width.
4
4
  // A card is the view (LumenView) or a widget. Chart and top-N widgets read every row of the view
5
5
  // (the table's filter is the table's), narrowed by their own tokens, and work their chart out here in the browser:
@@ -7,7 +7,7 @@ import { OUTLOOK_INDEXES, marketWindows } from '@columenlabs/lumen-ui-types/mark
7
7
  // cards out; this file draws the card frame every card shares (its header, ↻ and ⋯ menu), each
8
8
  // widget, the Edit layout bar, Add widget (Pre-built and Build your own) and Edit widget.
9
9
  import { ALLOCATION_MAX, VIEW_CARD_KIND_NAMES, WIDGET_STYLES, controlOn, isDollarFormat, isRowCard, isTableCard, trendBuckets, viewCardKinds, viewCardSchema, viewWidgetCatalog, widgetStyleId, widgetStyleOf, } from '@columenlabs/lumen-ui-types/views';
10
- import { ActivityIcon, ArrowDownIcon, ArrowUpIcon, BarChart3Icon, CandlestickChartIcon, ChartLineIcon, ChartPieIcon, CheckIcon, CodeIcon, CopyIcon, EyeOffIcon, FoldVerticalIcon, FunnelIcon, GripVerticalIcon, InfoIcon, LayoutGridIcon, ListOrderedIcon, MoreHorizontalIcon, PackageIcon, PencilIcon, PlusIcon, RotateCcwIcon, Rows3Icon, SparklesIcon, SquareIcon, TableIcon, Trash2Icon, TrendingUpIcon, WrenchIcon, XIcon, } from 'lucide-react';
10
+ import { ActivityIcon, ArrowDownIcon, ArrowUpIcon, BarChart3Icon, CandlestickChartIcon, ChartLineIcon, ChartPieIcon, CheckIcon, CodeIcon, CopyIcon, DatabaseIcon, EyeOffIcon, FoldVerticalIcon, FunnelIcon, GripVerticalIcon, InfoIcon, LayoutGridIcon, ListOrderedIcon, MoreHorizontalIcon, PackageIcon, PencilIcon, PlusIcon, RotateCcwIcon, Rows3Icon, SparklesIcon, SquareIcon, TableIcon, Trash2Icon, TrendingUpIcon, WrenchIcon, XIcon, } from 'lucide-react';
11
11
  import { useEffect, useMemo, useRef, useState } from 'react';
12
12
  import { useAssistant, useAssistantName } from './assistant-context.js';
13
13
  import { LumenConfirm } from './confirm.js';
@@ -15,6 +15,7 @@ import { FoldChevron, useFolded, usePageKey } from './fold.js';
15
15
  import { GrowthChart } from './growth-chart.js';
16
16
  import { useIsMobile } from './hooks/use-mobile.js';
17
17
  import { IconButton } from './icon-button.js';
18
+ import { useDomainReads } from './joins.js';
18
19
  import { cn } from './lib/utils.js';
19
20
  import { LumenBoundary } from './lumen-boundary.js';
20
21
  import { LumenTokenizedFilter, filterByTokens, useLumenFilter, } from './lumen-filter.js';
@@ -48,6 +49,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip.js';
48
49
  import { fieldOptions } from './view-formulas.js';
49
50
  import { computedOf, label, summaryCash, } from './view-model.js';
50
51
  import { WidgetControlsEditor } from './widget-controls.js';
52
+ import { PAGE_SOURCE, useWidgetDataOptions } from './widget-data.js';
51
53
  import { dateFields } from './widget-settings.js';
52
54
  /** Each style's icon, by its id in `WIDGET_STYLES`; a style left out takes its kind's. */
53
55
  const STYLE_ICONS = {
@@ -367,9 +369,7 @@ export function LumenWidget({ id, kind, icon, title, note, controls, tools, filt
367
369
  "relative after:pointer-events-none after:absolute after:inset-0 after:z-30 after:rounded-[inherit] after:border-2 after:border-primary/40 after:border-dashed after:content-['']", flat && 'border-0 bg-transparent shadow-none', className), children: [!headless && (_jsxs("div", { ref: head, className: cn('flex shrink-0 items-center gap-2 px-3', compact ? 'h-8' : 'h-10 border-b', (folded || below) && 'border-b-0', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: [layout && (_jsx(Button, { variant: "ghost", size: "icon-xs", className: "lumen-drag -ml-1 cursor-grab touch-none text-muted-foreground active:cursor-grabbing", "aria-label": `Drag ${title}`, children: _jsx(GripVerticalIcon, {}) })), link && !bare && (_jsx(LinkPicker, { color: link.color, onPick: link.onPick, label: title, className: "-mr-1" })), !compact && _jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: cn('truncate', compact ? 'text-xs text-muted-foreground' : 'text-sm font-semibold'), children: title }), link?.color && link.follows && !folded && (_jsxs("span", { className: "min-w-0 shrink truncate text-xs", style: { color: linkVar(link.color) }, title: `Follows ${link.follows}`, children: ["\u00B7 ", link.follows] })), layout && headerOff && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Badge, { variant: "secondary", className: "shrink-0 gap-1 text-muted-foreground", children: [_jsx(EyeOffIcon, {}), " Header off"] }) }), _jsx(TooltipContent, { children: "Shown only in Edit layout. Change it in Edit widget \u25B8 Controls." })] })), !layout && !bare && (_jsx(FoldChevron, { folded: folded, label: title, onToggle: toggle, className: "-ml-1" })), folded && rows != null && (_jsxs("span", { className: "shrink-0 text-xs text-muted-foreground num", children: [rows.toLocaleString(), " ", rows === 1 ? 'row' : 'rows'] })), inline && (_jsx("div", { "data-card-filter": true, className: "flex min-w-40 max-w-xl flex-1 items-center gap-2", children: filter })), note && !layout && !folded && (_jsx("span", { className: "hidden truncate text-xs text-muted-foreground sm:inline", children: note })), _jsxs("span", { className: cn('ml-auto flex shrink-0 items-center gap-1', bare && 'hidden'), children: [!layout && load?.updated && _jsx(LumenFreshness, { load: load }), !layout && !folded && tools, showFilter && mobile && link?.color && !!link.linked && (_jsxs("span", { className: "num flex items-center gap-1 text-xs text-muted-foreground", title: `${link.linked} linked`, children: [_jsx(LinkDot, { color: link.color, className: "size-2" }), link.linked] })), showFilter && mobile && (_jsxs("span", { className: "relative", children: [_jsx(IconButton, { icon: FunnelIcon, label: filterOpen ? 'Hide filter' : 'Filter', "aria-expanded": filterOpen, className: cn(filterOpen && 'text-primary'), onClick: () => setFilterOpen(!filterOpen) }), filterCount > 0 && (_jsx(Badge, { className: "num pointer-events-none absolute -top-1 -right-1 h-4 min-w-4 px-1 text-[10px]", children: filterCount }))] })), !layout && !folded && help && _jsx(CardHelp, { title: title, children: help }), !layout && load && on('refresh') && (_jsx(LumenRefresh, { load: load, label: `Refresh ${title}` })), (controls?.editable || (!layout && (ask || load?.editScript))) && (_jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(IconButton, { icon: MoreHorizontalIcon, label: `${title} menu` }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-44", children: [ask && !layout && (_jsxs(DropdownMenuItem, { onSelect: ask, children: [_jsx(SparklesIcon, {}), " Ask ", assistantName] })), load?.editScript && !layout && (_jsxs(DropdownMenuItem, { onSelect: load.editScript, children: [_jsx(CodeIcon, {}), " Script"] })), (ask || load?.editScript) && !layout && controls?.editable && (_jsx(DropdownMenuSeparator, {})), controls?.editable && (_jsxs(_Fragment, { children: [!controls.fixed && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: () => controls.onAction('edit'), children: [_jsx(PencilIcon, {}), " Edit widget"] }), _jsxs(DropdownMenuItem, { disabled: !own, onSelect: () => controls.onAction('duplicate'), children: [_jsx(CopyIcon, {}), " Duplicate"] })] })), _jsxs(DropdownMenuItem, { disabled: controls.first, onSelect: () => controls.onAction('up'), children: [_jsx(ArrowUpIcon, {}), " Move up"] }), _jsxs(DropdownMenuItem, { disabled: controls.last, onSelect: () => controls.onAction('down'), children: [_jsx(ArrowDownIcon, {}), " Move down"] }), controls.onFit && (_jsxs(DropdownMenuItem, { onSelect: controls.onFit, children: [_jsx(FoldVerticalIcon, {}), " Fit height"] })), !controls.fixed && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "text-muted-foreground focus:text-destructive", onSelect: () => setRemoving(true), children: [_jsx(Trash2Icon, {}), " Remove"] })] }))] }))] })] }))] })] })), below && !headless && (_jsx("div", { "data-card-filter": true, className: cn('flex items-center gap-2 border-b px-3 pb-2', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: filter })), strip && !layout && !folded && !headless ? (_jsx("div", { "data-card-strip": true, className: cn('flex min-w-0 items-center gap-2 border-b px-3 py-1.5 empty:hidden', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: strip })) : null, _jsx("div", { hidden: folded, className: cn('relative min-w-0',
368
370
  // On the grid a card fills its cell: with a height of its own its body scrolls; one as
369
371
  // tall as it draws keeps its body's height, so the grid can measure it.
370
- !cell ? 'flex-1' : cell.fixed && 'min-h-0 flex-1 overflow-auto', pad && !table && !flat && (compact ? 'px-3 pb-3' : 'p-3')), children: _jsx(LumenBoundary, { where: id, children: children }) }), !layout && !folded && load && on('footer') && (
371
- // The load line sits at the card's foot, whatever the body's height.
372
- _jsx("div", { className: cn('mt-auto shrink-0', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: _jsx(LumenLoadFooter, { load: load, rows: rows, note: loadNote }) })), controls && (_jsx(LumenConfirm, { open: removing, onOpenChange: setRemoving, title: `Remove ${title}?`, description: table ? 'Add widget brings a table back.' : 'Add widget brings it back.', action: "Remove", onConfirm: () => controls.onAction('remove') }))] }) }));
372
+ !cell ? 'flex-1' : cell.fixed && 'min-h-0 flex-1 overflow-auto', pad && !table && !flat && (compact ? 'px-3 pb-3' : 'p-3')), children: _jsx(LumenBoundary, { where: id, children: children }) }), !layout && !folded && load && on('footer') && (_jsx("div", { className: cn('mt-auto shrink-0', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: _jsx(LumenLoadFooter, { load: load, rows: rows, note: loadNote }) })), controls && (_jsx(LumenConfirm, { open: removing, onOpenChange: setRemoving, title: `Remove ${title}?`, description: table ? 'Add widget brings a table back.' : 'Add widget brings it back.', action: "Remove", onConfirm: () => controls.onAction('remove') }))] }) }));
373
373
  }
374
374
  /**
375
375
  * One widget in its card. Widgets over the view's rows (tiles,
@@ -403,9 +403,7 @@ export function LumenSavedWidget({ widget, title, domain, rows, fields, merge, o
403
403
  if (widget.kind === 'chart' && widget.style === 'market')
404
404
  return (_jsx(LumenWidget, { ...frame, children: _jsx(LumenLazy, { children: _jsx(MarketWidget, { symbols: widget.symbols, style: widget.layout }) }) }));
405
405
  const body = row || number ? (tiles) : (_jsx(WidgetBody, { card: widget, domain: domain, rows: rows, fields: fields, merge: merge, onDetails: onDetails }));
406
- return (_jsx(LumenWidget, { ...frame, pad: widgetStyleId(widget) !== 'table:top_n', load: load, loadNote: "from the view's rows", children: load ? (
407
- // A failed view says so once, in its table; its widgets just wait for its rows.
408
- _jsx(LumenStates, { load: { ...load, error: null }, empty: load.error ? _jsx(Empty, { children: "Shows once the view's rows load." }) : undefined, kind: "widget", skeleton: row || number ? body : _jsx(Skeleton, { className: "h-40 w-full" }), children: body })) : (body) }));
406
+ return (_jsx(LumenWidget, { ...frame, pad: widgetStyleId(widget) !== 'table:top_n', load: load, loadNote: "from the view's rows", children: load ? (_jsx(LumenStates, { load: { ...load, error: null }, empty: load.error ? _jsx(Empty, { children: "Shows once the view's rows load." }) : undefined, kind: "widget", skeleton: row || number ? body : _jsx(Skeleton, { className: "h-40 w-full" }), children: body })) : (body) }));
409
407
  }
410
408
  /** Growth of $1: the domain's lines for the shown rows, its bands and markers. */
411
409
  function GrowthCard({ widget, frame, domain, rows, load, }) {
@@ -517,13 +515,26 @@ function PickCard({ icon: Icon, name, note, added, disabled, onClick, }) {
517
515
  return (_jsxs(Button, { variant: "outline", disabled: disabled, onClick: onClick, className: "h-auto justify-start gap-3 p-3 text-left font-normal whitespace-normal", children: [_jsx("span", { className: "grid size-8 shrink-0 place-items-center rounded-md bg-primary/10 text-primary", children: _jsx(Icon, { className: "size-4" }) }), _jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [_jsx("span", { className: "font-medium", children: name }), _jsx("span", { className: "text-xs text-muted-foreground", children: note })] }), added && (_jsxs(Badge, { variant: "secondary", className: "ml-auto font-normal", children: [_jsx(CheckIcon, {}), " Added"] }))] }));
518
516
  }
519
517
  /** Add widget: the domain's pre-built widgets, or a kind to build. */
520
- export function AddWidgetDialog({ open, onOpenChange, domain, cards, onAdd, onBuild, }) {
521
- const catalog = viewWidgetCatalog[domain.id] ?? [];
518
+ export function AddWidgetDialog({ open, onOpenChange, domain, cards, onAdd, onBuild, name = 'This page', }) {
519
+ // Step 1: the widget's main data, this page's rows unless another is picked.
520
+ const [data, setData] = useState(PAGE_SOURCE);
521
+ // biome-ignore lint/correctness/useExhaustiveDependencies: starts on this page each time it opens.
522
+ useEffect(() => setData(PAGE_SOURCE), [open]);
523
+ const pageLabel = `${name} (this page)`;
524
+ const dataOptions = useWidgetDataOptions(open, pageLabel);
525
+ const own = data !== PAGE_SOURCE;
526
+ const ids = useMemo(() => (own ? [data] : []), [own, data]);
527
+ const read = useDomainReads(ids, ids)[data];
528
+ const shapeDomain = (own ? (read?.domain ?? { id: data, rowKey: String, fields: [] }) : domain);
529
+ const sourced = (c) => (own ? { ...c, source: data } : c);
530
+ const catalog = own ? [] : (viewWidgetCatalog[domain.id] ?? []);
522
531
  const keys = new Set(domain.fields.map((f) => f.key));
523
- const hasRow = cards.some(isRowCard);
532
+ const hasRow = !own && cards.some(isRowCard);
524
533
  const full = cards.length >= 12;
525
- const offered = useOffered(domain);
526
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-2xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Add widget" }), _jsxs(DialogDescription, { children: ["Widgets use this view's rows and their own filter", full ? '. A page holds 12 cards at most.' : '.'] })] }), _jsxs(Tabs, { defaultValue: catalog.length ? 'prebuilt' : 'own', children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "prebuilt", children: [_jsx(PackageIcon, {}), "Pre-built"] }), _jsxs(TabsTrigger, { value: "own", children: [_jsx(WrenchIcon, {}), "Build your own"] })] }), _jsx(TabsContent, { value: "prebuilt", className: dialogBody, children: catalog.length ? (_jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: catalog.map((it) => {
534
+ const offered = useOffered(shapeDomain);
535
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-2xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Add widget" }), _jsxs(DialogDescription, { children: ["Pick its data, then its shape", full ? '. A page holds 12 cards at most.' : '.'] })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "add-widget-data", className: "text-xs font-medium text-muted-foreground", children: "Data" }), _jsx(LumenSelector, { id: "add-widget-data", icon: DatabaseIcon, label: "Widget data", search: true, value: data, options: dataOptions, onChange: setData }), own && read?.state !== 'ok' && (_jsx("p", { className: cn('text-xs', read?.state === 'loading' || !read ? 'text-muted-foreground' : 'text-destructive'), children: read?.state === 'loading' || !read
536
+ ? 'Reading its fields…'
537
+ : (read.detail ?? 'Its rows could not be read.') }))] }), _jsxs(Tabs, { defaultValue: catalog.length ? 'prebuilt' : 'own', children: [_jsxs(TabsList, { children: [!own && (_jsxs(TabsTrigger, { value: "prebuilt", children: [_jsx(PackageIcon, {}), "Pre-built"] })), _jsxs(TabsTrigger, { value: "own", children: [_jsx(WrenchIcon, {}), "Build your own"] })] }), _jsx(TabsContent, { value: "prebuilt", className: dialogBody, children: catalog.length ? (_jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: catalog.map((it) => {
527
538
  const added = cards.some((b) => b.id === it.widget.id);
528
539
  const fits = [
529
540
  ...('by' in it.widget ? [it.widget.by, it.widget.value] : []),
@@ -540,16 +551,16 @@ export function AddWidgetDialog({ open, onOpenChange, domain, cards, onAdd, onBu
540
551
  // A table goes on as it is; the page's first row of numbers shows the
541
552
  // view's cards, any other starts empty in Edit widget.
542
553
  const ready = w.id === 'table:table' || (w.id === 'metric:row' && !hasRow);
543
- return (_jsx(PickCard, { icon: cardIcon(w), name: w.name, note: w.note, disabled: full, onClick: () => ready
544
- ? onAdd(draftOf(w.id, domain, cards))
545
- : onBuild(w.id === 'metric:row'
554
+ return (_jsx(PickCard, { icon: cardIcon(w), name: w.name, note: w.note, disabled: full || (own && read?.state !== 'ok'), onClick: () => ready
555
+ ? onAdd(sourced(draftOf(w.id, shapeDomain, cards)))
556
+ : onBuild(sourced(w.id === 'metric:row'
546
557
  ? viewCardSchema.parse({
547
- ...draftOf(w.id, domain, cards),
558
+ ...draftOf(w.id, shapeDomain, cards),
548
559
  tiles: [],
549
560
  })
550
- : draftOf(w.id, domain, cards)) }, w.id));
561
+ : draftOf(w.id, shapeDomain, cards))) }, w.id));
551
562
  }) })] }, kind));
552
- }) })] })] }) }));
563
+ }) })] }, data)] }) }));
553
564
  }
554
565
  /** A page's cards after `action` on card `i`, or null for Edit (the caller opens a dialog). */
555
566
  export function cardsAfter(cards, i, action) {
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { parseChartFormula } from '@columenlabs/lumen-ui-types/chart-formula';
3
3
  import { PlusIcon, SaveIcon, Trash2Icon } from 'lucide-react';
4
4
  import { useState } from 'react';
@@ -108,10 +108,10 @@ export declare function useMarket(symbols: string[]): {
108
108
  };
109
109
  /** Daily breadth and macro series of `keys` (never live); `error` when they could not be read. */
110
110
  export declare function useMarketDaily(keys: string[]): {
111
- retry: () => void;
112
111
  series: Record<string, MarketSeries>;
113
112
  loading: boolean;
114
113
  error: string | null;
114
+ retry: () => void;
115
115
  };
116
116
  /** True where a market provider is mounted (an app, suite or not). */
117
117
  export declare const useHasMarket: () => boolean;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { MARKET_SYMBOLS, isDailyMarketKey, marketWindows, } from '@columenlabs/lumen-ui-types/market';
3
3
  import { queryOptions, useQueries, useQuery, useQueryClient } from '@tanstack/react-query';
4
4
  import { XIcon } from 'lucide-react';
@@ -18,20 +18,20 @@ export interface ChartBar {
18
18
  }
19
19
  /** How the price is drawn: the basics every charting tool offers. */
20
20
  export declare const CHART_TYPES: readonly [{
21
- readonly id: "candles";
22
- readonly label: "Candles";
21
+ readonly id: 'candles';
22
+ readonly label: 'Candles';
23
23
  }, {
24
- readonly id: "ohlc";
25
- readonly label: "OHLC bars";
24
+ readonly id: 'ohlc';
25
+ readonly label: 'OHLC bars';
26
26
  }, {
27
- readonly id: "line";
28
- readonly label: "Line";
27
+ readonly id: 'line';
28
+ readonly label: 'Line';
29
29
  }, {
30
- readonly id: "area";
31
- readonly label: "Area";
30
+ readonly id: 'area';
31
+ readonly label: 'Area';
32
32
  }, {
33
- readonly id: "heikin";
34
- readonly label: "Heikin-Ashi";
33
+ readonly id: 'heikin';
34
+ readonly label: 'Heikin-Ashi';
35
35
  }];
36
36
  export type ChartType = (typeof CHART_TYPES)[number]['id'];
37
37
  /** Heikin-Ashi candles: each bar averages itself with the one before, which smooths the trend. */
package/dist/nav.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // Sidebar building blocks an app composes its nav from (shadcn NavMain pattern): a group of links
3
3
  // that may nest one level (chevron on the right, hidden in icon mode), and a section whose items a
4
4
  // person adds with `+` and removes with the trash icon. Every item carries an icon; section headers are bold caps without one.
@@ -128,9 +128,7 @@ function Webhooks({ hooks, api }) {
128
128
  e.preventDefault();
129
129
  if (name.trim() && url.trim())
130
130
  add.mutate();
131
- }, children: [_jsx(Field, { id: "wh-name", label: "Name", children: _jsx(Input, { id: "wh-name", placeholder: "Discord", value: name, onChange: (e) => setName(e.target.value) }) }), _jsx(Field, { id: "wh-url", label: "Address (https)", children: _jsx(Input, { id: "wh-url", className: "num", autoComplete: "off", placeholder: "https://", value: url, onChange: (e) => setUrl(e.target.value) }) }), headers.length ? (_jsx(Field, { id: "wh-header-0", label: "Headers (sent with each post; hidden once saved)", children: _jsx("div", { className: "grid grid-cols-1 gap-2", children: headers.map((h, i) => (
132
- // biome-ignore lint/suspicious/noArrayIndexKey: rows have no id until saved
133
- _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { id: `wh-header-${i}`, className: "num w-2/5", autoComplete: "off", placeholder: "Authorization", "aria-label": "Header name", value: h.name, onChange: (e) => setHeader(i, 'name', e.target.value) }), _jsx(Input, { className: "num min-w-0 flex-1", type: "password", autoComplete: "off", placeholder: "Value", "aria-label": "Header value", value: h.value, onChange: (e) => setHeader(i, 'value', e.target.value) }), _jsx(IconButton, { type: "button", icon: XIcon, label: "Remove header", onClick: () => setHeaders((hs) => hs.filter((_, j) => j !== i)) })] }, i))) }) })) : null, _jsx(ErrorLine, { error: add.error }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [headers.length < 5 ? (_jsx(IconButton, { type: "button", icon: PlusIcon, label: "Add header", variant: "outline", onClick: () => setHeaders((hs) => [...hs, { name: '', value: '' }]) })) : null, _jsx("span", { className: "flex-1" }), _jsxs(Button, { type: "submit", size: "sm", disabled: !name.trim() || !url.trim() || add.isPending, children: [add.isPending ? _jsx(RefreshCwIcon, { className: "animate-spin" }) : _jsx(PlusIcon, {}), "Add"] })] })] }) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Each post is ", _jsx("span", { className: "num", children: '{ event, title, message, click, at }' }), ". Messages name no account, holding or amount. For a Cloudflare Access service token, add the headers CF-Access-Client-Id and CF-Access-Client-Secret."] })] }));
131
+ }, children: [_jsx(Field, { id: "wh-name", label: "Name", children: _jsx(Input, { id: "wh-name", placeholder: "Discord", value: name, onChange: (e) => setName(e.target.value) }) }), _jsx(Field, { id: "wh-url", label: "Address (https)", children: _jsx(Input, { id: "wh-url", className: "num", autoComplete: "off", placeholder: "https://", value: url, onChange: (e) => setUrl(e.target.value) }) }), headers.length ? (_jsx(Field, { id: "wh-header-0", label: "Headers (sent with each post; hidden once saved)", children: _jsx("div", { className: "grid grid-cols-1 gap-2", children: headers.map((h, i) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { id: `wh-header-${i}`, className: "num w-2/5", autoComplete: "off", placeholder: "Authorization", "aria-label": "Header name", value: h.name, onChange: (e) => setHeader(i, 'name', e.target.value) }), _jsx(Input, { className: "num min-w-0 flex-1", type: "password", autoComplete: "off", placeholder: "Value", "aria-label": "Header value", value: h.value, onChange: (e) => setHeader(i, 'value', e.target.value) }), _jsx(IconButton, { type: "button", icon: XIcon, label: "Remove header", onClick: () => setHeaders((hs) => hs.filter((_, j) => j !== i)) })] }, i))) }) })) : null, _jsx(ErrorLine, { error: add.error }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [headers.length < 5 ? (_jsx(IconButton, { type: "button", icon: PlusIcon, label: "Add header", variant: "outline", onClick: () => setHeaders((hs) => [...hs, { name: '', value: '' }]) })) : null, _jsx("span", { className: "flex-1" }), _jsxs(Button, { type: "submit", size: "sm", disabled: !name.trim() || !url.trim() || add.isPending, children: [add.isPending ? _jsx(RefreshCwIcon, { className: "animate-spin" }) : _jsx(PlusIcon, {}), "Add"] })] })] }) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Each post is ", _jsx("span", { className: "num", children: '{ event, title, message, click, at }' }), ". Messages name no account, holding or amount. For a Cloudflare Access service token, add the headers CF-Access-Client-Id and CF-Access-Client-Secret."] })] }));
134
132
  }
135
133
  // ── Events: one row per event, one column per target ──
136
134
  function EventCell({ event, target, name, api, }) {
package/dist/overview.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // An app's Overview: a service-health page, not a decisions page. A stage map
3
3
  // (each stage a chip with its status dot, name and a one-line verdict; arrows between; tap one to
4
4
  // focus it) and then each stage's widgets, one LumenLayout card each with its own ↻ and states
package/dist/page.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // The page frame: a fixed-height header (sidebar toggle, breadcrumb, actions)
3
3
  // and the body, with the shell's gutter, section gap and max width. Pages never set their own outer
4
4
  // padding or header. On phone the actions stay in the bar as icons and a back chevron replaces the trail.
package/dist/pager.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // The compact pager, the same look as LumenView's: the count opens the page sizes,
3
3
  // and the chevrons turn the page while there is more than one. Coverage views page their rows with it.
4
4
  import { viewPageSizes } from '@columenlabs/lumen-ui-types/views';
package/dist/prefs.d.ts CHANGED
@@ -1,46 +1,46 @@
1
- export declare const THEMES: readonly ["light", "dark", "system"];
1
+ export declare const THEMES: readonly ['light', 'dark', 'system'];
2
2
  export type Theme = (typeof THEMES)[number];
3
3
  export declare const TIME_ZONES: readonly [{
4
- readonly id: "America/New_York";
5
- readonly label: "Eastern";
4
+ readonly id: 'America/New_York';
5
+ readonly label: 'Eastern';
6
6
  }, {
7
- readonly id: "America/Chicago";
8
- readonly label: "Central";
7
+ readonly id: 'America/Chicago';
8
+ readonly label: 'Central';
9
9
  }, {
10
- readonly id: "America/Los_Angeles";
11
- readonly label: "Pacific";
10
+ readonly id: 'America/Los_Angeles';
11
+ readonly label: 'Pacific';
12
12
  }, {
13
- readonly id: "UTC";
14
- readonly label: "UTC";
13
+ readonly id: 'UTC';
14
+ readonly label: 'UTC';
15
15
  }];
16
16
  export type TimeZoneId = (typeof TIME_ZONES)[number]['id'];
17
17
  /** The text font; Manrope is the suite's own, the rest are for reading comfort. */
18
18
  export declare const FONTS: readonly [{
19
- readonly id: "manrope";
20
- readonly label: "Manrope";
19
+ readonly id: 'manrope';
20
+ readonly label: 'Manrope';
21
21
  }, {
22
- readonly id: "inter";
23
- readonly label: "Inter";
22
+ readonly id: 'inter';
23
+ readonly label: 'Inter';
24
24
  }, {
25
- readonly id: "plex";
26
- readonly label: "IBM Plex Sans";
25
+ readonly id: 'plex';
26
+ readonly label: 'IBM Plex Sans';
27
27
  }, {
28
- readonly id: "system";
29
- readonly label: "System";
28
+ readonly id: 'system';
29
+ readonly label: 'System';
30
30
  }];
31
31
  export type FontId = (typeof FONTS)[number]['id'];
32
32
  /** Text size: the root size every rem scales from, so spacing grows with the text. */
33
33
  export declare const FONT_SIZES: readonly [{
34
- readonly id: "small";
35
- readonly label: "Small";
34
+ readonly id: 'small';
35
+ readonly label: 'Small';
36
36
  readonly px: 15;
37
37
  }, {
38
- readonly id: "default";
39
- readonly label: "Default";
38
+ readonly id: 'default';
39
+ readonly label: 'Default';
40
40
  readonly px: 16;
41
41
  }, {
42
- readonly id: "large";
43
- readonly label: "Large";
42
+ readonly id: 'large';
43
+ readonly label: 'Large';
44
44
  readonly px: 17;
45
45
  }];
46
46
  export type FontSizeId = (typeof FONT_SIZES)[number]['id'];
@@ -63,9 +63,9 @@ export declare const ASSISTANT_WIDTH: {
63
63
  /** Eastern by default, as an app's zone picker had it (the market's own zone). */
64
64
  export declare const DEFAULT_PREFS: Prefs;
65
65
  /** The theme on screen: `system` follows the device. */
66
- export declare const resolvedTheme: (p?: Prefs) => "light" | "dark";
66
+ export declare const resolvedTheme: (p?: Prefs) => 'light' | 'dark';
67
67
  export declare function setPref<K extends keyof Prefs>(key: K, value: Prefs[K]): void;
68
68
  export declare const getPrefs: () => Prefs;
69
69
  export declare function usePrefs(): Prefs;
70
70
  /** `Pacific`, `UTC`: the short name shown next to times. */
71
- export declare const timeZoneLabel: (id: TimeZoneId) => "America/New_York" | "Eastern" | "America/Chicago" | "Central" | "America/Los_Angeles" | "Pacific" | "UTC";
71
+ export declare const timeZoneLabel: (id: TimeZoneId) => "America/Chicago" | "America/Los_Angeles" | "America/New_York" | "Central" | "Eastern" | "Pacific" | "UTC";
package/dist/schedules.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { AlertTriangleIcon, CheckIcon, ListIcon, MessageSquareIcon, PlayIcon, PlusIcon, RefreshCwIcon, Trash2Icon, } from 'lucide-react';
3
3
  import { Fragment, useState } from 'react';
4
4
  import { useAssistant, useAssistantName } from './assistant-context.js';
package/dist/settings.js CHANGED
@@ -147,9 +147,7 @@ export function LumenSettings({ open, section, onOpen, onClose, appLabel, profil
147
147
  ...(sections.length ? [{ label: appLabel, items: sections.filter(shown) }] : []),
148
148
  ].filter((g) => g.items.length);
149
149
  const search = (_jsxs(InputGroup, { className: "bg-background", children: [_jsx(InputGroupAddon, { children: _jsx(SearchIcon, {}) }), _jsx(InputGroupInput, { type: "search", className: "[&::-webkit-search-cancel-button]:hidden", "aria-label": "Search settings", placeholder: "Search", value: query, onChange: (e) => setQuery(e.target.value) }), query && (_jsx(InputGroupAddon, { align: "inline-end", children: _jsx(InputGroupButton, { size: "icon-xs", "aria-label": "Clear search", onClick: () => setQuery(''), children: _jsx(XIcon, {}) }) }))] }));
150
- const header = (s, slot) => (
151
- // Actions sit at the bottom right, in line with the description.
152
- _jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsxs("h3", { id: `settings-${s.id}-title`, className: "flex items-center gap-2 text-base font-semibold", children: [_jsx(s.icon, { className: "size-4 text-muted-foreground" }), s.title] }), s.description && (_jsx("p", { className: "truncate text-sm text-muted-foreground", title: typeof s.description === 'string' ? s.description : undefined, children: s.description }))] }), _jsx("div", { ref: slot, className: "flex shrink-0 items-center gap-2 empty:hidden" })] }));
150
+ const header = (s, slot) => (_jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsxs("h3", { id: `settings-${s.id}-title`, className: "flex items-center gap-2 text-base font-semibold", children: [_jsx(s.icon, { className: "size-4 text-muted-foreground" }), s.title] }), s.description && (_jsx("p", { className: "truncate text-sm text-muted-foreground", title: typeof s.description === 'string' ? s.description : undefined, children: s.description }))] }), _jsx("div", { ref: slot, className: "flex shrink-0 items-center gap-2 empty:hidden" })] }));
153
151
  return (_jsx(Dialog, { open: open, onOpenChange: (o) => {
154
152
  if (o)
155
153
  onOpen(section);
package/dist/shell.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // The frame every app renders in, from the shadcn sidebar-07 block: the app's
3
3
  // brand (or an app switcher) top-left, its own nav, the profile bottom-left; collapses to icons on
4
4
  // desktop and becomes a drawer on phone. An app passes its id, nav and status items; pages render in
@@ -0,0 +1,10 @@
1
+ import { type ViewWidget } from '@columenlabs/lumen-ui-types/views';
2
+ import { type CardControls } from './lumen-widget.js';
3
+ export declare function SourcedCard({ card, name, controls, }: {
4
+ card: ViewWidget & {
5
+ source: string;
6
+ };
7
+ /** The page's name, for a widget with no title of its own. */
8
+ name: string;
9
+ controls?: CardControls;
10
+ }): import("react").JSX.Element;
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // A widget with its own main data (`source` on the card): it reads that domain or recipe itself
3
+ // (the same reads a link uses), joins its own links, and draws like any widget over those rows. It
4
+ // shares nothing of the page's rows; the page's chips reach it only by a key both carry.
5
+ import { isRowCard, isTableCard, sourceConfigOf, tilesOfCard, } from '@columenlabs/lumen-ui-types/views';
6
+ import { useMemo } from 'react';
7
+ import { useDomainReads, useJoins } from './joins.js';
8
+ import { filterByTokens } from './lumen-filter.js';
9
+ import { followedBy, followsText, keyFilterFields, useLinks } from './lumen-links.js';
10
+ import { mergeRows } from './lumen-view.js';
11
+ import { LumenViewCard } from './lumen-view-card.js';
12
+ import { LumenSavedWidget, cardFilterFields, cardTitle } from './lumen-widget.js';
13
+ import { LumenCard, SummaryTiles } from './summary-cards.js';
14
+ import { summaryCash, withViewFields } from './view-model.js';
15
+ /** A source read's state as the card's loading state. */
16
+ function loadOf(state, detail, label) {
17
+ const failed = state === 'failed' || state === 'off';
18
+ return {
19
+ loading: state === 'loading',
20
+ fetching: state === 'loading',
21
+ refreshing: false,
22
+ background: false,
23
+ error: failed ? new Error(`${label}: ${detail ?? 'could not be read'}`) : null,
24
+ stale: null,
25
+ refresh: async () => { },
26
+ tookMs: null,
27
+ cached: false,
28
+ at: null,
29
+ };
30
+ }
31
+ export function SourcedCard({ card, name, controls, }) {
32
+ const ids = useMemo(() => [card.source], [card.source]);
33
+ const read = useDomainReads(ids, ids)[card.source];
34
+ const base = (read?.domain ?? {
35
+ id: card.source,
36
+ rowKey: String,
37
+ fields: [],
38
+ });
39
+ const rows = (read?.rows ?? []);
40
+ const config = useMemo(() => sourceConfigOf(card, name), [card, name]);
41
+ const load = loadOf(read?.state ?? 'loading', read?.detail, read?.label ?? card.source);
42
+ if (isTableCard(card))
43
+ return (_jsx(LumenViewCard, { id: card.id, domain: base, config: config, rows: card.filter.length
44
+ ? filterByTokens(rows, cardFilterFields(base.fields), card.filter)
45
+ : rows, load: load, title: card.title ?? read?.label ?? name, controls: controls, shown: card.controls }));
46
+ return (_jsx(SourcedWidget, { card: card, base: base, rows: rows, load: load, label: read?.label ?? name, controls: controls }));
47
+ }
48
+ function SourcedWidget({ card, base, rows, load, label, controls, }) {
49
+ const config = useMemo(() => sourceConfigOf(card, label), [card, label]);
50
+ const joins = useJoins(base, rows, config);
51
+ const domain = useMemo(() => withViewFields(joins.domain, config), [joins.domain, config]);
52
+ const fields = useMemo(() => [...cardFilterFields(domain.fields), ...keyFilterFields(domain.keys)], [domain.fields, domain.keys]);
53
+ const links = useLinks();
54
+ const follows = followedBy(links?.follows(card.id) ?? [], fields, domain.keys);
55
+ const own = card.filter.length ? filterByTokens(rows, fields, card.filter, domain.text) : rows;
56
+ const drawn = follows.length ? filterByTokens(own, fields, follows, domain.text) : own;
57
+ const narrowed = card.filter.length > 0 || follows.length > 0;
58
+ const counted = domain.cash && !summaryCash(isRowCard(card) ? card : {}, narrowed)
59
+ ? drawn.filter((r) => !domain.cash?.(r))
60
+ : drawn;
61
+ const loading = load.loading;
62
+ return (_jsx(LumenSavedWidget, { widget: card, title: card.title ?? cardTitle(card, label), domain: domain, rows: drawn, link: links
63
+ ? {
64
+ color: links.colorOf(card.id),
65
+ onPick: (c) => links.setColor(card.id, c),
66
+ follows: followsText(follows),
67
+ }
68
+ : undefined, fields: fields, merge: (rs) => mergeRows(domain, rs, null), load: load, tiles: 'tile' in card ? (_jsx(LumenCard, { domain: domain, tile: card.tile, rows: counted, fields: fields, loading: loading, style: card.style, target: card.target, meter: card.meter, bands: card.bands })) : isRowCard(card) ? (_jsx(SummaryTiles, { domain: domain, tiles: tilesOfCard(config, card), rows: counted, fields: fields, loading: loading })) : null, controls: controls }));
69
+ }
@@ -85,9 +85,7 @@ function OwnTile({ domain, tile: t, rows, fields, }) {
85
85
  if (!figs)
86
86
  return null;
87
87
  const v = figs.main.value;
88
- return (_jsx(Tile, { label: t.label, value: _jsx(FigureText, { f: figs.main }), valueClassName: cn(t.sign && typeof v === 'number' && (v > 0 ? 'text-gain' : v < 0 ? 'text-loss' : '')), lines: figs.lines.map((l, i) => (
89
- // biome-ignore lint/suspicious/noArrayIndexKey: a card's lines in their saved order
90
- _jsx(FigureText, { f: l }, i))) }));
88
+ return (_jsx(Tile, { label: t.label, value: _jsx(FigureText, { f: figs.main }), valueClassName: cn(t.sign && typeof v === 'number' && (v > 0 ? 'text-gain' : v < 0 ? 'text-loss' : '')), lines: figs.lines.map((l, i) => (_jsx(FigureText, { f: l }, i))) }));
91
89
  }
92
90
  /**
93
91
  * A LumenCard's number and lines: its frame shows the label, so the body is
@@ -109,9 +107,7 @@ export function LumenCard({ domain, tile: t, rows, fields, drawn, loading, style
109
107
  const of = target ? (_jsx(ViewValueCell, { value: target, format: figs.main.format, amount: figs.main.amount })) : null;
110
108
  if (style === 'progress_circle')
111
109
  return (_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx(ProgressRing, { value: share, tone: tone }), _jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [_jsx("span", { className: "num truncate text-xl leading-7 font-semibold", children: _jsx(FigureText, { f: figs.main }) }), _jsx("span", { className: "num truncate text-xs text-muted-foreground", children: of ? _jsxs(_Fragment, { children: ["of ", of] }) : 'Set a target in Edit widget' })] })] }));
112
- return (_jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [_jsx("span", { className: cn('num truncate text-2xl leading-8 font-semibold', t.sign && typeof v === 'number' && (v > 0 ? 'text-gain' : v < 0 ? 'text-loss' : '')), children: _jsx(FigureText, { f: figs.main }) }), style === 'progress_bar' && (_jsxs("div", { className: "flex flex-col gap-1 pt-1", children: [_jsx(Progress, { value: share ?? 0, className: cn('h-2', tone && TONE_BAR[tone]), "aria-label": "Progress to target" }), _jsx("span", { className: "num text-xs text-muted-foreground", children: of ? (_jsxs(_Fragment, { children: [share === null ? '—' : `${share.toFixed(0)}%`, " of ", of] })) : ('Set a target in Edit widget') })] })), figs.lines.map((l, i) => (
113
- // biome-ignore lint/suspicious/noArrayIndexKey: a card's lines in their saved order
114
- _jsx("span", { className: "num line-clamp-2 text-xs text-muted-foreground", children: _jsx(FigureText, { f: l }) }, i)))] }));
110
+ return (_jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [_jsx("span", { className: cn('num truncate text-2xl leading-8 font-semibold', t.sign && typeof v === 'number' && (v > 0 ? 'text-gain' : v < 0 ? 'text-loss' : '')), children: _jsx(FigureText, { f: figs.main }) }), style === 'progress_bar' && (_jsxs("div", { className: "flex flex-col gap-1 pt-1", children: [_jsx(Progress, { value: share ?? 0, className: cn('h-2', tone && TONE_BAR[tone]), "aria-label": "Progress to target" }), _jsx("span", { className: "num text-xs text-muted-foreground", children: of ? (_jsxs(_Fragment, { children: [share === null ? '—' : `${share.toFixed(0)}%`, " of ", of] })) : ('Set a target in Edit widget') })] })), figs.lines.map((l, i) => (_jsx("span", { className: "num line-clamp-2 text-xs text-muted-foreground", children: _jsx(FigureText, { f: l }) }, i)))] }));
115
111
  }
116
112
  /** A meter's tone on a bar's fill (theme tokens, no glow). */
117
113
  const TONE_BAR = {
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // The Symbol sheet (the assistant drafts step 1): a tap on any symbol in any view opens one sheet
3
3
  // about it, the same drawer as a row's details (bottom on phone, right on desktop). The shell draws
4
4
  // its header and LumenChart; the app adds its own sections
package/dist/tasks.d.ts CHANGED
@@ -23,7 +23,7 @@ export declare function useTasks(): TasksState | null;
23
23
  export declare function TasksProvider({ config, children }: {
24
24
  config?: TasksConfig;
25
25
  children: ReactNode;
26
- }): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
26
+ }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
27
27
  /** The header button: a ring while anything runs, the count, a gold dot for ended unseen tasks. */
28
28
  export declare function TasksButton(): import("react").JSX.Element | null;
29
29
  export {};
package/dist/tasks.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { isActive } from '@columenlabs/lumen-ui-types/tasks';
3
3
  import { useQueryClient } from '@tanstack/react-query';
4
4
  import { useRouter } from '@tanstack/react-router';
package/dist/tile.js CHANGED
@@ -1,11 +1,9 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from './lib/utils.js';
3
3
  import { Card } from './ui/card.js';
4
4
  import { ToggleGroup, ToggleGroupItem } from './ui/toggle-group.js';
5
5
  function TileBody({ label, value, caption, lines, valueClassName, }) {
6
- return (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-full truncate text-xs text-muted-foreground", children: label }), _jsx("span", { className: cn('num truncate text-xl leading-7 font-semibold', valueClassName), children: value }), caption ? (_jsx("span", { className: "num line-clamp-2 text-xs text-muted-foreground", children: caption })) : null, lines?.map((l, i) => (
7
- // biome-ignore lint/suspicious/noArrayIndexKey: a card's lines in their saved order
8
- _jsx("span", { className: "num line-clamp-2 text-xs text-muted-foreground", children: l }, i)))] }));
6
+ return (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-full truncate text-xs text-muted-foreground", children: label }), _jsx("span", { className: cn('num truncate text-xl leading-7 font-semibold', valueClassName), children: value }), caption ? (_jsx("span", { className: "num line-clamp-2 text-xs text-muted-foreground", children: caption })) : null, lines?.map((l, i) => (_jsx("span", { className: "num line-clamp-2 text-xs text-muted-foreground", children: l }, i)))] }));
9
7
  }
10
8
  export function Tile({ label, value, caption, lines, className, valueClassName, }) {
11
9
  return (_jsx(Card, { size: "sm", className: cn('min-w-0 gap-0.5 px-3', className), children: _jsx(TileBody, { label: label, value: value, caption: caption, lines: lines, valueClassName: valueClassName }) }));
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  import type * as React from 'react';
3
3
  declare const badgeVariants: (props?: ({
4
- variant?: "link" | "success" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "warning" | "info" | null | undefined;
4
+ variant?: "default" | "destructive" | "ghost" | "info" | "link" | "outline" | "secondary" | "success" | "warning" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
7
7
  asChild?: boolean;
@@ -1,8 +1,8 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  import type * as React from 'react';
3
3
  declare const buttonVariants: (props?: ({
4
- variant?: "link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "destructive-confirm" | null | undefined;
5
- size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
4
+ variant?: "default" | "destructive" | "destructive-confirm" | "ghost" | "link" | "outline" | "secondary" | null | undefined;
5
+ size?: "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
8
8
  asChild?: boolean;
@@ -2,8 +2,8 @@ import * as React from 'react';
2
2
  import * as RechartsPrimitive from 'recharts';
3
3
  import type { TooltipValueType } from 'recharts';
4
4
  declare const THEMES: {
5
- readonly light: "";
6
- readonly dark: ".dark";
5
+ readonly light: '';
6
+ readonly dark: '.dark';
7
7
  };
8
8
  type TooltipNameType = number | string;
9
9
  export type ChartConfig = Record<string, {
package/dist/ui/chart.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from '../lib/utils.js';
3
3
  import * as React from 'react';
4
4
  import * as RechartsPrimitive from 'recharts';
@@ -86,10 +86,7 @@ function ChartLegendContent({ className, hideIcon = false, payload, verticalAlig
86
86
  if (!payload?.length) {
87
87
  return null;
88
88
  }
89
- return (
90
- // One series a line, cut to fit: a list on phone that scrolls past five
91
- // lines, wrapping rows on wider screens; the full name shows on hover.
92
- _jsx("div", { className: cn('flex max-h-24 flex-col items-start gap-x-4 gap-y-0.5 overflow-y-auto sm:max-h-none sm:flex-row sm:flex-wrap sm:items-center sm:justify-center sm:overflow-visible', verticalAlign === 'top' ? 'pb-3' : 'pt-3', className), children: payload
89
+ return (_jsx("div", { className: cn('flex max-h-24 flex-col items-start gap-x-4 gap-y-0.5 overflow-y-auto sm:max-h-none sm:flex-row sm:flex-wrap sm:items-center sm:justify-center sm:overflow-visible', verticalAlign === 'top' ? 'pb-3' : 'pt-3', className), children: payload
93
90
  .filter((item) => item.type !== 'none')
94
91
  .map((item, index) => {
95
92
  const key = `${nameKey ?? item.dataKey ?? 'value'}`;
@@ -6,7 +6,7 @@ declare function FieldLegend({ className, variant, ...props }: React.ComponentPr
6
6
  }): import("react").JSX.Element;
7
7
  declare function FieldGroup({ className, ...props }: React.ComponentProps<'div'>): import("react").JSX.Element;
8
8
  declare const fieldVariants: (props?: ({
9
- orientation?: "horizontal" | "vertical" | "responsive" | null | undefined;
9
+ orientation?: "horizontal" | "responsive" | "vertical" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
11
  declare function Field({ className, orientation, ...props }: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>): import("react").JSX.Element;
12
12
  declare function FieldContent({ className, ...props }: React.ComponentProps<'div'>): import("react").JSX.Element;
@@ -3,11 +3,11 @@ import type * as React from 'react';
3
3
  import { Button } from './button.js';
4
4
  declare function InputGroup({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
5
5
  declare const inputGroupAddonVariants: (props?: ({
6
- align?: "inline-end" | "inline-start" | "block-end" | "block-start" | null | undefined;
6
+ align?: "block-end" | "block-start" | "inline-end" | "inline-start" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  declare function InputGroupAddon({ className, align, ...props }: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>): React.JSX.Element;
9
9
  declare const inputGroupButtonVariants: (props?: ({
10
- size?: "xs" | "sm" | "icon-xs" | "icon-sm" | null | undefined;
10
+ size?: "icon-sm" | "icon-xs" | "sm" | "xs" | null | undefined;
11
11
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
12
12
  declare function InputGroupButton({ className, type, variant, size, ...props }: Omit<React.ComponentProps<typeof Button>, 'size'> & VariantProps<typeof inputGroupButtonVariants>): React.JSX.Element;
13
13
  declare function InputGroupText({ className, ...props }: React.ComponentProps<'span'>): React.JSX.Element;