@columenlabs/lumen-ui 0.1.259 → 0.1.261
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AllowanceRequest, AssistantChat, AssistantChatRow, AssistantEvent, AssistantMessage, AssistantOwnWay, AssistantProvider, AssistantSettingsPatch, AssistantStatus, DraftItem, KeyState, StepItem, UsageHistory } from '@columenlabs/lumen-ui-types/assistant';
|
|
1
|
+
import type { AllowanceRequest, AssistantChat, AssistantChatRow, AssistantConnectorInput, AssistantEvent, AssistantMessage, AssistantOwnWay, AssistantProvider, AssistantSettingsPatch, AssistantStatus, DraftItem, KeyState, StepItem, UsageHistory } from '@columenlabs/lumen-ui-types/assistant';
|
|
2
2
|
type Test = NonNullable<KeyState['test']>;
|
|
3
3
|
export interface AssistantAdapter {
|
|
4
4
|
/** Names the cache its reads keep (two assistants on one page need two keys). */
|
|
@@ -31,6 +31,13 @@ export interface AssistantAdapter {
|
|
|
31
31
|
client_secret: string;
|
|
32
32
|
} | null): Promise<AssistantStatus>;
|
|
33
33
|
testScrapeql(): Promise<Test>;
|
|
34
|
+
/** Admin: adds a connector (an MCP server), kept only when its test lists its tools. */
|
|
35
|
+
addConnector?(input: AssistantConnectorInput): Promise<AssistantStatus>;
|
|
36
|
+
/** Admin: changes one; left out, `headers` keeps the ones saved. */
|
|
37
|
+
saveConnector?(id: string, input: AssistantConnectorInput): Promise<AssistantStatus>;
|
|
38
|
+
removeConnector?(id: string): Promise<AssistantStatus>;
|
|
39
|
+
/** Admin: lists its tools again. */
|
|
40
|
+
testConnector?(id: string): Promise<Test>;
|
|
34
41
|
chats(): Promise<AssistantChatRow[]>;
|
|
35
42
|
chat(id: string): Promise<AssistantChat>;
|
|
36
43
|
removeChat(id: string): Promise<void>;
|
package/dist/assistant/client.js
CHANGED
|
@@ -55,6 +55,10 @@ export function assistantClient(base) {
|
|
|
55
55
|
signOut: (p) => send('DELETE', `/my-signin?provider=${p}`),
|
|
56
56
|
scrapeql: (token) => token ? send('PUT', '/web/scrapeql', token) : send('DELETE', '/web/scrapeql'),
|
|
57
57
|
testScrapeql: () => send('POST', '/web/test'),
|
|
58
|
+
addConnector: (input) => send('POST', '/connectors', input),
|
|
59
|
+
saveConnector: (id, input) => send('PUT', `/connectors/${encodeURIComponent(id)}`, input),
|
|
60
|
+
removeConnector: (id) => send('DELETE', `/connectors/${encodeURIComponent(id)}`),
|
|
61
|
+
testConnector: (id) => send('POST', `/connectors/${encodeURIComponent(id)}/test`),
|
|
58
62
|
chats: () => get('/chats'),
|
|
59
63
|
chat: (id) => get(`/chats/${encodeURIComponent(id)}`),
|
|
60
64
|
removeChat: async (id) => {
|
|
@@ -4,10 +4,12 @@ import type { AssistantAdapter } from './client.js';
|
|
|
4
4
|
* The admin's section: the app's shared keys and each person's monthly allowance on them. Put it
|
|
5
5
|
* where the app keeps its admin settings, shown only to admins.
|
|
6
6
|
*/
|
|
7
|
-
export declare function assistantAdminSections({ client, allowance, providers, }: {
|
|
7
|
+
export declare function assistantAdminSections({ client, allowance, providers, connectors, }: {
|
|
8
8
|
client: AssistantAdapter;
|
|
9
9
|
/** Add Allowance requests (the app set `allowance` on the server). */
|
|
10
10
|
allowance?: boolean;
|
|
11
|
+
/** Add Connectors: outside MCP servers the assistant may use (the app set `connectors` on the server). */
|
|
12
|
+
connectors?: boolean;
|
|
11
13
|
/** Give each provider's shared key a section of its own (listed under Allowance) instead of one
|
|
12
14
|
* Shared model keys section: the app's providers, in order. */
|
|
13
15
|
providers?: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
3
|
-
import { ChartColumnIcon, CoinsIcon, GlobeIcon, InboxIcon, KeyRoundIcon, LayersIcon, Link2Icon, LogInIcon, ReceiptIcon, SendIcon, SparklesIcon, WrenchIcon, } from 'lucide-react';
|
|
3
|
+
import { ChartColumnIcon, CoinsIcon, GlobeIcon, InboxIcon, KeyRoundIcon, LayersIcon, Link2Icon, LogInIcon, PlugIcon, ReceiptIcon, SendIcon, SparklesIcon, WrenchIcon, } from 'lucide-react';
|
|
4
4
|
import { useId, useState } from 'react';
|
|
5
5
|
import { ConnectionLogo, LumenConnection, } from '../connection.js';
|
|
6
6
|
import { LumenExpandList, LumenExpandRow } from '../expand-list.js';
|
|
@@ -312,6 +312,104 @@ function Web({ client }) {
|
|
|
312
312
|
const { apply } = useApply(client);
|
|
313
313
|
return (_jsx(WithStatus, { client: client, children: (s) => (_jsxs("div", { className: "flex flex-col", children: [_jsx(SettingField, { label: "Read the web", info: "A plain fetch of public pages and APIs from the server, never the app's own pages.", children: _jsx(Switch, { "aria-label": "Read the web", checked: s.web.fetch, onCheckedChange: (fetch) => void apply('web', () => client.settings({ web: { fetch } })) }) }), _jsx(ScrapeqlBlock, { client: client, s: s })] })) }));
|
|
314
314
|
}
|
|
315
|
+
// ---------------------------------------------------------------- connectors
|
|
316
|
+
const CONNECTOR_ACCESS_LABELS = [
|
|
317
|
+
{ value: 'off', label: 'Off' },
|
|
318
|
+
{ value: 'read', label: 'Read' },
|
|
319
|
+
{ value: 'draft', label: 'Read + draft' },
|
|
320
|
+
];
|
|
321
|
+
/** Name, address and up to two sign-in headers; a pair left empty is not sent. */
|
|
322
|
+
const headerFields = (prefix = '') => [
|
|
323
|
+
{ id: `${prefix}h1`, label: 'Header', placeholder: 'CF-Access-Client-Id' },
|
|
324
|
+
{ id: `${prefix}v1`, label: 'Value', secret: true },
|
|
325
|
+
{ id: `${prefix}h2`, label: 'Header', placeholder: 'CF-Access-Client-Secret' },
|
|
326
|
+
{ id: `${prefix}v2`, label: 'Value', secret: true },
|
|
327
|
+
];
|
|
328
|
+
const headersOf = (v) => {
|
|
329
|
+
const out = {};
|
|
330
|
+
for (const i of [1, 2]) {
|
|
331
|
+
const k = v[`h${i}`]?.trim();
|
|
332
|
+
const val = v[`v${i}`]?.trim();
|
|
333
|
+
if (k && val)
|
|
334
|
+
out[k] = val;
|
|
335
|
+
}
|
|
336
|
+
return out;
|
|
337
|
+
};
|
|
338
|
+
function ConnectorRow({ client, c }) {
|
|
339
|
+
const { apply, refresh } = useApply(client);
|
|
340
|
+
const reads = c.tools.filter((t) => t.read).length;
|
|
341
|
+
const access = (v) => apply('connector', () => client.saveConnector(c.id, {
|
|
342
|
+
name: c.name,
|
|
343
|
+
url: c.url,
|
|
344
|
+
access: v,
|
|
345
|
+
}));
|
|
346
|
+
return (_jsx(LumenConnection, { id: c.id, name: c.name, logo: _jsx(ConnectionLogo, { icon: PlugIcon }), meta: `${c.tools.length} tool${c.tools.length === 1 ? '' : 's'}${c.access === 'off' ? ' · off' : ''}`, ways: [
|
|
347
|
+
{
|
|
348
|
+
id: 'headers',
|
|
349
|
+
label: 'Sign-in headers',
|
|
350
|
+
icon: KeyRoundIcon,
|
|
351
|
+
info: 'Sent with every call to this server, such as a Cloudflare Access service token. Kept sealed on the server and never shown again.',
|
|
352
|
+
set: true,
|
|
353
|
+
ident: c.headers.length ? c.headers.join(', ') : 'None',
|
|
354
|
+
test: c.test
|
|
355
|
+
? {
|
|
356
|
+
at: c.test.at,
|
|
357
|
+
ok: c.test.ok,
|
|
358
|
+
detail: c.test.detail,
|
|
359
|
+
error: c.test.ok ? null : c.test.detail,
|
|
360
|
+
}
|
|
361
|
+
: null,
|
|
362
|
+
fields: [{ id: 'url', label: 'Address', placeholder: c.url }, ...headerFields()],
|
|
363
|
+
addLabel: 'Replace',
|
|
364
|
+
onSave: (v) => apply('connector', () => client.saveConnector(c.id, {
|
|
365
|
+
name: c.name,
|
|
366
|
+
url: v.url?.trim() || c.url,
|
|
367
|
+
headers: headersOf(v),
|
|
368
|
+
}), 'Connector saved'),
|
|
369
|
+
...(client.testConnector
|
|
370
|
+
? {
|
|
371
|
+
onTest: () => client.testConnector(c.id).finally(refresh),
|
|
372
|
+
}
|
|
373
|
+
: {}),
|
|
374
|
+
onRemove: () => apply('connector', () => client.removeConnector(c.id), 'Connector removed'),
|
|
375
|
+
removeTitle: `Remove ${c.name}?`,
|
|
376
|
+
removeText: 'The assistant stops using its tools.',
|
|
377
|
+
},
|
|
378
|
+
], lines: [
|
|
379
|
+
{ label: 'Address', value: c.url, mono: true },
|
|
380
|
+
{
|
|
381
|
+
label: 'Tools',
|
|
382
|
+
info: 'The assistant finds these only when it needs them, so they cost nothing until used.',
|
|
383
|
+
value: c.tools.length
|
|
384
|
+
? `${c.tools.map((t) => t.title ?? t.name).join(', ')} (${reads} read-only)`
|
|
385
|
+
: 'None listed',
|
|
386
|
+
},
|
|
387
|
+
], children: _jsx(SettingField, { label: "Use", info: "Read uses the tools the server marks read-only. Read + draft also lets the others make a card the person saves or dismisses.", children: _jsx(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, value: c.access, "aria-label": `Use ${c.name}`, onValueChange: (v) => v && void access(v), children: CONNECTOR_ACCESS_LABELS.map((a) => (_jsx(ToggleGroupItem, { value: a.value, children: a.label }, a.value))) }) }) }));
|
|
388
|
+
}
|
|
389
|
+
/** Admin: the app's connectors, each an MCP server, and a row to add one. */
|
|
390
|
+
function Connectors({ client }) {
|
|
391
|
+
const { apply } = useApply(client);
|
|
392
|
+
return (_jsx(WithStatus, { client: client, children: (s) => (_jsxs(LumenExpandList, { children: [(s.connectors ?? []).map((c) => (_jsx(ConnectorRow, { client: client, c: c }, c.id))), _jsx(LumenConnection, { id: "connector-new", name: "Add connector", logo: _jsx(ConnectionLogo, { icon: PlugIcon }), meta: "Any MCP server over HTTP", ways: [
|
|
393
|
+
{
|
|
394
|
+
id: 'new',
|
|
395
|
+
label: 'MCP server',
|
|
396
|
+
icon: PlugIcon,
|
|
397
|
+
info: 'Its address (Streamable HTTP) and the headers it signs in with. It is tested before it is kept.',
|
|
398
|
+
set: false,
|
|
399
|
+
fields: [
|
|
400
|
+
{ id: 'name', label: 'Name', placeholder: 'Web search' },
|
|
401
|
+
{ id: 'url', label: 'Address', placeholder: 'https://example.com/mcp' },
|
|
402
|
+
...headerFields(),
|
|
403
|
+
],
|
|
404
|
+
addLabel: 'Add connector',
|
|
405
|
+
onSave: (v) => apply('connector', () => client.addConnector({
|
|
406
|
+
name: v.name?.trim() ?? '',
|
|
407
|
+
url: v.url?.trim() ?? '',
|
|
408
|
+
headers: headersOf(v),
|
|
409
|
+
}), 'Connector added'),
|
|
410
|
+
},
|
|
411
|
+
] })] })) }));
|
|
412
|
+
}
|
|
315
413
|
// ---------------------------------------------------------------- spend
|
|
316
414
|
function Spend({ client }) {
|
|
317
415
|
return (_jsx(WithStatus, { client: client, children: (s) => {
|
|
@@ -414,7 +512,7 @@ function AllowanceRequests({ client }) {
|
|
|
414
512
|
* The admin's section: the app's shared keys and each person's monthly allowance on them. Put it
|
|
415
513
|
* where the app keeps its admin settings, shown only to admins.
|
|
416
514
|
*/
|
|
417
|
-
export function assistantAdminSections({ client, allowance = false, providers, }) {
|
|
515
|
+
export function assistantAdminSections({ client, allowance = false, providers, connectors = false, }) {
|
|
418
516
|
const keys = providers
|
|
419
517
|
? [
|
|
420
518
|
{
|
|
@@ -455,6 +553,17 @@ export function assistantAdminSections({ client, allowance = false, providers, }
|
|
|
455
553
|
},
|
|
456
554
|
]
|
|
457
555
|
: []),
|
|
556
|
+
...(connectors
|
|
557
|
+
? [
|
|
558
|
+
{
|
|
559
|
+
id: 'assistant-connectors',
|
|
560
|
+
title: 'Connectors',
|
|
561
|
+
icon: PlugIcon,
|
|
562
|
+
description: 'Outside services the assistant may use',
|
|
563
|
+
content: _jsx(Connectors, { client: client }),
|
|
564
|
+
},
|
|
565
|
+
]
|
|
566
|
+
: []),
|
|
458
567
|
];
|
|
459
568
|
}
|
|
460
569
|
/**
|
|
@@ -36,7 +36,7 @@ export declare function AssistantChatCost({ client, chatId, busy, }: {
|
|
|
36
36
|
busy?: boolean;
|
|
37
37
|
}): import("react").JSX.Element | null;
|
|
38
38
|
/**
|
|
39
|
-
* This month's use, one row a chat or run: its Type (the person's API key, their plan sign-in, or
|
|
39
|
+
* This month's use, one row a session (a chat, or every run of one task on one thing): its Type (the person's API key, their plan sign-in, or
|
|
40
40
|
* the app's credits), Date, Calls and Amount, grouped by Type, Date or not at all, filtered by
|
|
41
41
|
* type or name. A row's hover gives its tokens and cost. A sign-in's amount is an estimate: the
|
|
42
42
|
* same tokens at the model's API rates (the plan charges $0).
|
|
@@ -254,30 +254,59 @@ const TYPE_LABEL = {
|
|
|
254
254
|
key: 'API key',
|
|
255
255
|
plan: 'Sign-in',
|
|
256
256
|
credits: 'Credits',
|
|
257
|
+
internal: 'Internal',
|
|
258
|
+
};
|
|
259
|
+
const TYPE_TONE = {
|
|
260
|
+
key: 'sky',
|
|
261
|
+
plan: 'amber',
|
|
262
|
+
credits: 'muted',
|
|
263
|
+
internal: 'muted',
|
|
257
264
|
};
|
|
258
|
-
const TYPE_TONE = { key: 'sky', plan: 'amber', credits: 'muted' };
|
|
259
265
|
/** Dollars with no `<` or `≈` (the table's note says which amounts are estimates). */
|
|
260
266
|
const money = (n) => (n === 0 ? '$0' : `$${n.toFixed(n < 10 ? 2 : 0)}`);
|
|
261
267
|
const amountOf = (r) => r.key + r.credits + r.plan;
|
|
262
268
|
const EARLIER = 'Earlier';
|
|
263
269
|
/** This month's rows: every chat and run, then the spend no chat or run carries, by who paid. */
|
|
264
270
|
function usageRows(h) {
|
|
265
|
-
|
|
271
|
+
// One row a session: a chat, or every run of one task on one thing (each Rebuild of a part).
|
|
272
|
+
const sessions = new Map();
|
|
273
|
+
for (const e of h.entries) {
|
|
266
274
|
const key = e.own_usd ?? 0;
|
|
267
275
|
const plan = e.own_plan_usd ?? 0;
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
276
|
+
const id = e.kind === 'chat' ? e.id : `task:${e.task ?? ''}:${e.title}`;
|
|
277
|
+
const s = sessions.get(id);
|
|
278
|
+
const planCalls = (s?.planCalls ?? 0) + (e.own_plan_calls ?? 0);
|
|
279
|
+
const row = {
|
|
280
|
+
id,
|
|
271
281
|
title: e.title,
|
|
272
|
-
type,
|
|
273
|
-
at: e.at,
|
|
274
|
-
calls: e.calls,
|
|
275
|
-
tokens: {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
282
|
+
type: 'credits',
|
|
283
|
+
at: Math.max(s?.at ?? 0, e.at),
|
|
284
|
+
calls: (s?.calls ?? 0) + e.calls,
|
|
285
|
+
tokens: {
|
|
286
|
+
in: (s?.tokens?.in ?? 0) + e.in,
|
|
287
|
+
read: (s?.tokens?.read ?? 0) + e.read,
|
|
288
|
+
out: (s?.tokens?.out ?? 0) + e.out,
|
|
289
|
+
},
|
|
290
|
+
key: (s?.key ?? 0) + key,
|
|
291
|
+
credits: (s?.credits ?? 0) + Math.max(0, e.usd - key),
|
|
292
|
+
plan: (s?.plan ?? 0) + plan,
|
|
293
|
+
planCalls,
|
|
279
294
|
};
|
|
280
|
-
|
|
295
|
+
// Its type is whoever paid most; the hover splits the rest.
|
|
296
|
+
const top = Math.max(row.key, row.credits, row.plan);
|
|
297
|
+
row.type =
|
|
298
|
+
row.plan > 0 && row.plan === top
|
|
299
|
+
? 'plan'
|
|
300
|
+
: row.key > 0 && row.key === top
|
|
301
|
+
? 'key'
|
|
302
|
+
: row.credits > 0 || !planCalls
|
|
303
|
+
? 'credits'
|
|
304
|
+
: 'plan';
|
|
305
|
+
if (e.internal)
|
|
306
|
+
row.type = 'internal';
|
|
307
|
+
sessions.set(id, row);
|
|
308
|
+
}
|
|
309
|
+
const out = [...sessions.values()];
|
|
281
310
|
const sum = (xs) => xs.reduce((a, n) => a + n, 0);
|
|
282
311
|
const earlier = (type, title, calls, amount) => amount >= 0.01 &&
|
|
283
312
|
out.push({
|
|
@@ -298,7 +327,8 @@ function usageRows(h) {
|
|
|
298
327
|
earlier('plan', 'Earlier this month, not per chat', Math.max(0, sum(plans.map((r) => r.calls)) - sum(h.entries.map((e) => e.own_plan_calls ?? 0))), sum(plans.map((r) => r.list_usd ?? 0)) - sum(h.entries.map((e) => e.own_plan_usd ?? 0)));
|
|
299
328
|
earlier('credits', 'Earlier this month, not per chat', Math.max(0, sum(app.map((r) => r.calls)) -
|
|
300
329
|
sum(h.entries.filter((e) => e.usd > (e.own_usd ?? 0)).map((e) => e.calls))), sum(app.map((r) => r.usd)) - sum(h.entries.map((e) => Math.max(0, e.usd - (e.own_usd ?? 0)))));
|
|
301
|
-
|
|
330
|
+
// The app's own calls are kept (and counted above, so no Earlier row takes them) but not shown.
|
|
331
|
+
return out.filter((r) => r.type !== 'internal');
|
|
302
332
|
}
|
|
303
333
|
/** A row's hover: its tokens (cache reads as a share of input) and what it cost, by who paid. */
|
|
304
334
|
function UsageTip({ r, children }) {
|
|
@@ -319,7 +349,7 @@ function UsageTip({ r, children }) {
|
|
|
319
349
|
? [['Estimate (API rates)', money(r.plan)]]
|
|
320
350
|
: []),
|
|
321
351
|
];
|
|
322
|
-
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: children }), _jsx(TooltipContent, { className: "max-w-72", children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { className: "
|
|
352
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: children }), _jsx(TooltipContent, { className: "max-w-72", children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [_jsx("span", { className: "whitespace-normal break-words font-medium", children: r.title }), _jsx("dl", { className: "grid grid-cols-[auto_auto] gap-x-4 gap-y-0.5", children: lines.map(([k, v]) => (_jsxs(Fragment, { children: [_jsx("dt", { className: "opacity-75", children: k }), _jsx("dd", { className: "num text-right", children: v })] }, k))) }), !t && _jsx("span", { className: "opacity-75", children: "Spent before each chat kept its own count." })] }) })] }));
|
|
323
353
|
}
|
|
324
354
|
const dayOf = (at) => at ? new Date(at).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) : EARLIER;
|
|
325
355
|
const USAGE_FIELDS = [
|
|
@@ -328,6 +358,9 @@ const USAGE_FIELDS = [
|
|
|
328
358
|
label: 'Session',
|
|
329
359
|
icon: MessageSquareIcon,
|
|
330
360
|
format: 'text',
|
|
361
|
+
// Names run long (a chat's first line): as wide as the table leaves after its other columns
|
|
362
|
+
// (the table's box is a size container), never under the usual name column.
|
|
363
|
+
maxWidth: 'max(12rem, calc(100cqw - 22rem))',
|
|
331
364
|
value: (r) => r.title,
|
|
332
365
|
render: (r) => (_jsx(UsageTip, { r: r, children: _jsx("span", {
|
|
333
366
|
// biome-ignore lint/a11y/noNoninteractiveTabindex: a keyboard reaches the row's hover
|
|
@@ -398,7 +431,7 @@ const GROUPS = [
|
|
|
398
431
|
{ id: 'date', label: 'Date', field: DAY_FIELD },
|
|
399
432
|
];
|
|
400
433
|
/**
|
|
401
|
-
* This month's use, one row a chat or run: its Type (the person's API key, their plan sign-in, or
|
|
434
|
+
* This month's use, one row a session (a chat, or every run of one task on one thing): its Type (the person's API key, their plan sign-in, or
|
|
402
435
|
* the app's credits), Date, Calls and Amount, grouped by Type, Date or not at all, filtered by
|
|
403
436
|
* type or name. A row's hover gives its tokens and cost. A sign-in's amount is an estimate: the
|
|
404
437
|
* same tokens at the model's API rates (the plan charges $0).
|
|
@@ -412,5 +445,5 @@ export function AssistantUsageTable({ client }) {
|
|
|
412
445
|
return null;
|
|
413
446
|
if (!q.data)
|
|
414
447
|
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, {})) }));
|
|
415
|
-
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(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."] })] }));
|
|
448
|
+
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: "@container 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."] })] }));
|
|
416
449
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.261",
|
|
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.261",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|