@columenlabs/lumen-ui 0.1.276 → 0.1.278
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/dist/assistant/usage-history.js +33 -5
- package/dist/lumen-view.js +7 -1
- package/dist/theme.css +12 -0
- package/dist/view-model.d.ts +3 -2
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useQuery } from '@tanstack/react-query';
|
|
3
|
-
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon, CpuIcon, HashIcon, KeyRoundIcon, MessageSquareIcon, ReceiptIcon, } from 'lucide-react';
|
|
3
|
+
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon, CpuIcon, HashIcon, KeyRoundIcon, MessageSquareIcon, MessagesSquareIcon, ReceiptIcon, } from 'lucide-react';
|
|
4
4
|
import { Fragment, useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import { DetailsTable } from '../details-table.js';
|
|
6
6
|
import { IconButton } from '../icon-button.js';
|
|
@@ -291,6 +291,13 @@ function usageRows(h) {
|
|
|
291
291
|
credits: (s?.credits ?? 0) + Math.max(0, e.usd - key),
|
|
292
292
|
plan: (s?.plan ?? 0) + plan,
|
|
293
293
|
planCalls,
|
|
294
|
+
...(e.turns || s?.turns
|
|
295
|
+
? {
|
|
296
|
+
turns: (s?.turns ?? 0) + (e.turns ?? 0),
|
|
297
|
+
images: (s?.images ?? 0) + (e.images ?? 0),
|
|
298
|
+
refs: (s?.refs ?? 0) + (e.refs ?? 0),
|
|
299
|
+
}
|
|
300
|
+
: {}),
|
|
294
301
|
};
|
|
295
302
|
// Its type is whoever paid most; the hover splits the rest.
|
|
296
303
|
const top = Math.max(row.key, row.credits, row.plan);
|
|
@@ -330,6 +337,15 @@ function usageRows(h) {
|
|
|
330
337
|
// The app's own calls are kept (and counted above, so no Earlier row takes them) but not shown.
|
|
331
338
|
return out.filter((r) => r.type !== 'internal');
|
|
332
339
|
}
|
|
340
|
+
const plural = (n, one) => `${n} ${one}${n === 1 ? '' : 's'}`;
|
|
341
|
+
/** A row's attachments: their count, then each kind there was (`3 · 2 images, 1 pick`). */
|
|
342
|
+
const attachments = (r) => {
|
|
343
|
+
const kinds = [
|
|
344
|
+
...(r.images ? [plural(r.images, 'image')] : []),
|
|
345
|
+
...(r.refs ? [plural(r.refs, 'pick')] : []),
|
|
346
|
+
];
|
|
347
|
+
return kinds.length ? `${(r.images ?? 0) + (r.refs ?? 0)} · ${kinds.join(', ')}` : '0';
|
|
348
|
+
};
|
|
333
349
|
/** A row's hover: its tokens (cache reads as a share of input) and what it cost, by who paid. */
|
|
334
350
|
function UsageTip({ r, children }) {
|
|
335
351
|
const t = r.tokens;
|
|
@@ -341,6 +357,8 @@ function UsageTip({ r, children }) {
|
|
|
341
357
|
['Output', tokens(t.out)],
|
|
342
358
|
]
|
|
343
359
|
: []),
|
|
360
|
+
...(r.turns ? [['Turns', String(r.turns)]] : []),
|
|
361
|
+
...(r.turns ? [['Attachments', attachments(r)]] : []),
|
|
344
362
|
...(r.key > 0 ? [['Cost (your key)', money(r.key)]] : []),
|
|
345
363
|
...(r.credits > 0 || (r.type === 'credits' && !r.plan)
|
|
346
364
|
? [['Credits', money(r.credits)]]
|
|
@@ -395,9 +413,8 @@ const USAGE_FIELDS = [
|
|
|
395
413
|
label: 'Session',
|
|
396
414
|
icon: MessageSquareIcon,
|
|
397
415
|
format: 'text',
|
|
398
|
-
// Names run long (a chat's first line):
|
|
399
|
-
|
|
400
|
-
maxWidth: 'max(12rem, calc(100cqw - 22rem))',
|
|
416
|
+
// Names run long (a chat's first line): all the width the other columns leave.
|
|
417
|
+
maxWidth: 'fill',
|
|
401
418
|
value: (r) => r.title,
|
|
402
419
|
render: (r) => (_jsx(UsageTip, { r: r, children: _jsx("span", {
|
|
403
420
|
// biome-ignore lint/a11y/noNoninteractiveTabindex: a keyboard reaches the row's hover
|
|
@@ -422,6 +439,17 @@ const USAGE_FIELDS = [
|
|
|
422
439
|
value: (r) => r.at,
|
|
423
440
|
render: (r) => (_jsx(UsageTip, { r: r, children: r.at ? (_jsx("time", { dateTime: new Date(r.at).toISOString(), className: "text-muted-foreground", children: relWhen(r.at) })) : (_jsx("span", { className: "text-muted-foreground", children: EARLIER })) })),
|
|
424
441
|
},
|
|
442
|
+
{
|
|
443
|
+
key: 'turns',
|
|
444
|
+
label: 'Turns',
|
|
445
|
+
icon: MessagesSquareIcon,
|
|
446
|
+
format: 'integer',
|
|
447
|
+
hideOnPhone: true,
|
|
448
|
+
aggregate: 'sum',
|
|
449
|
+
// A run, or a chat from before turns were counted, has none: a dash, not 0.
|
|
450
|
+
value: (r) => r.turns ?? null,
|
|
451
|
+
render: (r) => (_jsx(UsageTip, { r: r, children: _jsx("span", { className: cn(!r.turns && 'text-muted-foreground'), children: r.turns ?? '—' }) })),
|
|
452
|
+
},
|
|
425
453
|
{
|
|
426
454
|
key: 'calls',
|
|
427
455
|
label: 'Calls',
|
|
@@ -482,5 +510,5 @@ export function AssistantUsageTable({ client }) {
|
|
|
482
510
|
return null;
|
|
483
511
|
if (!q.data)
|
|
484
512
|
return (_jsx("div", { className: "flex justify-center p-4", children: q.isError ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Usage didn't load. Try again later." })) : (_jsx(Spinner, {})) }));
|
|
485
|
-
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(LumenTokenizedFilter, { filter: filter, label: "Filter usage", placeholder: "Filter: type, or a session name", className: "min-w-48 flex-1", link: null }), _jsx(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, value: group, "aria-label": "Group by", onValueChange: (v) => v && setGroup(v), children: GROUPS.map((g) => (_jsx(ToggleGroupItem, { value: g.id, "aria-label": `Group by ${g.label}`, children: g.label }, g.id))) })] }), _jsx("div", { className: "
|
|
513
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(LumenTokenizedFilter, { filter: filter, label: "Filter usage", placeholder: "Filter: type, or a session name", className: "min-w-48 flex-1", link: null }), _jsx(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, value: group, "aria-label": "Group by", onValueChange: (v) => v && setGroup(v), children: GROUPS.map((g) => (_jsx(ToggleGroupItem, { value: g.id, "aria-label": `Group by ${g.label}`, children: g.label }, g.id))) })] }), _jsx("div", { className: "min-w-0", children: _jsx(LumenTable, { domain: USAGE_DOMAIN, rows: filter.results, groupBy: GROUPS.find((g) => g.id === group)?.field ?? null, sort: { key: 'at', desc: true }, emptyState: rows.length ? undefined : (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nothing used this month." })), onClearFilter: filter.clear }) }), _jsxs("p", { className: "text-muted-foreground text-xs", children: ["Sign-in amounts are estimates: your ", PLAN_NAME, " charges $0, so the amount is what the same tokens would cost at the model's API rates. Key and credit amounts are what was charged."] })] }));
|
|
486
514
|
}
|
package/dist/lumen-view.js
CHANGED
|
@@ -493,12 +493,18 @@ const colPropsOf = (units) => (f, ...more) => ({
|
|
|
493
493
|
className: cn(colOf(f), ...more),
|
|
494
494
|
'data-col': f?.key,
|
|
495
495
|
'data-units': f && units.has(f.key) ? '' : undefined,
|
|
496
|
+
'data-fill': f?.maxWidth === 'fill' ? '' : undefined,
|
|
496
497
|
});
|
|
497
498
|
/**
|
|
498
499
|
* A column's widest cell (`maxWidth`): the first column defaults to `--lead-col-max` (18rem in
|
|
499
500
|
* theme.css), so one long name never pushes the other columns off screen; `false` lifts it.
|
|
501
|
+
* `fill` takes the room the other columns leave (theme.css `[data-fill]`), so its cell holds to it.
|
|
500
502
|
*/
|
|
501
|
-
const capOf = (f, i) => f.maxWidth === false
|
|
503
|
+
const capOf = (f, i) => f.maxWidth === false
|
|
504
|
+
? undefined
|
|
505
|
+
: f.maxWidth === 'fill'
|
|
506
|
+
? '100%'
|
|
507
|
+
: (f.maxWidth ?? (i === 0 ? 'var(--lead-col-max)' : undefined));
|
|
502
508
|
/** Holds a cell to its column's cap on one line: text past it ends in an ellipsis. `end` keeps a
|
|
503
509
|
* right-aligned column's value on its right edge (the wrapper is a flex row, which `text-right`
|
|
504
510
|
* alone does not move), so a capped or resized column keeps one edge. */
|
package/dist/theme.css
CHANGED
|
@@ -537,6 +537,18 @@
|
|
|
537
537
|
);
|
|
538
538
|
}
|
|
539
539
|
|
|
540
|
+
/* A column that fills (`ViewField.maxWidth: 'fill'`): it takes the width the other columns leave
|
|
541
|
+
and gives none of its own text's width to the table (max-width: 0), so its cells end in an
|
|
542
|
+
ellipsis only where the table truly has no more room. Never under 12rem: narrower, the table
|
|
543
|
+
scrolls. A phone's cards lay the row out themselves. */
|
|
544
|
+
@media (min-width: 640px) {
|
|
545
|
+
[data-slot="table"] :is(th, td)[data-fill] {
|
|
546
|
+
width: 100%;
|
|
547
|
+
max-width: 0;
|
|
548
|
+
min-width: 12rem;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
540
552
|
/* A LumenView table on a narrow phone (`data-cards`): each row is a card, so nothing runs off
|
|
541
553
|
the right edge, and every row is ONE line, whatever an app draws in it: the row's name (shortens
|
|
542
554
|
in an ellipsis), then one figure (`data-card="meta"`, the sorted one, else the first number or
|
package/dist/view-model.d.ts
CHANGED
|
@@ -141,9 +141,10 @@ export interface ViewField<T> {
|
|
|
141
141
|
/**
|
|
142
142
|
* The column's widest cell, a CSS length (`14rem`): past it the cell ends in an ellipsis on one
|
|
143
143
|
* line, the full text in its hover. The first column defaults to `--lead-col-max` (18rem);
|
|
144
|
-
* `false` lets it grow.
|
|
144
|
+
* `false` lets it grow. `fill` gives it all the width the other columns leave (at least 12rem),
|
|
145
|
+
* so a long name ends in an ellipsis only where the table truly has no more room.
|
|
145
146
|
*/
|
|
146
|
-
maxWidth?: string | false;
|
|
147
|
+
maxWidth?: 'fill' | (string & {}) | false;
|
|
147
148
|
/** Left out on phones, where the table has no room for it (a returns table's longer windows). */
|
|
148
149
|
hideOnPhone?: boolean;
|
|
149
150
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.278",
|
|
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.
|
|
52
|
+
"@columenlabs/lumen-ui-types": "0.1.278",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|