@columenlabs/lumen-ui 0.1.242 → 0.1.244

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.
package/README.md CHANGED
@@ -59,7 +59,7 @@ Use `createDeskQueryClient()` for the app's TanStack Query client: data stays fr
59
59
 
60
60
  ## What's inside
61
61
 
62
- **Frame.** `LumenShell` (sidebar with icons-only collapse and a drawer on phone, header, status bar, Settings, profile menu, ⌘K Find), `LumenAppSwitcher` and `LumenSuiteSwitcher` (groups, apps on other sites, a list read from an address), `WhatsNew` (release notes in a drawer), `TasksButton` and `TasksProvider` (background work in the header), `NotificationsSettings`, `McpSettings`, sign-in screens (`LumenAuthStage`, `LumenSecondStep`, `LumenSignInMethods`, `LumenBackupCodes`, `LumenBotCheck` for Cloudflare Turnstile, `authErrorText` for errors in plain words), `StatusHealth`, `LumenBoundary` (keeps a crash inside its card), `LumenPageState` (loading, empty and error), `lumenToast`.
62
+ **Frame.** `LumenShell` (sidebar with icons-only collapse and a drawer on phone, header, status bar, Settings, profile menu, ⌘K Find; `LumenFinder` mounts Find on its own for an app that draws its own frame, with commands from `useLumenFinderRows`), `LumenAppSwitcher` and `LumenSuiteSwitcher` (groups, apps on other sites, a list read from an address), `WhatsNew` (release notes in a drawer), `TasksButton` and `TasksProvider` (background work in the header), `NotificationsSettings`, `McpSettings`, sign-in screens (`LumenAuthStage`, `LumenSecondStep`, `LumenSignInMethods`, `LumenBackupCodes`, `LumenBotCheck` for Cloudflare Turnstile, `authErrorText` for errors in plain words), `StatusHealth`, `LumenBoundary` (keeps a crash inside its card), `LumenPageState` (loading, empty and error), `lumenToast`.
63
63
 
64
64
  **Pages and views.** `LumenLayout` (a page of cards from one view config: filter, tiles, widgets; Edit layout and Edit widget when you pass `onConfigChange`; a card with `header: false` is its body alone), `LumenGrid` (the 6-column grid under it), `LumenView` and `LumenViewCard` (a table that filters, groups, pivots and totals the rows you load), `LumenTable`, `LumenTokenizedFilter`, `LumenWidget` and `LumenSavedWidget` (the frame every card sits in, with its link square and controls), `LumenCard` (one number and its lines), `LumenPager`, `DetailsTable`, `LumenTree`, `LumenCalendar` (events by month, week or agenda, in a time zone), `LumenBoard` (items in columns by stage), `useAppView` (a page whose layout each person can change and reset).
65
65
 
@@ -174,9 +174,23 @@ function Providers({ client }) {
174
174
  return (_jsxs("div", { className: "flex flex-col", children: [_jsx(LumenExpandList, { children: rows.map((p) => (_jsx(OwnProviderKey, { client: client, p: p }, p.id))) }), ownSet && (_jsx(SettingField, { label: "Answers run on", info: "Your own key or plan first, with the shared credits behind it; or the shared credits first.", children: _jsx(LumenSelector, { label: "Answers run on", size: "sm", className: "w-44", value: s.pay, options: [
175
175
  { value: 'own', label: 'Your own first' },
176
176
  { value: 'app', label: 'Shared credits first' },
177
- ], onChange: (pay) => void apply('pay', () => client.settings({ pay: pay })) }) })), ownSet && (_jsx(SettingField, { label: "Your keys may spend", info: "On your keys, every provider together.", children: _jsx(CapInput, { label: "Your monthly cap", value: s.caps.own, onSave: (n) => void apply('own', () => client.ownCap(n)) }) })), _jsx(OwnKeyUsage, { client: client, always: ownSet })] }));
177
+ ], onChange: (pay) => void apply('pay', () => client.settings({ pay: pay })) }) })), ownSet && (_jsx(SettingField, { label: "Your keys may spend", info: "On your keys, every provider together.", children: _jsx(CapInput, { label: "Your monthly cap", value: s.caps.own, onSave: (n) => void apply('own', () => client.ownCap(n)) }) })), ownSet && _jsx(OwnCapUse, { s: s }), _jsx(OwnKeyUsage, { client: client, always: ownSet })] }));
178
178
  } }));
179
179
  }
180
+ /**
181
+ * How much of the person's own monthly cap is used: the same spend rows the cap is checked
182
+ * against, so this and the switch to shared credits never disagree.
183
+ */
184
+ function OwnCapUse({ s }) {
185
+ const used = s.spend.rows
186
+ .filter((r) => r.payer === 'own' && !r.free)
187
+ .reduce((a, r) => a + r.usd, 0);
188
+ const cap = s.caps.own;
189
+ const full = cap > 0 && used >= cap;
190
+ return (_jsx(SettingField, { label: "Used this month", info: full
191
+ ? 'Your cap is used up, so the shared credits pay until next month or until you raise the cap.'
192
+ : 'What your own keys and plan have spent this month, against your cap.', children: _jsxs("div", { className: "flex w-56 min-w-0 flex-col gap-1", children: [_jsx(ShareMeter, { value: cap > 0 ? used / cap : 1, label: `${usd(used)} of ${usd(cap)}`, wide: true }), full && _jsx("span", { className: "text-muted-foreground text-xs", children: "Cap reached: credits pay" })] }) }));
193
+ }
180
194
  /** One provider's shared key, which an admin sets for everyone. */
181
195
  function SharedProviderKey({ client, p }) {
182
196
  const { apply, refresh } = useApply(client);
@@ -119,17 +119,39 @@ export function OwnKeyUsage({ client, always }) {
119
119
  const sessions = all
120
120
  .filter((e) => e.at >= since && !seen.has(e.id) && seen.add(e.id))
121
121
  .sort((a, b) => b.at - a.at);
122
- const total = sessions.reduce((a, e) => a + (e.own_usd ?? 0), 0);
123
- return (_jsxs("div", { className: "flex flex-col gap-2 border-t border-border/60 pb-3 first:border-t-0", children: [_jsx(SettingField, { label: "Spent on your keys", info: "Each chat and run your own key or plan paid for, by its last use.", className: "border-t-0", end: _jsx(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, value: range, "aria-label": "Range", onValueChange: (v) => v && setRange(v), children: OWN_RANGES.map((r) => (_jsx(ToggleGroupItem, { value: r.id, "aria-label": `Last ${r.id}`, children: r.id }, r.id))) }) }), sessions.length ? (_jsx(DetailsTable, { head: [
122
+ // This month's own spend that no chat or run carries (spent before per-chat tracking), so the
123
+ // table never reads "nothing" while the cap's own numbers say otherwise.
124
+ const tagged = now.entries.reduce((a, e) => a + (e.own_usd ?? 0), 0);
125
+ const ownMonth = now.rows
126
+ .filter((r) => r.payer === 'own' && !r.free)
127
+ .reduce((a, r) => a + r.usd, 0);
128
+ const untracked = ownMonth - tagged >= 0.01 ? ownMonth - tagged : 0;
129
+ const total = sessions.reduce((a, e) => a + (e.own_usd ?? 0), 0) + untracked;
130
+ const earlier = 'Earlier this month, not per chat';
131
+ return (_jsxs("div", { className: "flex flex-col gap-2 border-t border-border/60 pb-3 first:border-t-0", children: [_jsx(SettingField, { label: "Spent on your keys", info: "Each chat and run your own key or plan paid for, by its last use.", className: "border-t-0", end: _jsx(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, value: range, "aria-label": "Range", onValueChange: (v) => v && setRange(v), children: OWN_RANGES.map((r) => (_jsx(ToggleGroupItem, { value: r.id, "aria-label": `Last ${r.id}`, children: r.id }, r.id))) }) }), sessions.length || untracked ? (_jsx(DetailsTable, { head: [
124
132
  { label: 'Session', grow: true },
125
133
  { label: 'Date', hideOnPhone: true },
126
134
  { label: 'Amount', right: true },
127
- ], rows: sessions.map((e) => ({
128
- key: e.id,
129
- cells: [
130
- _jsx("span", { className: "block truncate", title: e.title, children: e.title }, "n"),
131
- _jsx("time", { dateTime: new Date(e.at).toISOString(), title: new Date(e.at).toLocaleString(), children: when(e.at) }, "t"),
132
- usd(e.own_usd ?? 0),
133
- ],
134
- })), foot: ['Total', _jsx("span", { className: "max-md:hidden" }, "d"), usd(total)] })) : (_jsxs("p", { className: "text-muted-foreground text-sm", children: ["Nothing spent on your keys in the last ", range, "."] }))] }));
135
+ ], rows: [
136
+ ...sessions.map((e) => ({
137
+ key: e.id,
138
+ cells: [
139
+ _jsx("span", { className: "block truncate", title: e.title, children: e.title }, "n"),
140
+ _jsx("time", { dateTime: new Date(e.at).toISOString(), title: new Date(e.at).toLocaleString(), children: when(e.at) }, "t"),
141
+ usd(e.own_usd ?? 0),
142
+ ],
143
+ })),
144
+ ...(untracked
145
+ ? [
146
+ {
147
+ key: 'earlier',
148
+ cells: [
149
+ _jsx("span", { className: "block truncate text-muted-foreground", title: earlier, children: earlier }, "n"),
150
+ _jsx("span", {}, "t"),
151
+ usd(untracked),
152
+ ],
153
+ },
154
+ ]
155
+ : []),
156
+ ], foot: ['Total', _jsx("span", { className: "max-md:hidden" }, "d"), usd(total)] })) : (_jsxs("p", { className: "text-muted-foreground text-sm", children: ["Nothing spent on your keys in the last ", range, "."] }))] }));
135
157
  }
package/dist/index.d.ts CHANGED
@@ -117,6 +117,6 @@ export { LumenChart, type LumenChartOptions, type MineIndicators, SymbolChartPro
117
117
  export { SymbolSheetProvider } from './symbol-sheet.js';
118
118
  export { useSymbolSheet } from './symbol-sheet-context.js';
119
119
  export { NoteLine, type NoteLineProps } from './note-line.js';
120
- export { type LumenFinderConfig, type LumenFinderPageType, type LumenFinderRow, type LumenFinderScope, LumenFinderButton, useLumenFinder, useLumenFinderRows, useOpenLumenFinder, } from './lumen-finder.js';
120
+ export { type LumenFinderConfig, type LumenFinderPageType, type LumenFinderRow, type LumenFinderScope, LumenFinder, LumenFinderButton, useLumenFinder, useLumenFinderRows, useOpenLumenFinder, } from './lumen-finder.js';
121
121
  export { type AssistantChat, LumenAssistantButton, LumenAssistantChats, LumenAssistantPanel, assistantOpen, tabState, useAssistantHotkey, } from './assistant-panel.js';
122
122
  export { LumenTree, LumenTreeItem, type LumenTreeItemProps, type LumenTreeProps, useLumenTree, } from './lumen-tree.js';
package/dist/index.js CHANGED
@@ -124,6 +124,6 @@ export { LumenChart, SymbolChartProvider, useLumenChart, useSymbolChart, } from
124
124
  export { SymbolSheetProvider } from './symbol-sheet.js';
125
125
  export { useSymbolSheet } from './symbol-sheet-context.js';
126
126
  export { NoteLine } from './note-line.js';
127
- export { LumenFinderButton, useLumenFinder, useLumenFinderRows, useOpenLumenFinder, } from './lumen-finder.js';
127
+ export { LumenFinder, LumenFinderButton, useLumenFinder, useLumenFinderRows, useOpenLumenFinder, } from './lumen-finder.js';
128
128
  export { LumenAssistantButton, LumenAssistantChats, LumenAssistantPanel, assistantOpen, tabState, useAssistantHotkey, } from './assistant-panel.js';
129
129
  export { LumenTree, LumenTreeItem, useLumenTree, } from './lumen-tree.js';
@@ -46,7 +46,11 @@ export declare function useLumenFinderRows(rows: LumenFinderRow[] | null): void;
46
46
  type Icon = ComponentType<{
47
47
  className?: string;
48
48
  }>;
49
- /** Find for the shell: ⌘K, the rows apps add and the dialog. LumenShell mounts it. */
49
+ /**
50
+ * Find: ⌘K, the rows apps add and the dialog. LumenShell mounts it; an app that draws its own frame
51
+ * (a full-screen editor) mounts it around its screen instead and adds its commands with
52
+ * `useLumenFinderRows`. No router is needed there: rows run, or follow plain links.
53
+ */
50
54
  export declare function LumenFinder({ app, settings, openSettings, config, children, }: {
51
55
  app: string;
52
56
  settings: {
@@ -162,7 +162,11 @@ function useShellRows(open, query, settings, openSettings, app, pageTypes, setti
162
162
  ]
163
163
  : base, [base, assistant, q]);
164
164
  }
165
- /** Find for the shell: ⌘K, the rows apps add and the dialog. LumenShell mounts it. */
165
+ /**
166
+ * Find: ⌘K, the rows apps add and the dialog. LumenShell mounts it; an app that draws its own frame
167
+ * (a full-screen editor) mounts it around its screen instead and adds its commands with
168
+ * `useLumenFinderRows`. No router is needed there: rows run, or follow plain links.
169
+ */
166
170
  export function LumenFinder({ app, settings, openSettings, config, children, }) {
167
171
  const pageTypes = config?.pageTypes ?? NO_TYPES;
168
172
  const settingsScope = config?.settingsScope ?? 'Settings';
@@ -218,7 +222,9 @@ export function LumenFinder({ app, settings, openSettings, config, children, })
218
222
  }
219
223
  function FinderDialog({ app, open, onOpenChange, query, onQuery, scope, onScope, scopes, placeholder, groups, }) {
220
224
  const mobile = useIsMobile();
221
- const router = useRouter();
225
+ // Optional: an app without TanStack Router (a single-screen tool) still gets Find; its rows run or
226
+ // go by plain links.
227
+ const router = useRouter({ warn: false });
222
228
  const list = useRef(null);
223
229
  const input = useRef(null);
224
230
  // Focus without scrolling: on a phone iOS pans the page to a focused field as the keyboard opens,
@@ -241,8 +247,8 @@ function FinderDialog({ app, open, onOpenChange, query, onQuery, scope, onScope,
241
247
  return;
242
248
  const outside = /^https?:/.test(r.href);
243
249
  if (tab)
244
- window.open(outside ? r.href : router.history.createHref(r.href), '_blank');
245
- else if (outside)
250
+ window.open(outside || !router ? r.href : router.history.createHref(r.href), '_blank');
251
+ else if (outside || !router)
246
252
  window.location.assign(r.href);
247
253
  else
248
254
  void router.navigate({ href: r.href });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.242",
3
+ "version": "0.1.244",
4
4
  "description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,7 +49,7 @@
49
49
  "@codemirror/lang-javascript": "6.2.5",
50
50
  "@codemirror/lang-json": "6.0.2",
51
51
  "@codemirror/lang-sql": "6.10.0",
52
- "@columenlabs/lumen-ui-types": "0.1.242",
52
+ "@columenlabs/lumen-ui-types": "0.1.244",
53
53
  "@dnd-kit/core": "6.3.1",
54
54
  "@dnd-kit/sortable": "10.0.0",
55
55
  "@dnd-kit/utilities": "3.2.2",