@columenlabs/lumen-ui 0.1.83 → 0.1.84
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/client.d.ts +7 -1
- package/dist/assistant/client.js +6 -0
- package/dist/assistant/lumen-assistant.d.ts +3 -1
- package/dist/assistant/lumen-assistant.js +24 -1
- package/dist/assistant/settings.d.ts +6 -2
- package/dist/assistant/settings.js +117 -6
- package/dist/shell.d.ts +2 -0
- package/dist/shell.js +4 -0
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantChat, AssistantChatRow, AssistantEvent, AssistantMessage, AssistantProvider, AssistantSettingsPatch, AssistantStatus, DraftItem, KeyState, StepItem } from '@columenlabs/lumen-ui-types/assistant';
|
|
1
|
+
import type { AllowanceRequest, AssistantChat, AssistantChatRow, AssistantEvent, AssistantMessage, AssistantProvider, AssistantSettingsPatch, AssistantStatus, DraftItem, KeyState, StepItem } 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). */
|
|
@@ -36,6 +36,12 @@ export interface AssistantAdapter {
|
|
|
36
36
|
settleDraft(chatId: string, draftId: string, action: 'save' | 'dismiss'): Promise<DraftItem>;
|
|
37
37
|
/** Takes back a change the assistant made at once. */
|
|
38
38
|
undoWrite(chatId: string, stepId: string): Promise<StepItem>;
|
|
39
|
+
/** Asks for more allowance on the shared keys (an app with `allowance`). */
|
|
40
|
+
requestMore?(amount: number, why: string): Promise<AssistantStatus>;
|
|
41
|
+
/** Approvers: every request, newest first. */
|
|
42
|
+
allowanceRequests?(): Promise<AllowanceRequest[]>;
|
|
43
|
+
/** Approvers: grants (the asked amount unless `amount` says) or declines a request. */
|
|
44
|
+
decideRequest?(id: string, action: 'grant' | 'decline', amount?: number): Promise<AllowanceRequest>;
|
|
39
45
|
}
|
|
40
46
|
/** The adapter over `assistantRoutes` mounted at `base` (`/api/assistant`). */
|
|
41
47
|
export declare function assistantClient(base: string): AssistantAdapter;
|
package/dist/assistant/client.js
CHANGED
|
@@ -63,6 +63,12 @@ export function assistantClient(base) {
|
|
|
63
63
|
action,
|
|
64
64
|
}),
|
|
65
65
|
undoWrite: (chatId, stepId) => send('POST', `/chats/${encodeURIComponent(chatId)}/writes/${encodeURIComponent(stepId)}/undo`),
|
|
66
|
+
requestMore: (amount, why) => send('POST', '/allowance/request', { amount, why }),
|
|
67
|
+
allowanceRequests: () => get('/allowance/requests'),
|
|
68
|
+
decideRequest: (id, action, amount) => send('POST', `/allowance/requests/${encodeURIComponent(id)}`, {
|
|
69
|
+
action,
|
|
70
|
+
...(amount !== undefined ? { amount } : {}),
|
|
71
|
+
}),
|
|
66
72
|
async send(msg, onEvent, signal) {
|
|
67
73
|
const url = `${root}/chat`;
|
|
68
74
|
const res = await fetch(url, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessage, AssistantRefNote, AssistantStatus } from '@columenlabs/lumen-ui-types/assistant';
|
|
1
|
+
import type { AllowanceUnit, AssistantMessage, AssistantRefNote, AssistantStatus } from '@columenlabs/lumen-ui-types/assistant';
|
|
2
2
|
import { type LumenOption } from '../lumen-selector.js';
|
|
3
3
|
import type { AssistantAdapter } from './client.js';
|
|
4
4
|
import { type AssistantSource } from './sources.js';
|
|
@@ -42,6 +42,8 @@ export interface AssistantCommand {
|
|
|
42
42
|
}
|
|
43
43
|
/** The assistant's chat: header, messages and composer. */
|
|
44
44
|
export declare function LumenAssistant({ client, context, onSettings, keepDays, onClose, chat: outside, commands, sources, }: LumenAssistantProps): import("react").JSX.Element;
|
|
45
|
+
/** An allowance in its unit: `$4.20`, or `350 credits`. */
|
|
46
|
+
export declare const allowanceAmount: (u: AllowanceUnit, n: number) => string;
|
|
45
47
|
/** What the composer sends with the text: the tier picked with `@` and the things pointed at with `#`. */
|
|
46
48
|
export interface ComposerExtra {
|
|
47
49
|
tier?: string;
|
|
@@ -9,6 +9,7 @@ import { errorText } from '../integrations.js';
|
|
|
9
9
|
import { cn } from '../lib/utils.js';
|
|
10
10
|
import { LumenSelector, LumenSelectorList, } from '../lumen-selector.js';
|
|
11
11
|
import { lumenToast } from '../lumen-toast.js';
|
|
12
|
+
import { useOpenSettings } from '../shell.js';
|
|
12
13
|
import { cardsOnScreen, kindName, pointAt, sectionsOnScreen, targetEl, textOf, } from '../spotlight.js';
|
|
13
14
|
import { Alert, AlertDescription } from '../ui/alert.js';
|
|
14
15
|
import { Button } from '../ui/button.js';
|
|
@@ -148,7 +149,18 @@ function Messages({ chat, status, onSettings, }) {
|
|
|
148
149
|
useEffect(() => {
|
|
149
150
|
end.current?.scrollIntoView({ block: 'end' });
|
|
150
151
|
}, [count]);
|
|
151
|
-
|
|
152
|
+
// The app's own link to Settings, else the shell's, at the section that answers the wait.
|
|
153
|
+
const shellSettings = useOpenSettings();
|
|
154
|
+
const settings = onSettings ? (_section) => onSettings() : shellSettings;
|
|
155
|
+
return (_jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 py-3", children: [status?.wait && (_jsxs(Alert, { className: "mb-3", children: [_jsx(CircleAlertIcon, {}), _jsxs(AlertDescription, { className: "flex flex-col items-start gap-2", children: [status.wait === 'app_cap' && status.allowance
|
|
156
|
+
? allowanceWait(status.allowance)
|
|
157
|
+
: WAIT[status.wait](status.admin, status.providers.find((p) => p.id === status.wait_provider)?.label ?? 'API'), settings && (_jsx(Button, { size: "sm", variant: "outline", onClick: () => settings(status.wait === 'app_cap' && status.allowance
|
|
158
|
+
? 'assistant-allowance'
|
|
159
|
+
: 'assistant-providers'), children: status.wait === 'app_cap' &&
|
|
160
|
+
status.allowance &&
|
|
161
|
+
status.allowance.request?.status !== 'open'
|
|
162
|
+
? 'Request more'
|
|
163
|
+
: 'Open Settings' }))] })] })), chat.loading ? (_jsxs("p", { className: "flex items-center gap-2 text-muted-foreground text-sm", children: [_jsx(Spinner, {}), " Loading the chat\u2026"] })) : chat.items.length === 0 && !status?.wait ? (_jsxs("p", { className: "pt-8 text-center text-muted-foreground text-sm", children: ["Ask ", status?.name ?? 'the assistant', " anything."] })) : (_jsx(TierNames.Provider, { value: (id) => status?.tiers.find((t) => t.id === id)?.label ?? id, children: _jsx(UndoWrite.Provider, { value: chat.undo, children: _jsx(Items, { items: chat.items, onSettle: chat.settle, busy: chat.busy, turns: true }) }) })), chat.busy && !chat.items.some(isLive) && (_jsxs("p", { className: "flex items-center gap-2 py-1 text-muted-foreground text-sm", children: [_jsx(Spinner, {}), " Thinking\u2026"] })), _jsx("div", { ref: end })] }));
|
|
152
164
|
}
|
|
153
165
|
const isLive = (i) => !!i.live || (i.kind === 'handoff' && i.items.some(isLive));
|
|
154
166
|
const WAIT = {
|
|
@@ -158,6 +170,17 @@ const WAIT = {
|
|
|
158
170
|
app_cap: () => "You reached this month's cap on the shared keys. Add your own key, or wait for next month.",
|
|
159
171
|
own_cap: () => 'Your own keys reached their monthly cap. Raise it in Settings ▸ Model providers.',
|
|
160
172
|
};
|
|
173
|
+
/** An allowance in its unit: `$4.20`, or `350 credits`. */
|
|
174
|
+
export const allowanceAmount = (u, n) => u.usd === 1 && u.many === 'dollars'
|
|
175
|
+
? usd(n)
|
|
176
|
+
: `${n.toLocaleString()} ${n === 1 ? u.one : u.many}`;
|
|
177
|
+
function allowanceWait(a) {
|
|
178
|
+
if (a.request?.status === 'open')
|
|
179
|
+
return `You used your allowance on the shared keys. Your request for ${allowanceAmount(a.unit, a.request.amount)} is waiting, or add your own key.`;
|
|
180
|
+
return a.period === 'once'
|
|
181
|
+
? 'You used your allowance on the shared keys. Request more, or add your own key.'
|
|
182
|
+
: "You used this month's allowance on the shared keys. Request more, or add your own key.";
|
|
183
|
+
}
|
|
161
184
|
/** The assistant's name, for a copied turn's headings. */
|
|
162
185
|
const AssistantName = createContext('Assistant');
|
|
163
186
|
/** A tier's name for a message's `@` chip. */
|
|
@@ -4,17 +4,21 @@ 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, }: {
|
|
7
|
+
export declare function assistantAdminSections({ client, allowance, }: {
|
|
8
8
|
client: AssistantAdapter;
|
|
9
|
+
/** Add Allowance requests (the app set `allowance` on the server). */
|
|
10
|
+
allowance?: boolean;
|
|
9
11
|
}): SettingsSectionDef[];
|
|
10
12
|
/**
|
|
11
13
|
* The assistant's Settings sections: the assistant, then Model providers, Models, Tools, Web and
|
|
12
14
|
* Spend under it. Pass them to LumenShell's `shared` (or an app's own `settings`).
|
|
13
15
|
*/
|
|
14
|
-
export declare function assistantSettingsSections({ client, name, web, }: {
|
|
16
|
+
export declare function assistantSettingsSections({ client, name, web, allowance, }: {
|
|
15
17
|
client: AssistantAdapter;
|
|
16
18
|
/** The assistant's name, the parent section's title. */
|
|
17
19
|
name: string;
|
|
18
20
|
/** Show the Web section (the app set up `web` on the server). */
|
|
19
21
|
web?: boolean;
|
|
22
|
+
/** Show Allowance: what is left on the shared keys, and Request more (`allowance` on the server). */
|
|
23
|
+
allowance?: boolean;
|
|
20
24
|
}): SettingsSectionDef[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useQueryClient } from '@tanstack/react-query';
|
|
3
|
-
import { CheckIcon, ExternalLinkIcon, GlobeIcon, KeyRoundIcon, LayersIcon, ReceiptIcon, SparklesIcon, Trash2Icon, WrenchIcon, } from 'lucide-react';
|
|
2
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { CheckIcon, CoinsIcon, ExternalLinkIcon, GlobeIcon, InboxIcon, KeyRoundIcon, LayersIcon, ReceiptIcon, SparklesIcon, Trash2Icon, WrenchIcon, } from 'lucide-react';
|
|
4
4
|
import { useId, useState } from 'react';
|
|
5
5
|
import { LumenChatgptSignin } from '../chatgpt-signin.js';
|
|
6
6
|
import { LumenExpandList, LumenExpandRow } from '../expand-list.js';
|
|
@@ -10,12 +10,14 @@ import { InfoLabel } from '../info-tip.js';
|
|
|
10
10
|
import { KeyTestLine, TestBadge, errorText } from '../integrations.js';
|
|
11
11
|
import { LumenSelector } from '../lumen-selector.js';
|
|
12
12
|
import { lumenToast } from '../lumen-toast.js';
|
|
13
|
+
import { ShareMeter } from '../share-meter.js';
|
|
13
14
|
import { Badge } from '../ui/badge.js';
|
|
15
|
+
import { Button } from '../ui/button.js';
|
|
14
16
|
import { Input } from '../ui/input.js';
|
|
15
17
|
import { Label } from '../ui/label.js';
|
|
16
18
|
import { Switch } from '../ui/switch.js';
|
|
17
19
|
import { ToggleGroup, ToggleGroupItem } from '../ui/toggle-group.js';
|
|
18
|
-
import { assistantStatusKey, usd, useAssistantStatus } from './lumen-assistant.js';
|
|
20
|
+
import { allowanceAmount, assistantStatusKey, usd, useAssistantStatus } from './lumen-assistant.js';
|
|
19
21
|
const EFFORTS = [
|
|
20
22
|
{ value: 'default', label: 'Model default' },
|
|
21
23
|
{ value: 'low', label: 'Low' },
|
|
@@ -158,7 +160,7 @@ function SharedProviderKey({ client, p }) {
|
|
|
158
160
|
/** Admins only: the app's shared keys and what each person may spend on them. */
|
|
159
161
|
function SharedKeys({ client }) {
|
|
160
162
|
const { apply } = useApply(client);
|
|
161
|
-
return (_jsx(WithStatus, { client: client, children: (s) => s.admin ? (_jsxs("div", { className: "flex flex-col", children: [_jsx(LumenExpandList, { children: s.providers.map((p) => (_jsx(SharedProviderKey, { client: client, p: p }, p.id))) }), _jsx(Row, { label: "Each person may spend", info: "A month on the shared keys, every provider together. Past it, a person's own key pays.", children: _jsx(CapInput, { label: "Allowance per person", value: s.caps.app, onSave: (n) => void apply('cap', () => client.appCap(n)) }) })] })) : (_jsx("p", { className: "text-muted-foreground text-sm", children: "Only an admin can change these." })) }));
|
|
163
|
+
return (_jsx(WithStatus, { client: client, children: (s) => s.admin ? (_jsxs("div", { className: "flex flex-col", children: [_jsx(LumenExpandList, { children: s.providers.map((p) => (_jsx(SharedProviderKey, { client: client, p: p }, p.id))) }), s.allowance?.period !== 'once' && (_jsx(Row, { label: "Each person may spend", info: "A month on the shared keys, every provider together. Past it, a person's own key pays.", children: _jsx(CapInput, { label: "Allowance per person", value: s.caps.app, onSave: (n) => void apply('cap', () => client.appCap(n)) }) }))] })) : (_jsx("p", { className: "text-muted-foreground text-sm", children: "Only an admin can change these." })) }));
|
|
162
164
|
}
|
|
163
165
|
// ---------------------------------------------------------------- models
|
|
164
166
|
function Models({ client }) {
|
|
@@ -225,11 +227,97 @@ function Spend({ client }) {
|
|
|
225
227
|
` · ${usd(sp.cap)} cap on ${sp.payer === 'app' ? 'the app key' : 'your key'}`] })] }), sp.rows.length ? (_jsx("div", { className: "flex flex-col text-sm", children: sp.rows.map((r) => (_jsxs("div", { className: "flex items-center gap-2 border-b py-2 last:border-b-0", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: r.label }), _jsx("span", { className: "text-muted-foreground text-xs", children: r.payer === 'app' ? 'Shared key' : 'Your key' }), _jsxs("span", { className: "w-16 text-right text-muted-foreground text-xs tabular-nums", children: [r.calls, " calls"] }), _jsx("span", { className: "w-16 text-right tabular-nums", children: usd(r.usd) })] }, `${r.label}:${r.payer}`))) })) : (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nothing spent this month." }))] }));
|
|
226
228
|
} }));
|
|
227
229
|
}
|
|
230
|
+
// ---------------------------------------------------------------- allowance
|
|
231
|
+
const day = (at) => new Date(at).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
|
232
|
+
function RequestBadge({ r }) {
|
|
233
|
+
if (r.status === 'open')
|
|
234
|
+
return _jsx(Badge, { variant: "outline", children: "Waiting" });
|
|
235
|
+
if (r.status === 'granted')
|
|
236
|
+
return _jsx(Badge, { variant: "secondary", children: "Granted" });
|
|
237
|
+
return _jsx(Badge, { variant: "outline", children: "Declined" });
|
|
238
|
+
}
|
|
239
|
+
/** A person's allowance on the shared keys, their newest request and Request more. */
|
|
240
|
+
function Allowance({ client }) {
|
|
241
|
+
const { apply, busy } = useApply(client);
|
|
242
|
+
const [amount, setAmount] = useState('');
|
|
243
|
+
const [why, setWhy] = useState('');
|
|
244
|
+
const whyId = useId();
|
|
245
|
+
return (_jsx(WithStatus, { client: client, children: (s) => {
|
|
246
|
+
const a = s.allowance;
|
|
247
|
+
const requestMore = client.requestMore;
|
|
248
|
+
if (!a || !requestMore)
|
|
249
|
+
return _jsx("p", { className: "text-muted-foreground text-sm", children: "This app sets no allowance." });
|
|
250
|
+
const n = (x) => allowanceAmount(a.unit, x);
|
|
251
|
+
const r = a.request;
|
|
252
|
+
const pick = amount || String(a.amounts[0] ?? '');
|
|
253
|
+
const issues = [
|
|
254
|
+
...(pick ? [] : [{ where: 'Amount', message: 'Pick how much to ask for' }]),
|
|
255
|
+
...(why.trim()
|
|
256
|
+
? []
|
|
257
|
+
: [{ where: 'What for', message: 'Say what it is for', target: whyId }]),
|
|
258
|
+
];
|
|
259
|
+
const ask = () => void apply('request', () => requestMore(Number(pick), why.trim()), 'Request sent').then((ok) => ok && setWhy(''));
|
|
260
|
+
return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex flex-col gap-2 border-b pb-3", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "font-semibold text-2xl tabular-nums", children: n(a.left) }), _jsxs("span", { className: "text-muted-foreground text-sm", children: ["left of ", n(a.total), a.period === 'month' ? ' this month' : ''] })] }), _jsx(ShareMeter, { value: a.total > 0 ? a.used / a.total : 1, wide: true })] }), r && (_jsx(Row, { label: `Asked for ${n(r.amount)} · ${day(r.at)}`, info: r.why || undefined, children: _jsxs("div", { className: "flex items-center gap-2 text-sm", children: [r.status === 'granted' && r.granted !== undefined && (_jsxs("span", { className: "tabular-nums", children: ["+", n(r.granted)] })), _jsx(RequestBadge, { r: r })] }) })), r?.status === 'open' ? (_jsx("p", { className: "pt-3 text-muted-foreground text-sm", children: "You can ask again once this one is decided." })) : (_jsxs("div", { className: "flex flex-col gap-2 pt-3", children: [_jsx(Input, { id: whyId, "aria-label": "What for", placeholder: "What it is for", className: "h-8 w-full", maxLength: 500, value: why, onChange: (e) => setWhy(e.target.value) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(LumenSelector, { label: "Amount", size: "sm", className: "w-40", value: pick, options: a.amounts.map((x) => ({ value: String(x), label: n(x) })), onChange: (v) => setAmount(v) }), _jsx(SaveButton, { issues: issues, label: "Request more", icon: null, busy: busy === 'request', onSave: ask })] })] }))] }));
|
|
261
|
+
} }));
|
|
262
|
+
}
|
|
263
|
+
/** One request, opened in place to grant (any amount) or decline. */
|
|
264
|
+
function RequestRow({ client, r, n, onDone, }) {
|
|
265
|
+
const [text, setText] = useState(String(r.amount));
|
|
266
|
+
const [busy, setBusy] = useState(null);
|
|
267
|
+
const amountId = useId();
|
|
268
|
+
const value = Number(text);
|
|
269
|
+
const decide = async (action) => {
|
|
270
|
+
if (!client.decideRequest)
|
|
271
|
+
return;
|
|
272
|
+
setBusy(action);
|
|
273
|
+
try {
|
|
274
|
+
await client.decideRequest(r.id, action, action === 'grant' ? value : undefined);
|
|
275
|
+
lumenToast.success(action === 'grant' ? `Granted ${n(value)}` : 'Declined');
|
|
276
|
+
onDone();
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
lumenToast.error(errorText(e));
|
|
280
|
+
}
|
|
281
|
+
finally {
|
|
282
|
+
setBusy(null);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
const issues = Number.isFinite(value) && value > 0
|
|
286
|
+
? []
|
|
287
|
+
: [{ where: 'Grant', message: 'Grant more than 0', target: amountId }];
|
|
288
|
+
return (_jsx(LumenExpandRow, { id: r.id, name: r.who, meta: `${n(r.amount)} · ${day(r.at)}`, badge: _jsx(RequestBadge, { r: r }), children: _jsxs("div", { className: "flex flex-col gap-3 text-sm", children: [_jsx("p", { className: "whitespace-pre-wrap", children: r.why || 'No reason given.' }), r.status === 'open' ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Input, { id: amountId, "aria-label": "Grant", inputMode: "decimal", className: "h-8 w-24 text-right tabular-nums", value: text, onChange: (e) => setText(e.target.value) }), _jsx(SaveButton, { issues: issues, label: "Grant", busy: busy === 'grant', onSave: () => void decide('grant') }), _jsx(Button, { size: "sm", variant: "outline", disabled: !!busy, onClick: () => void decide('decline'), children: "Decline" })] })) : (_jsxs("p", { className: "text-muted-foreground", children: [r.status === 'granted' ? `Granted ${n(r.granted ?? r.amount)}` : 'Declined', r.decided_by ? ` by ${r.decided_by}` : '', r.decided_at ? ` · ${day(r.decided_at)}` : ''] }))] }) }));
|
|
289
|
+
}
|
|
290
|
+
/** Approvers: every request for more allowance, open ones first. */
|
|
291
|
+
function AllowanceRequests({ client }) {
|
|
292
|
+
const qc = useQueryClient();
|
|
293
|
+
const key = [...assistantStatusKey(client), 'allowance-requests'];
|
|
294
|
+
const q = useQuery({
|
|
295
|
+
queryKey: key,
|
|
296
|
+
queryFn: () => client.allowanceRequests?.() ?? Promise.resolve([]),
|
|
297
|
+
enabled: !!client.allowanceRequests,
|
|
298
|
+
});
|
|
299
|
+
return (_jsx(WithStatus, { client: client, children: (s) => {
|
|
300
|
+
const a = s.allowance;
|
|
301
|
+
if (!a || !client.allowanceRequests)
|
|
302
|
+
return _jsx("p", { className: "text-muted-foreground text-sm", children: "This app sets no allowance." });
|
|
303
|
+
if (!a.approver)
|
|
304
|
+
return _jsx("p", { className: "text-muted-foreground text-sm", children: "Only an approver can see these." });
|
|
305
|
+
if (!q.data)
|
|
306
|
+
return (_jsx("p", { className: "text-muted-foreground text-sm", children: q.isError ? errorText(q.error) : 'Loading…' }));
|
|
307
|
+
if (!q.data.length)
|
|
308
|
+
return _jsx("p", { className: "text-muted-foreground text-sm", children: "No requests yet." });
|
|
309
|
+
const rows = [...q.data].sort((x, y) => Number(y.status === 'open') - Number(x.status === 'open') || y.at - x.at);
|
|
310
|
+
const done = () => {
|
|
311
|
+
void qc.invalidateQueries({ queryKey: assistantStatusKey(client) });
|
|
312
|
+
};
|
|
313
|
+
return (_jsx(LumenExpandList, { children: rows.map((r) => (_jsx(RequestRow, { client: client, r: r, n: (x) => allowanceAmount(a.unit, x), onDone: done }, r.id))) }));
|
|
314
|
+
} }));
|
|
315
|
+
}
|
|
228
316
|
/**
|
|
229
317
|
* The admin's section: the app's shared keys and each person's monthly allowance on them. Put it
|
|
230
318
|
* where the app keeps its admin settings, shown only to admins.
|
|
231
319
|
*/
|
|
232
|
-
export function assistantAdminSections({ client, }) {
|
|
320
|
+
export function assistantAdminSections({ client, allowance = false, }) {
|
|
233
321
|
return [
|
|
234
322
|
{
|
|
235
323
|
id: 'assistant-shared-keys',
|
|
@@ -238,13 +326,24 @@ export function assistantAdminSections({ client, }) {
|
|
|
238
326
|
description: "Keys everyone's answers run on, and the allowance",
|
|
239
327
|
content: _jsx(SharedKeys, { client: client }),
|
|
240
328
|
},
|
|
329
|
+
...(allowance
|
|
330
|
+
? [
|
|
331
|
+
{
|
|
332
|
+
id: 'assistant-allowance-requests',
|
|
333
|
+
title: 'Allowance requests',
|
|
334
|
+
icon: InboxIcon,
|
|
335
|
+
description: 'Grant or decline asks for more allowance',
|
|
336
|
+
content: _jsx(AllowanceRequests, { client: client }),
|
|
337
|
+
},
|
|
338
|
+
]
|
|
339
|
+
: []),
|
|
241
340
|
];
|
|
242
341
|
}
|
|
243
342
|
/**
|
|
244
343
|
* The assistant's Settings sections: the assistant, then Model providers, Models, Tools, Web and
|
|
245
344
|
* Spend under it. Pass them to LumenShell's `shared` (or an app's own `settings`).
|
|
246
345
|
*/
|
|
247
|
-
export function assistantSettingsSections({ client, name, web = true, }) {
|
|
346
|
+
export function assistantSettingsSections({ client, name, web = true, allowance = false, }) {
|
|
248
347
|
return [
|
|
249
348
|
{
|
|
250
349
|
id: 'assistant',
|
|
@@ -297,5 +396,17 @@ export function assistantSettingsSections({ client, name, web = true, }) {
|
|
|
297
396
|
description: 'What answers cost this month',
|
|
298
397
|
content: _jsx(Spend, { client: client }),
|
|
299
398
|
},
|
|
399
|
+
...(allowance
|
|
400
|
+
? [
|
|
401
|
+
{
|
|
402
|
+
id: 'assistant-allowance',
|
|
403
|
+
title: 'Allowance',
|
|
404
|
+
icon: CoinsIcon,
|
|
405
|
+
sub: true,
|
|
406
|
+
description: 'What is left on the shared keys, and Request more',
|
|
407
|
+
content: _jsx(Allowance, { client: client }),
|
|
408
|
+
},
|
|
409
|
+
]
|
|
410
|
+
: []),
|
|
300
411
|
];
|
|
301
412
|
}
|
package/dist/shell.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ interface ShellContext {
|
|
|
21
21
|
headerActions: ReactNode;
|
|
22
22
|
}
|
|
23
23
|
export declare function useShell(): ShellContext;
|
|
24
|
+
/** Opens Settings at a section, or null outside a shell (a preview). */
|
|
25
|
+
export declare function useOpenSettings(): ShellContext['openSettings'] | null;
|
|
24
26
|
/** The app this page renders in, or null outside a shell (a preview). */
|
|
25
27
|
export declare function useApp(): {
|
|
26
28
|
id: string;
|
package/dist/shell.js
CHANGED
|
@@ -27,6 +27,10 @@ export function useShell() {
|
|
|
27
27
|
throw new Error('useShell must be used inside <LumenShell>.');
|
|
28
28
|
return c;
|
|
29
29
|
}
|
|
30
|
+
/** Opens Settings at a section, or null outside a shell (a preview). */
|
|
31
|
+
export function useOpenSettings() {
|
|
32
|
+
return useContext(Ctx)?.openSettings ?? null;
|
|
33
|
+
}
|
|
30
34
|
/** The app this page renders in, or null outside a shell (a preview). */
|
|
31
35
|
export function useApp() {
|
|
32
36
|
const c = useContext(Ctx);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.84",
|
|
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.84",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|