@columenlabs/lumen-ui 0.1.256 → 0.1.258

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.
@@ -2,6 +2,6 @@ export { type AssistantAdapter, assistantClient } from './client.js';
2
2
  export { ChatImage } from './chat-image.js';
3
3
  export { type AssistantCommand, type AssistantMode, type ComposerExtra, LumenAssistant, type LumenAssistantProps, LumenComposer, assistantStatusKey, readAssistantImage, useAssistantStatus, useLumenAssistantDock, } from './lumen-assistant.js';
4
4
  export { assistantAdminSections, assistantSettingsSections } from './settings.js';
5
- export { UsageHistoryView, assistantUsageKey, tokens as formatTokens, usd as formatUsd, useAssistantUsage, } from './usage-history.js';
5
+ export { AssistantChatCost, UsageHistoryView, assistantUsageKey, tokens as formatTokens, usd as formatUsd, useAssistantUsage, } from './usage-history.js';
6
6
  export { type AssistantSource, type AssistantSourceItem, type ComposeRequest, type PageSelection, composeAssistant, selectionRef, useAssistantSource, usePageSelection, useShownAssistantSources, } from './sources.js';
7
7
  export { type AssistantChatState, type LiveItem, applyEvent, useAssistantChat, } from './use-assistant-chat.js';
@@ -5,6 +5,6 @@ export { assistantClient } from './client.js';
5
5
  export { ChatImage } from './chat-image.js';
6
6
  export { LumenAssistant, LumenComposer, assistantStatusKey, readAssistantImage, useAssistantStatus, useLumenAssistantDock, } from './lumen-assistant.js';
7
7
  export { assistantAdminSections, assistantSettingsSections } from './settings.js';
8
- export { UsageHistoryView, assistantUsageKey, tokens as formatTokens, usd as formatUsd, useAssistantUsage, } from './usage-history.js';
8
+ export { AssistantChatCost, UsageHistoryView, assistantUsageKey, tokens as formatTokens, usd as formatUsd, useAssistantUsage, } from './usage-history.js';
9
9
  export { composeAssistant, selectionRef, useAssistantSource, usePageSelection, useShownAssistantSources, } from './sources.js';
10
10
  export { applyEvent, useAssistantChat, } from './use-assistant-chat.js';
@@ -31,7 +31,7 @@ import { chatMarkdown, chatTurns, copyMarkdown, turnMarkdown } from './copy.js';
31
31
  import { ImageCard, ImagePending, toolIs } from './image-card.js';
32
32
  import { ReplyToSelection } from './reply-selection.js';
33
33
  import { composeAssistant, selectionRef, useComposeRequest, usePageSelection, useShownAssistantSources, } from './sources.js';
34
- import { UsageHistoryView, tokens, usd } from './usage-history.js';
34
+ import { AssistantChatCost, UsageHistoryView, tokens, usd } from './usage-history.js';
35
35
  import { useAssistantChat, } from './use-assistant-chat.js';
36
36
  // The Markdown renderer (tables included) loads with the first answer (lazy-part.tsx).
37
37
  // A tab whose build a deploy replaced shows the answer as plain text instead of reloading mid chat.
@@ -186,7 +186,7 @@ export function LumenAssistant({ client, context, onSettings, keepDays = 30, cha
186
186
  allCommands.push(c);
187
187
  const shown = useShownAssistantSources();
188
188
  const allSources = [...shown, ...sources.filter((x) => !shown.some((y) => y.id === x.id))];
189
- return (_jsxs("div", { "data-assistant": "", className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "flex h-12 shrink-0 items-center gap-1 border-b px-3", children: [_jsx(SparklesIcon, { className: "size-4 text-edge" }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: chat.title || s?.name || 'Assistant' }), s && tiers.length > 1 && (_jsx(LumenSelector, { trigger: "chip", size: "sm", label: "Tier", value: s.tier, options: tiers.map((t) => ({ value: t.id, label: t.label, detail: t.model_label })), onChange: (t) => void setTier(t) })), _jsx(LumenAssistantChats, { chats: chats.data
189
+ return (_jsxs("div", { "data-assistant": "", className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "flex h-12 shrink-0 items-center gap-1 border-b px-3", children: [_jsx(SparklesIcon, { className: "size-4 text-edge" }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: chat.title || s?.name || 'Assistant' }), s && tiers.length > 1 && (_jsx(LumenSelector, { trigger: "chip", size: "sm", label: "Tier", value: s.tier, options: tiers.map((t) => ({ value: t.id, label: t.label, detail: t.model_label })), onChange: (t) => void setTier(t) })), _jsx(AssistantChatCost, { client: client, chatId: chat.chatId, busy: chat.busy }), _jsx(LumenAssistantChats, { chats: chats.data
190
190
  ?.filter((c) => !chatScope || allScopes || c.scope === chatScope.current)
191
191
  .map((c) => ({
192
192
  id: c.id,
@@ -24,3 +24,14 @@ export declare function OwnKeyUsage({ client, always, usable, }: {
24
24
  /** Providers the person can pay with now: earlier spend on any other is listed apart, uncounted. */
25
25
  usable?: string[];
26
26
  }): import("react").JSX.Element | null;
27
+ /**
28
+ * What one chat has cost so far, as a chip for the assistant's header: what was charged (credits or
29
+ * the person's key) plus a plan sign-in's calls at API rates, shown ≈ when the plan's part is in it.
30
+ * Nothing while the chat has no history line. The hover splits it.
31
+ */
32
+ export declare function AssistantChatCost({ client, chatId, busy, }: {
33
+ client: AssistantAdapter;
34
+ chatId: string | null | undefined;
35
+ /** The chat is answering: the chip reads its history again when the answer ends. */
36
+ busy?: boolean;
37
+ }): import("react").JSX.Element | null;
@@ -1,15 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useQuery } from '@tanstack/react-query';
3
3
  import { ChevronLeftIcon, ChevronRightIcon, CpuIcon, MessageSquareIcon } from 'lucide-react';
4
- import { Fragment, useState } from 'react';
4
+ import { Fragment, useEffect, useState } from 'react';
5
5
  import { DetailsTable } from '../details-table.js';
6
6
  import { IconButton } from '../icon-button.js';
7
7
  import { InfoLabel } from '../info-tip.js';
8
8
  import { cn } from '../lib/utils.js';
9
9
  import { SettingField } from '../setting-parts.js';
10
+ import { Badge } from '../ui/badge.js';
10
11
  import { Label } from '../ui/label.js';
11
12
  import { Spinner } from '../ui/spinner.js';
12
13
  import { ToggleGroup, ToggleGroupItem } from '../ui/toggle-group.js';
14
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip.js';
13
15
  export const usd = (n) => n === 0 ? '$0' : n < 0.01 ? '<$0.01' : `$${n.toFixed(n < 10 ? 2 : 0)}`;
14
16
  export const tokens = (n) => n < 1000
15
17
  ? String(n)
@@ -213,3 +215,35 @@ export function OwnKeyUsage({ client, always, usable, }) {
213
215
  : usd(total),
214
216
  ] })) : (_jsxs("p", { className: "text-muted-foreground text-sm", children: ["Nothing spent on your keys in the last ", range, "."] }))] }));
215
217
  }
218
+ /**
219
+ * What one chat has cost so far, as a chip for the assistant's header: what was charged (credits or
220
+ * the person's key) plus a plan sign-in's calls at API rates, shown ≈ when the plan's part is in it.
221
+ * Nothing while the chat has no history line. The hover splits it.
222
+ */
223
+ export function AssistantChatCost({ client, chatId, busy, }) {
224
+ const q = useAssistantUsage(client, undefined, !!chatId);
225
+ const { refetch } = q;
226
+ // Read again once an answer ends, so the chip follows the chat.
227
+ useEffect(() => {
228
+ if (!busy && chatId)
229
+ void refetch();
230
+ }, [busy, chatId, refetch]);
231
+ const e = chatId ? q.data?.entries.find((x) => x.id === `chat:${chatId}`) : undefined;
232
+ if (!e)
233
+ return null;
234
+ const plan = e.own_plan_usd ?? 0;
235
+ const charged = e.usd;
236
+ const total = charged + plan;
237
+ if (total < 0.005 && !e.own_plan_calls)
238
+ return null;
239
+ const own = e.own_usd ?? 0;
240
+ const lines = [
241
+ own > 0 ? `${usd(own)} on your key` : null,
242
+ charged - own >= 0.005 ? `${usd(charged - own)} on credits` : null,
243
+ e.own_plan_calls
244
+ ? `${e.own_plan_calls === 1 ? '1 call' : `${e.own_plan_calls} calls`} on your ${PLAN_NAME}: ≈${usd(plan)} at API rates (charged $0)`
245
+ : null,
246
+ ].filter(Boolean);
247
+ const text = `${plan > 0 ? '≈' : ''}${usd(total)}`;
248
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": `This chat cost ${text}`, className: "shrink-0 rounded-md outline-none focus-visible:ring-3 focus-visible:ring-ring/50", children: _jsx(Badge, { variant: "outline", className: "num whitespace-nowrap", children: text }) }) }), _jsx(TooltipContent, { children: _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "font-medium", children: "This chat" }), lines.map((l) => (_jsx("span", { children: l }, l)))] }) })] }));
249
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.256",
3
+ "version": "0.1.258",
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.256",
52
+ "@columenlabs/lumen-ui-types": "0.1.258",
53
53
  "@dnd-kit/core": "6.3.1",
54
54
  "@dnd-kit/sortable": "10.0.0",
55
55
  "@dnd-kit/utilities": "3.2.2",