@columenlabs/lumen-ui 0.1.79 → 0.1.80

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,5 +1,5 @@
1
1
  export { type AssistantAdapter, assistantClient } from './client.js';
2
2
  export { type AssistantCommand, type ComposerExtra, LumenAssistant, type LumenAssistantProps, LumenComposer, assistantStatusKey, useAssistantStatus, useLumenAssistantDock, } from './lumen-assistant.js';
3
- export { assistantSettingsSections } from './settings.js';
3
+ export { assistantAdminSections, assistantSettingsSections } from './settings.js';
4
4
  export { type AssistantSource, type AssistantSourceItem, type ComposeRequest, type PageSelection, composeAssistant, selectionRef, useAssistantSource, usePageSelection, useShownAssistantSources, } from './sources.js';
5
5
  export { type AssistantChatState, type LiveItem, applyEvent, useAssistantChat, } from './use-assistant-chat.js';
@@ -3,6 +3,6 @@
3
3
  // `@columenlabs/lumen-ui-types/assistant`.
4
4
  export { assistantClient } from './client.js';
5
5
  export { LumenAssistant, LumenComposer, assistantStatusKey, useAssistantStatus, useLumenAssistantDock, } from './lumen-assistant.js';
6
- export { assistantSettingsSections } from './settings.js';
6
+ export { assistantAdminSections, assistantSettingsSections } from './settings.js';
7
7
  export { composeAssistant, selectionRef, useAssistantSource, usePageSelection, useShownAssistantSources, } from './sources.js';
8
8
  export { applyEvent, useAssistantChat, } from './use-assistant-chat.js';
@@ -1,5 +1,12 @@
1
1
  import type { SettingsSectionDef } from '../settings.js';
2
2
  import type { AssistantAdapter } from './client.js';
3
+ /**
4
+ * The admin's section: the app's shared keys and each person's monthly allowance on them. Put it
5
+ * where the app keeps its admin settings, shown only to admins.
6
+ */
7
+ export declare function assistantAdminSections({ client, }: {
8
+ client: AssistantAdapter;
9
+ }): SettingsSectionDef[];
3
10
  /**
4
11
  * The assistant's Settings sections: the assistant, then Model providers, Models, Tools, Web and
5
12
  * Spend under it. Pass them to LumenShell's `shared` (or an app's own `settings`).
@@ -173,28 +173,31 @@ function ChatgptSignin({ client, p }) {
173
173
  function SigninPaste({ id, redirect, pasted, setPasted, issues, busy, finish, }) {
174
174
  return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { htmlFor: id, children: _jsx(InfoLabel, { info: `The sign-in ends on ${redirect}, a page that does not load. That is expected: copy the whole address from that tab's address bar.`, children: "Paste the address the sign-in ended on" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { id: id, autoComplete: "off", placeholder: `${redirect}?code=…`, className: "font-code text-xs", value: pasted, onChange: (e) => setPasted(e.target.value) }), _jsx(SaveButton, { issues: issues, busy: busy, disabled: !pasted.trim(), label: "Finish", onSave: finish })] })] }));
175
175
  }
176
- /** One provider, collapsed to a row: where to make a key (a new tab), the app's key and the person's. */
177
- function ProviderKeys({ client, p, admin, }) {
176
+ const ready = (_jsxs(Badge, { variant: "outline", className: "text-gain", children: [_jsx(CheckIcon, {}), "Ready"] }));
177
+ const notSet = _jsx(Badge, { variant: "outline", children: "Not set" });
178
+ /** One provider, collapsed to a row: the person's own key (or their ChatGPT plan sign-in). */
179
+ function OwnProviderKey({ client, p }) {
178
180
  const { apply, refresh } = useApply(client);
179
- const appOn = p.app.set || !!p.app.from_env;
180
181
  const plan = p.own.via === 'chatgpt';
181
- const meta = [
182
- appOn ? 'Shared key set' : 'No shared key',
183
- plan ? 'your ChatGPT plan' : p.own.set ? 'your own key' : null,
184
- ]
185
- .filter(Boolean)
186
- .join(' · ');
187
- const failed = [p.app, p.own].some((k) => k.set && k.test && !k.test.ok);
188
- const badge = failed ? (_jsx(TestBadge, { test: { ok: false } })) : appOn || p.own.set ? (_jsxs(Badge, { variant: "outline", className: "text-gain", children: [_jsx(CheckIcon, {}), "Ready"] })) : (_jsx(Badge, { variant: "outline", children: "Not set" }));
189
- const ownKey = (_jsx(KeyBlock, { label: p.signin && !p.own.set ? 'Or your own API key' : 'Your own key', prefix: p.prefix, provider: p.label, state: p.own, canEdit: true, keysUrl: p.keys_url, onSave: (k) => apply(`own:${p.id}`, () => client.ownKey(p.id, k), 'Key saved'), onRemove: () => apply(`own:${p.id}`, () => client.ownKey(p.id, null), 'Key removed'), onTest: () => client.testOwnKey(p.id).finally(refresh) }));
190
- return (_jsx(LumenExpandRow, { id: p.id, name: p.label, meta: meta, badge: badge, children: _jsxs("div", { className: "flex flex-col", children: [_jsx(KeyBlock, { label: "Shared key", info: "An admin sets it once; everyone uses it, within each person's monthly cap.", prefix: p.prefix, provider: p.label, state: p.app, canEdit: admin, keysUrl: p.keys_url, onSave: (k) => apply(`app:${p.id}`, () => client.appKey(p.id, k), 'Key saved'), onRemove: () => apply(`app:${p.id}`, () => client.appKey(p.id, null), 'Key removed'), onTest: () => client.testAppKey(p.id).finally(refresh) }), p.signin === 'chatgpt' && (!p.own.set || plan) && _jsx(ChatgptSignin, { client: client, p: p }), !plan && ownKey] }) }));
182
+ const badge = p.own.set && p.own.test && !p.own.test.ok ? (_jsx(TestBadge, { test: { ok: false } })) : p.own.set ? (ready) : (notSet);
183
+ return (_jsx(LumenExpandRow, { id: p.id, name: p.label, meta: plan ? 'Your ChatGPT plan' : p.own.set ? 'Your key' : 'No key', badge: badge, children: _jsxs("div", { className: "flex flex-col", children: [p.signin === 'chatgpt' && (!p.own.set || plan) && _jsx(ChatgptSignin, { client: client, p: p }), !plan && (_jsx(KeyBlock, { label: p.signin && !p.own.set ? 'Or your API key' : 'Your key', prefix: p.prefix, provider: p.label, state: p.own, canEdit: true, keysUrl: p.keys_url, onSave: (k) => apply(`own:${p.id}`, () => client.ownKey(p.id, k), 'Key saved'), onRemove: () => apply(`own:${p.id}`, () => client.ownKey(p.id, null), 'Key removed'), onTest: () => client.testOwnKey(p.id).finally(refresh) }))] }) }));
191
184
  }
185
+ /** A person's own keys, one row a provider, and what they may spend on them. */
192
186
  function Providers({ client }) {
193
187
  const { apply } = useApply(client);
194
- return (_jsx(WithStatus, { client: client, children: (s) => {
195
- const ownSet = s.providers.some((p) => p.own.set);
196
- return (_jsxs("div", { className: "flex flex-col", children: [_jsx(LumenExpandList, { children: s.providers.map((p) => (_jsx(ProviderKeys, { client: client, p: p, admin: s.admin }, p.id))) }), s.admin && (_jsx(Row, { label: "Each person may spend", info: "On the shared keys, every provider together.", children: _jsx(CapInput, { label: "Cap per person", value: s.caps.app, onSave: (n) => void apply('cap', () => client.appCap(n)) }) })), ownSet && (_jsx(Row, { label: "Your keys may spend", info: "On your own keys, every provider together.", children: _jsx(CapInput, { label: "Your monthly cap", value: s.caps.own, onSave: (n) => void apply('own', () => client.ownCap(n)) }) })), _jsx(Row, { label: "Pay with first", info: "When it reaches its cap this month, or has no key for the tier's provider, the other pays.", children: _jsxs(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, value: s.pay, "aria-label": "Pay with", onValueChange: (v) => v && void apply('pay', () => client.settings({ pay: v })), children: [_jsx(ToggleGroupItem, { value: "app", children: "Shared keys" }), _jsx(ToggleGroupItem, { value: "own", disabled: !ownSet, children: "Your keys" })] }) })] }));
197
- } }));
188
+ return (_jsx(WithStatus, { client: client, children: (s) => (_jsxs("div", { className: "flex flex-col", children: [_jsx(LumenExpandList, { children: s.providers.map((p) => (_jsx(OwnProviderKey, { client: client, p: p }, p.id))) }), s.providers.some((p) => p.own.set) && (_jsx(Row, { 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)) }) }))] })) }));
189
+ }
190
+ /** One provider's shared key, which an admin sets for everyone. */
191
+ function SharedProviderKey({ client, p }) {
192
+ const { apply, refresh } = useApply(client);
193
+ const on = p.app.set || !!p.app.from_env;
194
+ const badge = p.app.set && p.app.test && !p.app.test.ok ? (_jsx(TestBadge, { test: { ok: false } })) : on ? (ready) : (notSet);
195
+ return (_jsx(LumenExpandRow, { id: p.id, name: p.label, meta: p.app.from_env ? 'From the server' : on ? 'Shared key' : 'No key', badge: badge, children: _jsx(KeyBlock, { label: "Shared key", info: "Everyone's answers run on it, within the monthly allowance below.", prefix: p.prefix, provider: p.label, state: p.app, canEdit: true, keysUrl: p.keys_url, onSave: (k) => apply(`app:${p.id}`, () => client.appKey(p.id, k), 'Key saved'), onRemove: () => apply(`app:${p.id}`, () => client.appKey(p.id, null), 'Key removed'), onTest: () => client.testAppKey(p.id).finally(refresh) }) }));
196
+ }
197
+ /** Admins only: the app's shared keys and what each person may spend on them. */
198
+ function SharedKeys({ client }) {
199
+ const { apply } = useApply(client);
200
+ 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." })) }));
198
201
  }
199
202
  // ---------------------------------------------------------------- models
200
203
  function Models({ client }) {
@@ -261,6 +264,21 @@ function Spend({ client }) {
261
264
  ` · ${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." }))] }));
262
265
  } }));
263
266
  }
267
+ /**
268
+ * The admin's section: the app's shared keys and each person's monthly allowance on them. Put it
269
+ * where the app keeps its admin settings, shown only to admins.
270
+ */
271
+ export function assistantAdminSections({ client, }) {
272
+ return [
273
+ {
274
+ id: 'assistant-shared-keys',
275
+ title: 'Shared model keys',
276
+ icon: KeyRoundIcon,
277
+ description: "Keys everyone's answers run on, and the allowance",
278
+ content: _jsx(SharedKeys, { client: client }),
279
+ },
280
+ ];
281
+ }
264
282
  /**
265
283
  * The assistant's Settings sections: the assistant, then Model providers, Models, Tools, Web and
266
284
  * Spend under it. Pass them to LumenShell's `shared` (or an app's own `settings`).
@@ -279,7 +297,7 @@ export function assistantSettingsSections({ client, name, web = true, }) {
279
297
  title: 'Model providers',
280
298
  icon: KeyRoundIcon,
281
299
  sub: true,
282
- description: 'Keys for each provider, and who pays',
300
+ description: 'Your own key for each provider',
283
301
  content: _jsx(Providers, { client: client }),
284
302
  },
285
303
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.79",
3
+ "version": "0.1.80",
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.79",
52
+ "@columenlabs/lumen-ui-types": "0.1.80",
53
53
  "@dnd-kit/core": "6.3.1",
54
54
  "@dnd-kit/sortable": "10.0.0",
55
55
  "@dnd-kit/utilities": "3.2.2",