@columenlabs/lumen-ui 0.1.190 → 0.1.191
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/copy.js +2 -0
- package/dist/assistant/lumen-assistant.js +77 -3
- package/dist/assistant/settings.js +4 -1
- package/dist/assistant/use-assistant-chat.d.ts +3 -1
- package/dist/assistant/use-assistant-chat.js +65 -3
- package/dist/assistant-screen.d.ts +9 -16
- package/dist/assistant-screen.js +26 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shell.js +13 -12
- package/package.json +2 -2
package/dist/assistant/copy.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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 { ArrowUpIcon, CheckIcon, ChevronRightIcon, CircleAlertIcon, CopyIcon, CornerDownRightIcon, CrosshairIcon, FileTextIcon, LightbulbIcon, PencilIcon, PlusIcon, SparklesIcon, SquareIcon, Undo2Icon, WrenchIcon, XIcon, } from 'lucide-react';
|
|
3
|
+
import { ArrowUpIcon, CheckIcon, ChevronRightIcon, CircleAlertIcon, CopyIcon, CornerDownRightIcon, CrosshairIcon, FileTextIcon, LightbulbIcon, MonitorIcon, PencilIcon, PlusIcon, SparklesIcon, SquareIcon, TriangleAlertIcon, Undo2Icon, WrenchIcon, XIcon, } from 'lucide-react';
|
|
4
4
|
import { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
5
5
|
import { LumenAssistantButton, LumenAssistantChats, LumenAssistantPanel, assistantOpen, useAssistantHotkey, } from '../assistant-panel.js';
|
|
6
|
+
import { screenMapFor, useAssistantScreen } from '../assistant-screen.js';
|
|
6
7
|
import { IconButton } from '../icon-button.js';
|
|
7
8
|
import { errorText } from '../integrations.js';
|
|
8
9
|
import { lazyPart } from '../lazy-part.js';
|
|
@@ -68,6 +69,43 @@ export function LumenAssistant({ client, context, onSettings, keepDays = 30, onC
|
|
|
68
69
|
lumenToast.error(errorText(e));
|
|
69
70
|
}
|
|
70
71
|
};
|
|
72
|
+
// The assistant moves the screen (Settings ▸ Screen): the page map goes with each message, and
|
|
73
|
+
// each new screen call runs here once; how it ended goes with the next message.
|
|
74
|
+
const screen = useAssistantScreen();
|
|
75
|
+
const screenOn = !!screen && (s?.screen ?? 'move') === 'move';
|
|
76
|
+
const extra = () => ({
|
|
77
|
+
...(context ? { context } : {}),
|
|
78
|
+
...(screenOn && screen ? { screen: screenMapFor(screen) } : {}),
|
|
79
|
+
});
|
|
80
|
+
const ran = useRef(new Set());
|
|
81
|
+
const ended = chat.screenEnded;
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
for (const i of screenCalls(chat.items)) {
|
|
84
|
+
if (!i.live || ran.current.has(i.id))
|
|
85
|
+
continue;
|
|
86
|
+
ran.current.add(i.id);
|
|
87
|
+
if (!screen) {
|
|
88
|
+
ended({ id: i.id, status: 'failed', text: 'this page cannot be moved' });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
void screen.run(i.id, i.moves).then((o) => {
|
|
92
|
+
ended({ id: i.id, ...o });
|
|
93
|
+
// On a phone the panel covers the page: it steps aside so the move shows.
|
|
94
|
+
if (o.status === 'done' && onClose && window.matchMedia('(max-width: 767px)').matches)
|
|
95
|
+
onClose();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}, [chat.items, screen, ended, onClose]);
|
|
99
|
+
const controls = screen
|
|
100
|
+
? {
|
|
101
|
+
canUndo: (id) => screen.canUndo(id),
|
|
102
|
+
undo: (id) => void screen.undo(id).then((o) => ended({ id, ...o })),
|
|
103
|
+
go: (item) => {
|
|
104
|
+
ran.current.add(item.id);
|
|
105
|
+
void screen.run(item.id, item.moves, true).then((o) => ended({ id: item.id, ...o }));
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
: null;
|
|
71
109
|
const tiers = s?.tiers ?? [];
|
|
72
110
|
const above = tiers.slice(tiers.findIndex((t) => t.id === s?.tier) + 1);
|
|
73
111
|
// The kit's commands; the app's own (`commands`, then the server's) come first and win a name.
|
|
@@ -121,7 +159,7 @@ export function LumenAssistant({ client, context, onSettings, keepDays = 30, onC
|
|
|
121
159
|
args: '<task>',
|
|
122
160
|
run: (task) => {
|
|
123
161
|
if (task.trim())
|
|
124
|
-
chat.send(task, { ...(
|
|
162
|
+
chat.send(task, { ...extra(), tier: above[0]?.id });
|
|
125
163
|
return undefined;
|
|
126
164
|
},
|
|
127
165
|
},
|
|
@@ -142,7 +180,7 @@ export function LumenAssistant({ client, context, onSettings, keepDays = 30, onC
|
|
|
142
180
|
id: c.id,
|
|
143
181
|
title: c.title,
|
|
144
182
|
updated_at: new Date(c.at).toISOString(),
|
|
145
|
-
})) ?? (chats.isFetched ? [] : null), value: chat.chatId ?? undefined, note: `Chats are kept ${keepDays} days after their last message.`, onRefresh: () => void chats.refetch(), onOpen: (c) => void chat.open(c.id) }), _jsx(IconButton, { icon: CopyIcon, label: "Copy chat", disabled: !chat.items.length, onClick: () => copyMarkdown(chatMarkdown(chat.title, chat.items, name), 'Chat') }), _jsx(IconButton, { icon: PlusIcon, label: "New chat", disabled: !chat.items.length, onClick: chat.reset }), onClose && (_jsx(IconButton, { icon: XIcon, label: `Close ${s?.name ?? 'assistant'}`, onClick: onClose }))] }), _jsx(AssistantName.Provider, { value: name, children: _jsx(Messages, { chat: chat, status: s, onSettings: onSettings }) }), _jsx(LumenComposer, { name: s?.name ?? 'the assistant', busy: chat.busy, queued: chat.queued, disabled: !s || !!s.wait, commands: allCommands, tiers: tiers, sources: allSources, page: context?.label, onSend: (text,
|
|
183
|
+
})) ?? (chats.isFetched ? [] : null), value: chat.chatId ?? undefined, note: `Chats are kept ${keepDays} days after their last message.`, onRefresh: () => void chats.refetch(), onOpen: (c) => void chat.open(c.id) }), _jsx(IconButton, { icon: CopyIcon, label: "Copy chat", disabled: !chat.items.length, onClick: () => copyMarkdown(chatMarkdown(chat.title, chat.items, name), 'Chat') }), _jsx(IconButton, { icon: PlusIcon, label: "New chat", disabled: !chat.items.length, onClick: chat.reset }), onClose && (_jsx(IconButton, { icon: XIcon, label: `Close ${s?.name ?? 'assistant'}`, onClick: onClose }))] }), _jsx(AssistantName.Provider, { value: name, children: _jsx(Screen.Provider, { value: controls, children: _jsx(Messages, { chat: chat, status: s, onSettings: onSettings }) }) }), _jsx(LumenComposer, { name: s?.name ?? 'the assistant', busy: chat.busy, queued: chat.queued, disabled: !s || !!s.wait, commands: allCommands, tiers: tiers, sources: allSources, page: context?.label, onSend: (text, more) => chat.send(text, { ...extra(), ...more }), onStop: chat.stop }), _jsx(UsageLine, { usage: chat.usage })] }));
|
|
146
184
|
}
|
|
147
185
|
function Messages({ chat, status, onSettings, }) {
|
|
148
186
|
const end = useRef(null);
|
|
@@ -223,6 +261,8 @@ function Item({ item, onSettle, busy, }) {
|
|
|
223
261
|
return _jsx(Step, { step: item, live: !!item.live });
|
|
224
262
|
case 'draft':
|
|
225
263
|
return _jsx(Draft, { draft: item, onSettle: onSettle, busy: busy });
|
|
264
|
+
case 'screen':
|
|
265
|
+
return _jsx(ScreenChip, { item: item });
|
|
226
266
|
case 'handoff':
|
|
227
267
|
return _jsx(HandOff, { item: item, onSettle: onSettle, busy: busy });
|
|
228
268
|
case 'advice':
|
|
@@ -239,6 +279,40 @@ function Item({ item, onSettle, busy, }) {
|
|
|
239
279
|
return (_jsxs("p", { role: "alert", className: "flex items-start gap-1.5 text-loss text-sm", children: [_jsx(CircleAlertIcon, { className: "mt-0.5 size-4 shrink-0" }), item.text] }));
|
|
240
280
|
}
|
|
241
281
|
}
|
|
282
|
+
/** Every screen call in the chat, hand-off sections included. */
|
|
283
|
+
function screenCalls(items) {
|
|
284
|
+
return items.flatMap((i) => i.kind === 'screen' ? [i] : i.kind === 'handoff' ? screenCalls(i.items) : []);
|
|
285
|
+
}
|
|
286
|
+
const Screen = createContext(null);
|
|
287
|
+
/** A screen call's moves in words, for the chip's hover. */
|
|
288
|
+
const movesText = (moves) => moves
|
|
289
|
+
.map((m) => 'go' in m
|
|
290
|
+
? `Go to ${m.go}`
|
|
291
|
+
: 'settings' in m
|
|
292
|
+
? `Settings ▸ ${m.settings}${m.row ? ` ▸ ${m.row}` : ''}`
|
|
293
|
+
: 'chart' in m
|
|
294
|
+
? `Chart ${[m.chart].flat().join(', ')}`
|
|
295
|
+
: 'open' in m
|
|
296
|
+
? `Open ${m.open}`
|
|
297
|
+
: `Point at ${m.point.replace(/^card:/, '')}`)
|
|
298
|
+
.join(' · ');
|
|
299
|
+
/**
|
|
300
|
+
* A screen call in the chat: ✓ and what is on screen, with Undo; why it failed; or, while the
|
|
301
|
+
* person was mid-edit, Go now.
|
|
302
|
+
*/
|
|
303
|
+
function ScreenChip({ item }) {
|
|
304
|
+
const c = useContext(Screen);
|
|
305
|
+
// No outcome and not running: it ran in another tab or before a reload, which kept no result.
|
|
306
|
+
const status = item.outcome?.status ?? (item.live ? 'running' : 'sent');
|
|
307
|
+
const said = status === 'failed'
|
|
308
|
+
? `Couldn't move · ${item.outcome?.text ?? ''}`
|
|
309
|
+
: status === 'waiting'
|
|
310
|
+
? 'Waits while you edit'
|
|
311
|
+
: status === 'undone'
|
|
312
|
+
? 'Undone'
|
|
313
|
+
: null;
|
|
314
|
+
return (_jsxs("div", { "data-slot": "assistant-screen", "data-status": status, className: "flex min-w-0 items-center gap-2 rounded-lg border bg-muted/30 px-3 py-1.5 text-muted-foreground text-xs", children: [status === 'running' ? (_jsx(Spinner, { className: "size-3.5" })) : status === 'done' ? (_jsx(CheckIcon, { className: "size-3.5 shrink-0 text-edge" })) : status === 'failed' ? (_jsx(TriangleAlertIcon, { className: "size-3.5 shrink-0 text-loss" })) : status === 'undone' ? (_jsx(Undo2Icon, { className: "size-3.5 shrink-0" })) : (_jsx(MonitorIcon, { className: "size-3.5 shrink-0" })), _jsxs("span", { className: "min-w-0 flex-1 truncate", title: `${item.label} · ${movesText(item.moves)}`, children: [_jsx("span", { className: cn(status === 'undone' ? 'line-through' : 'text-foreground'), children: item.label }), said && _jsxs("span", { children: [" \u00B7 ", said] })] }), status === 'done' && c?.canUndo(item.id) && (_jsx(IconButton, { icon: Undo2Icon, label: "Undo", size: "icon-xs", onClick: () => c.undo(item.id) })), status === 'waiting' && c && (_jsx(IconButton, { icon: MonitorIcon, label: "Go now", variant: "outline", size: "icon-xs", onClick: () => c.go(item) }))] }));
|
|
315
|
+
}
|
|
242
316
|
const Rule = ({ children }) => (_jsx("p", { className: "flex items-center gap-2 text-muted-foreground text-xs before:h-px before:flex-1 before:bg-border after:h-px after:flex-1 after:bg-border", children: children }));
|
|
243
317
|
function Step({ step, live }) {
|
|
244
318
|
const undo = useContext(UndoWrite);
|
|
@@ -85,7 +85,10 @@ function General({ client }) {
|
|
|
85
85
|
value: t.id,
|
|
86
86
|
label: t.label,
|
|
87
87
|
})),
|
|
88
|
-
], onChange: (advice) => void set({ advice }) }) }))
|
|
88
|
+
], onChange: (advice) => void set({ advice }) }) })), _jsx(Row, { label: "Screen", info: "Takes you to a page, a Settings row, a chart or a card when you ask. Each move has an Undo; nothing is saved.", children: _jsx(LumenSelector, { label: "Screen", size: "sm", className: "w-44", value: s.screen ?? 'move', options: [
|
|
89
|
+
{ value: 'move', label: 'Move' },
|
|
90
|
+
{ value: 'off', label: 'Off' },
|
|
91
|
+
], onChange: (screen) => void set({ screen: screen }) }) })] }));
|
|
89
92
|
} }));
|
|
90
93
|
}
|
|
91
94
|
// ---------------------------------------------------------------- model providers
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantEvent, AssistantMessage, ChatItem, ChatUsage, DraftItem, StepItem } from '@columenlabs/lumen-ui-types/assistant';
|
|
1
|
+
import type { AssistantEvent, AssistantMessage, AssistantScreenOutcome, ChatItem, ChatUsage, DraftItem, StepItem } from '@columenlabs/lumen-ui-types/assistant';
|
|
2
2
|
import type { AssistantAdapter } from './client.js';
|
|
3
3
|
/** An item as the panel draws it: a text still streaming, a step still running. */
|
|
4
4
|
export type LiveItem = ChatItem & {
|
|
@@ -22,6 +22,8 @@ export interface AssistantChatState {
|
|
|
22
22
|
settle: (draft: DraftItem, action: 'save' | 'dismiss') => Promise<void>;
|
|
23
23
|
/** Takes back a change the assistant made at once (Undo on its line). */
|
|
24
24
|
undo: (step: StepItem) => Promise<void>;
|
|
25
|
+
/** A screen call ended in the browser: shown on its chip, and sent with the next message. */
|
|
26
|
+
screenEnded: (outcome: AssistantScreenOutcome) => void;
|
|
25
27
|
}
|
|
26
28
|
/** A copy of the items with one event applied (hand-off sections copied too). */
|
|
27
29
|
export declare function applyEvent(prev: LiveItem[], e: AssistantEvent): LiveItem[];
|
|
@@ -9,6 +9,27 @@ function browserZone() {
|
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
// Screen calls' outcomes not yet sent (they go with the next message), kept for this tab so a
|
|
13
|
+
// reload neither loses them nor leaves their chips without a result.
|
|
14
|
+
const SCREEN_KEY = 'lumen-assistant:screen';
|
|
15
|
+
function readOutcomes() {
|
|
16
|
+
try {
|
|
17
|
+
const m = new Map(JSON.parse(sessionStorage.getItem(SCREEN_KEY) ?? '[]'));
|
|
18
|
+
return m;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return new Map();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function keepOutcomes(m) {
|
|
25
|
+
try {
|
|
26
|
+
if (m.size)
|
|
27
|
+
sessionStorage.setItem(SCREEN_KEY, JSON.stringify([...m].slice(-50)));
|
|
28
|
+
else
|
|
29
|
+
sessionStorage.removeItem(SCREEN_KEY);
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
12
33
|
/** Where an event lands: the chat, or the last hand-off section when it came from inside one. */
|
|
13
34
|
function sinkOf(items, handoff) {
|
|
14
35
|
if (!handoff)
|
|
@@ -62,6 +83,12 @@ export function applyEvent(prev, e) {
|
|
|
62
83
|
sink.push(draft);
|
|
63
84
|
break;
|
|
64
85
|
}
|
|
86
|
+
case 'screen': {
|
|
87
|
+
// Live until the browser runs it: the panel runs a screen call once, while it is new.
|
|
88
|
+
const { handoff: _h, ...call } = e.data;
|
|
89
|
+
sink.push({ ...call, live: true });
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
65
92
|
case 'handoff':
|
|
66
93
|
items.push({ ...e.data, items: [] });
|
|
67
94
|
break;
|
|
@@ -102,11 +129,14 @@ export function applyEvent(prev, e) {
|
|
|
102
129
|
}
|
|
103
130
|
return items;
|
|
104
131
|
}
|
|
105
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Nothing streams any more: a text or step cut by Stop ends where it was. A screen call stays live
|
|
134
|
+
* until the browser has run it (`screenEnded`).
|
|
135
|
+
*/
|
|
106
136
|
const settled = (items) => items.map((i) => {
|
|
107
137
|
if (i.kind === 'handoff')
|
|
108
138
|
return { ...i, items: settled(i.items) };
|
|
109
|
-
if (!i.live)
|
|
139
|
+
if (!i.live || i.kind === 'screen')
|
|
110
140
|
return i;
|
|
111
141
|
const { live: _l, ...rest } = i;
|
|
112
142
|
return rest;
|
|
@@ -120,6 +150,8 @@ export function useAssistantChat(client) {
|
|
|
120
150
|
const [loading, setLoading] = useState(false);
|
|
121
151
|
const [queued, setQueued] = useState(null);
|
|
122
152
|
const abort = useRef(null);
|
|
153
|
+
const outcomes = useRef(null);
|
|
154
|
+
outcomes.current ??= readOutcomes();
|
|
123
155
|
const idRef = useRef(null);
|
|
124
156
|
idRef.current = chatId;
|
|
125
157
|
const run = useCallback(async (message, extra) => {
|
|
@@ -138,12 +170,17 @@ export function useAssistantChat(client) {
|
|
|
138
170
|
...(extra?.tier ? { tier: extra.tier } : {}),
|
|
139
171
|
},
|
|
140
172
|
]);
|
|
173
|
+
// Every screen outcome not yet sent goes with this message; kept again if it fails.
|
|
174
|
+
const sent = [...(outcomes.current?.values() ?? [])];
|
|
175
|
+
outcomes.current?.clear();
|
|
176
|
+
keepOutcomes(new Map());
|
|
141
177
|
try {
|
|
142
178
|
await client.send({
|
|
143
179
|
message,
|
|
144
180
|
...(idRef.current ? { chat_id: idRef.current } : {}),
|
|
145
181
|
...(browserZone() ? { time_zone: browserZone() } : {}),
|
|
146
182
|
...extra,
|
|
183
|
+
...(sent.length ? { outcomes: sent } : {}),
|
|
147
184
|
}, (e) => {
|
|
148
185
|
if (e.event === 'chat') {
|
|
149
186
|
idRef.current = e.data.id;
|
|
@@ -159,6 +196,11 @@ export function useAssistantChat(client) {
|
|
|
159
196
|
}, ctl.signal);
|
|
160
197
|
}
|
|
161
198
|
catch (err) {
|
|
199
|
+
for (const o of sent)
|
|
200
|
+
if (!outcomes.current?.has(o.id))
|
|
201
|
+
outcomes.current?.set(o.id, o);
|
|
202
|
+
if (outcomes.current)
|
|
203
|
+
keepOutcomes(outcomes.current);
|
|
162
204
|
if (ctl.signal.aborted)
|
|
163
205
|
setItems((p) => [...p, { kind: 'stopped' }]);
|
|
164
206
|
else
|
|
@@ -205,7 +247,14 @@ export function useAssistantChat(client) {
|
|
|
205
247
|
idRef.current = c.id;
|
|
206
248
|
setChatId(c.id);
|
|
207
249
|
setTitle(c.title);
|
|
208
|
-
|
|
250
|
+
// A call that ended here but whose outcome is not sent yet shows it already.
|
|
251
|
+
const kept = outcomes.current ?? new Map();
|
|
252
|
+
setItems(c.items.map((i) => {
|
|
253
|
+
const o = i.kind === 'screen' && !i.outcome ? kept.get(i.id) : undefined;
|
|
254
|
+
return o && i.kind === 'screen'
|
|
255
|
+
? { ...i, outcome: { status: o.status, ...(o.text ? { text: o.text } : {}) } }
|
|
256
|
+
: i;
|
|
257
|
+
}));
|
|
209
258
|
setUsage(c.usage ?? null);
|
|
210
259
|
}
|
|
211
260
|
catch (err) {
|
|
@@ -236,6 +285,18 @@ export function useAssistantChat(client) {
|
|
|
236
285
|
return;
|
|
237
286
|
replace(await client.undoWrite(id, step.id));
|
|
238
287
|
}, [client, replace]);
|
|
288
|
+
const screenEnded = useCallback((o) => {
|
|
289
|
+
outcomes.current?.set(o.id, o);
|
|
290
|
+
if (outcomes.current)
|
|
291
|
+
keepOutcomes(outcomes.current);
|
|
292
|
+
const outcome = { status: o.status, ...(o.text ? { text: o.text } : {}) };
|
|
293
|
+
const mark = (list) => list.map((i) => i.kind === 'screen' && i.id === o.id
|
|
294
|
+
? { kind: 'screen', id: i.id, label: i.label, moves: i.moves, outcome }
|
|
295
|
+
: i.kind === 'handoff'
|
|
296
|
+
? { ...i, items: mark(i.items) }
|
|
297
|
+
: i);
|
|
298
|
+
setItems(mark);
|
|
299
|
+
}, []);
|
|
239
300
|
return {
|
|
240
301
|
chatId,
|
|
241
302
|
title,
|
|
@@ -250,5 +311,6 @@ export function useAssistantChat(client) {
|
|
|
250
311
|
reset,
|
|
251
312
|
settle,
|
|
252
313
|
undo,
|
|
314
|
+
screenEnded,
|
|
253
315
|
};
|
|
254
316
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AssistantScreenMap, AssistantScreenMove, AssistantScreenStatus } from '@columenlabs/lumen-ui-types/assistant';
|
|
1
2
|
import type { IndicatorSpec } from '@columenlabs/lumen-ui-types/chart-formula';
|
|
2
3
|
import { type ReactNode } from 'react';
|
|
3
4
|
import { type CardOnScreen } from './spotlight.js';
|
|
@@ -28,20 +29,8 @@ export interface AssistantMoveHandlers {
|
|
|
28
29
|
restore?: (snap: unknown) => void;
|
|
29
30
|
}
|
|
30
31
|
/** One move, as the server checked it. */
|
|
31
|
-
export type AssistantMove =
|
|
32
|
-
|
|
33
|
-
} | {
|
|
34
|
-
settings: string;
|
|
35
|
-
row?: string;
|
|
36
|
-
} | {
|
|
37
|
-
chart: string | string[];
|
|
38
|
-
indicators?: IndicatorSpec[];
|
|
39
|
-
} | {
|
|
40
|
-
open: string;
|
|
41
|
-
} | {
|
|
42
|
-
point: string;
|
|
43
|
-
};
|
|
44
|
-
export type ScreenStatus = 'done' | 'failed' | 'waiting' | 'undone';
|
|
32
|
+
export type AssistantMove = AssistantScreenMove;
|
|
33
|
+
export type ScreenStatus = AssistantScreenStatus;
|
|
45
34
|
export interface ScreenOutcome {
|
|
46
35
|
status: ScreenStatus;
|
|
47
36
|
text?: string;
|
|
@@ -86,7 +75,7 @@ export interface ScreenMap {
|
|
|
86
75
|
cards: CardOnScreen[];
|
|
87
76
|
workspace?: WorkspaceOnScreen;
|
|
88
77
|
}
|
|
89
|
-
interface ScreenApi {
|
|
78
|
+
export interface ScreenApi {
|
|
90
79
|
register: (slot: string, h: AssistantMoveHandlers | null) => void;
|
|
91
80
|
pages: (key: string, items: AssistantPage[] | null) => void;
|
|
92
81
|
/** The workspace page says which workspace and tabs are on screen while it shows. */
|
|
@@ -121,4 +110,8 @@ export declare function useAssistantPages(items: {
|
|
|
121
110
|
export declare function AssistantScreenProvider({ children }: {
|
|
122
111
|
children: ReactNode;
|
|
123
112
|
}): import("react").JSX.Element;
|
|
124
|
-
|
|
113
|
+
/**
|
|
114
|
+
* The page map as the assistant's server reads it (`AssistantMessage.screen`): routes, named pages,
|
|
115
|
+
* Settings sections, the cards on screen and the rows the view can open.
|
|
116
|
+
*/
|
|
117
|
+
export declare function screenMapFor(api: ScreenApi): AssistantScreenMap;
|
package/dist/assistant-screen.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRouter } from '@tanstack/react-router';
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, } from 'react';
|
|
4
4
|
import { cardsOnScreen } from './spotlight.js';
|
|
@@ -88,6 +88,31 @@ const here = () => `${window.location.pathname}${window.location.search}${window
|
|
|
88
88
|
const cardOf = (id) => document.querySelector(`[data-slot="lumen-card"][data-card="${CSS.escape(id)}"]`);
|
|
89
89
|
/** Holds what each part of the screen takes, and runs the assistant's moves over them. */
|
|
90
90
|
export function AssistantScreenProvider({ children }) {
|
|
91
|
+
// One per app: a shell inside an app's own provider uses that one.
|
|
92
|
+
if (useContext(ScreenCtx))
|
|
93
|
+
return _jsx(_Fragment, { children: children });
|
|
94
|
+
return _jsx(ScreenProvider, { children: children });
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The page map as the assistant's server reads it (`AssistantMessage.screen`): routes, named pages,
|
|
98
|
+
* Settings sections, the cards on screen and the rows the view can open.
|
|
99
|
+
*/
|
|
100
|
+
export function screenMapFor(api) {
|
|
101
|
+
const m = api.map();
|
|
102
|
+
const rows = api.can()?.rows ?? [];
|
|
103
|
+
return {
|
|
104
|
+
routes: m.routes,
|
|
105
|
+
pages: m.pages.map((p) => ({
|
|
106
|
+
path: p.path,
|
|
107
|
+
title: p.title,
|
|
108
|
+
...(p.section ? { section: p.section } : {}),
|
|
109
|
+
})),
|
|
110
|
+
settings: m.settings,
|
|
111
|
+
cards: m.cards.map((c) => ({ id: c.id, title: c.title, kind: c.kind })),
|
|
112
|
+
...(rows.length ? { rows } : {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function ScreenProvider({ children }) {
|
|
91
116
|
const router = useRouter();
|
|
92
117
|
const slots = useRef(new Map());
|
|
93
118
|
const navPages = useRef(new Map());
|
package/dist/index.d.ts
CHANGED
|
@@ -102,7 +102,7 @@ export { useTell } from './hooks/use-tell.js';
|
|
|
102
102
|
export { useBackClose } from './hooks/use-back-close.js';
|
|
103
103
|
export { VIEW_ICONS, iconOf, viewIcon } from './view-icons.js';
|
|
104
104
|
export { type AppViewState, useAppView } from './app-views.js';
|
|
105
|
-
export { type ScreenMap, type ScreenOutcome, type ScreenStatus, type WorkspaceOnScreen, type AssistantMove, type AssistantMoveHandlers, type AssistantPage, type AssistantPageType, AssistantScreenProvider, useAssistantMoves, useAssistantPages, useAssistantScreen, useAssistantWorkspace, } from './assistant-screen.js';
|
|
105
|
+
export { type ScreenApi, type ScreenMap, type ScreenOutcome, type ScreenStatus, type WorkspaceOnScreen, type AssistantMove, type AssistantMoveHandlers, type AssistantPage, type AssistantPageType, AssistantScreenProvider, screenMapFor, useAssistantMoves, useAssistantPages, useAssistantScreen, useAssistantWorkspace, } from './assistant-screen.js';
|
|
106
106
|
export { type AssistantActions, AssistantContext, type AssistantNote, type AssistantRef, type AssistantSlot, useAssistant, AssistantPendingContext, useAssistantName, useAssistantNote, useAssistantPending, } from './assistant-context.js';
|
|
107
107
|
export { WORKSPACES_KEY, frameKey, workspacesListQuery } from './workspace-keys.js';
|
|
108
108
|
export { type WorkspaceApp, type WorkspaceDraftProps, WorkspacePage, type WorkspaceRecipe, type WorkspacesConfig, WorkspacesProvider, WorkspacesSection, } from './workspaces.js';
|
package/dist/index.js
CHANGED
|
@@ -109,7 +109,7 @@ export { useTell } from './hooks/use-tell.js';
|
|
|
109
109
|
export { useBackClose } from './hooks/use-back-close.js';
|
|
110
110
|
export { VIEW_ICONS, iconOf, viewIcon } from './view-icons.js';
|
|
111
111
|
export { useAppView } from './app-views.js';
|
|
112
|
-
export { AssistantScreenProvider, useAssistantMoves, useAssistantPages, useAssistantScreen, useAssistantWorkspace, } from './assistant-screen.js';
|
|
112
|
+
export { AssistantScreenProvider, screenMapFor, useAssistantMoves, useAssistantPages, useAssistantScreen, useAssistantWorkspace, } from './assistant-screen.js';
|
|
113
113
|
export { AssistantContext, useAssistant, AssistantPendingContext, useAssistantName, useAssistantNote, useAssistantPending, } from './assistant-context.js';
|
|
114
114
|
export { WORKSPACES_KEY, frameKey, workspacesListQuery } from './workspace-keys.js';
|
|
115
115
|
export { WorkspacePage, WorkspacesProvider, WorkspacesSection, } from './workspaces.js';
|
package/dist/shell.js
CHANGED
|
@@ -7,6 +7,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
7
7
|
import { useRouterState } from '@tanstack/react-router';
|
|
8
8
|
import { ChevronsUpDownIcon, LogOutIcon, ScrollTextIcon, SettingsIcon, SunMoonIcon, } from 'lucide-react';
|
|
9
9
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
|
|
10
|
+
import { AssistantScreenProvider } from './assistant-screen.js';
|
|
10
11
|
import { lazyPart } from './lazy-part.js';
|
|
11
12
|
import { cn } from './lib/utils.js';
|
|
12
13
|
import { LumenFinder } from './lumen-finder.js';
|
|
@@ -133,18 +134,18 @@ export function LumenShell({ app, appLabel, brand, nav, tabs, bar, status, healt
|
|
|
133
134
|
title: s.title,
|
|
134
135
|
})));
|
|
135
136
|
const findSettings = useMemo(() => JSON.parse(sectionNames), [sectionNames]);
|
|
136
|
-
return (_jsx(Ctx.Provider, { value: {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
return (_jsx(AssistantScreenProvider, { children: _jsx(Ctx.Provider, { value: {
|
|
138
|
+
app,
|
|
139
|
+
appLabel,
|
|
140
|
+
statusSlot,
|
|
141
|
+
openSettings,
|
|
142
|
+
closeSettings: () => setSettingsAt(null),
|
|
143
|
+
hasSettings: (section) => findSettings.some((s) => s.id === section),
|
|
144
|
+
refresh: onRefresh ? refresh : null,
|
|
145
|
+
refreshing,
|
|
146
|
+
hideAmounts,
|
|
147
|
+
headerActions,
|
|
148
|
+
}, children: _jsx(StatusBarProvider, { children: _jsx(DockSlot.Provider, { value: dockSlot, children: _jsx(StatusSignals, { app: app, model: bar, openSettings: openSettings, children: _jsx(SourceHealthProvider, { children: wrap(_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(SidebarProvider, { defaultOpen: sidebarOpen(), children: _jsxs(LumenFinder, { app: app, settings: findSettings, openSettings: openSettings, config: finder, children: [_jsx(CloseDrawerOnNavigate, {}), _jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: brand }), _jsx(SidebarContent, { children: nav }), _jsx(SidebarFooter, { children: _jsx(NavUser, { who: user, signOutUrl: signOutUrl, onSettings: () => openSettings(), onLogs: onLogs }) }), _jsx(SidebarRail, {})] }), _jsxs(SidebarInset, { className: "flex min-h-svh min-w-0 flex-col", children: [_jsx(SignalBanners, {}), _jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: children }, epoch), _jsx(LumenStatusBar, { app: app, model: bar, legacy: status, slot: setStatusSlot, end: _jsxs(_Fragment, { children: [health, releases && _jsx(WhatsNew, { api: releases, apps: releaseApps }), statusEnd, _jsx(StatusTimeZone, {})] }) }), tabs && _jsx(LumenTabBar, { items: tabs })] }), _jsx("div", { ref: setDockSlot, "data-slot": "shell-dock", className: "contents" }), panel, _jsx(LumenSettings, { open: settingsAt !== null, section: settingsAt ?? lastSection, onOpen: openSettings, onClose: () => setSettingsAt(null), appLabel: appLabel, profile: profile, security: security, sections: settings, shared: shared, sharedLabel: sharedLabel, groups: settingsGroups }), _jsx(Toaster, { position: "bottom-right" })] }) }) })) }) }) }) }) }) }));
|
|
148
149
|
}
|
|
149
150
|
/** A status bar entry: an optional health dot, a label and its value ("Mail · Up to date"). */
|
|
150
151
|
export function StatusItem({ label, value, ok, className, }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.191",
|
|
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.191",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|