@columenlabs/lumen-ui 0.1.306 → 0.1.308
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.
|
@@ -197,18 +197,38 @@ export function LumenAssistant({ client, context, onSettings, keepDays = 30, cha
|
|
|
197
197
|
...(chatScope && allScopes && c.scope ? { where: chatScope.name?.(c.scope) } : {}),
|
|
198
198
|
})) ?? (chats.isFetched ? [] : null), value: chat.chatId ?? undefined, toggle: chatScope && { label: chatScope.label, on: allScopes, onChange: setAllScopes }, 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(ImageTool.Provider, { value: imageTool ?? null, children: _jsx(TaskSlots.Provider, { value: { actions: taskActions, state: taskState, view: taskView, stop: chat.stop }, children: _jsx(Messages, { chat: chat, status: s, onSettings: onSettings, onRetry: (at, user) => chat.retry(at, user, extra()), onFresh: (choice) => chat.fresh(choice, extra()), onAnswer: (text) => chat.send(text, extra()) }) }) }) }) }), _jsx(LumenComposer, { name: s?.name ?? 'the assistant', busy: chat.busy, queued: chat.queued, queue: chat.queue, canSteer: chat.canSteer, onSteerQueued: chat.steerQueued, onUnqueue: chat.unqueue, disabled: !s || !!s.wait, commands: allCommands, tiers: tiers, sources: allSources, page: context?.label, images: images, modes: modes, onSend: (text, more, how) => chat.send(text, { ...extra(), ...more }, how), onStop: chat.stop, meter: _jsx(ContextDonut, { usage: chat.usage, compactAt: s?.compact_at ?? compactAt, items: chat.items }) })] }));
|
|
199
199
|
}
|
|
200
|
+
/** How near the end of the chat (px) still counts as at the end, so it keeps following. */
|
|
201
|
+
const FOLLOW_PX = 48;
|
|
200
202
|
function Messages({ chat, status, onSettings, onRetry, onFresh, onAnswer, }) {
|
|
201
203
|
const end = useRef(null);
|
|
202
204
|
const box = useRef(null);
|
|
203
205
|
const count = JSON.stringify(chat.items).length;
|
|
206
|
+
// Follow the chat as it grows only while the person is at its end: once they scroll up to read,
|
|
207
|
+
// a streaming answer or a card that changes leaves their place alone. Sending a message, or
|
|
208
|
+
// opening another chat, goes to the end again.
|
|
209
|
+
const pinned = useRef(true);
|
|
210
|
+
const onScroll = () => {
|
|
211
|
+
const el = box.current;
|
|
212
|
+
if (el)
|
|
213
|
+
pinned.current = el.scrollHeight - el.scrollTop - el.clientHeight < FOLLOW_PX;
|
|
214
|
+
};
|
|
215
|
+
const last = chat.items[chat.items.length - 1];
|
|
216
|
+
const sent = last?.kind === 'user' ? last.at : undefined;
|
|
217
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: a new message or chat pins the end again
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
pinned.current = true;
|
|
220
|
+
}, [sent, chat.chatId]);
|
|
204
221
|
// biome-ignore lint/correctness/useExhaustiveDependencies: scroll to the end as the chat grows
|
|
205
222
|
useEffect(() => {
|
|
206
|
-
|
|
207
|
-
|
|
223
|
+
const el = box.current;
|
|
224
|
+
// The list's own scroll only: scrollIntoView would move the page and every scroll box around it.
|
|
225
|
+
if (el && pinned.current)
|
|
226
|
+
el.scrollTop = el.scrollHeight;
|
|
227
|
+
}, [count, sent, chat.chatId, chat.loading]);
|
|
208
228
|
// The app's own link to Settings, else the shell's, at the section that answers the wait.
|
|
209
229
|
const shellSettings = useOpenSettings();
|
|
210
230
|
const settings = onSettings ? (_section) => onSettings() : shellSettings;
|
|
211
|
-
return (_jsxs("div", { ref: box, className: "relative min-h-0 flex-1 overflow-y-auto px-3 py-3", children: [_jsx(ReplyToSelection, { box: box }), 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
|
|
231
|
+
return (_jsxs("div", { ref: box, onScroll: onScroll, className: "relative min-h-0 flex-1 overflow-y-auto px-3 py-3", children: [_jsx(ReplyToSelection, { box: box }), 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
|
|
212
232
|
? allowanceWait(status.allowance)
|
|
213
233
|
: 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
|
|
214
234
|
? 'assistant-allowance'
|
|
@@ -451,7 +451,10 @@ export function useAssistantChat(client) {
|
|
|
451
451
|
const open = useCallback(async (id) => {
|
|
452
452
|
abort.current?.abort();
|
|
453
453
|
setQueue([]);
|
|
454
|
-
|
|
454
|
+
// Reading the chat that is on screen again (a task ended, a change was kept) swaps its items in
|
|
455
|
+
// place: the list stays, so the person's place in it stays. Only another chat shows Loading.
|
|
456
|
+
if (idRef.current !== id)
|
|
457
|
+
setLoading(true);
|
|
455
458
|
try {
|
|
456
459
|
const c = await client.chat(id);
|
|
457
460
|
idRef.current = c.id;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.308",
|
|
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.308",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|