@nextclaw/ui 0.5.23 → 0.5.24
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/CHANGELOG.md +6 -0
- package/dist/assets/{ChannelsList-AKnD2r1L.js → ChannelsList-CAuBEcOr.js} +1 -1
- package/dist/assets/{ChatPage-DidO_pAN.js → ChatPage-Bxs3X5OC.js} +13 -13
- package/dist/assets/{CronConfig-C1pm-oKA.js → CronConfig-jOEPCnpf.js} +1 -1
- package/dist/assets/{DocBrowser-BY90Lf6L.js → DocBrowser-wov_cBSN.js} +1 -1
- package/dist/assets/{MarketplacePage-BRtmhP3G.js → MarketplacePage-Cob6DGoO.js} +1 -1
- package/dist/assets/{ModelConfig-Dga1Ko7_.js → ModelConfig-C_Y3UDYr.js} +1 -1
- package/dist/assets/{ProvidersList-DvCoBTrT.js → ProvidersList-6A6N2eDT.js} +1 -1
- package/dist/assets/{RuntimeConfig-2aqBJ6Xn.js → RuntimeConfig-B3k_dMdJ.js} +1 -1
- package/dist/assets/{SecretsConfig-wlnh__z0.js → SecretsConfig-BeCRCCEW.js} +2 -2
- package/dist/assets/{SessionsConfig-CN2WymbH.js → SessionsConfig-M32Qm7cL.js} +1 -1
- package/dist/assets/{action-link-DLZDwUfD.js → action-link-DsAjsb68.js} +1 -1
- package/dist/assets/{card-D3dD-I5t.js → card-uTj7-9XS.js} +1 -1
- package/dist/assets/{dialog-DZ0VC-RD.js → dialog-FRtXcCmk.js} +1 -1
- package/dist/assets/{index-D_vv0E-O.js → index-Dw8Ss2WH.js} +2 -2
- package/dist/assets/{label-CJIvvG6o.js → label-DAhEgM6-.js} +1 -1
- package/dist/assets/{page-layout-CLgr0qym.js → page-layout-DMpNQawS.js} +1 -1
- package/dist/assets/{switch-C-0Q8OH2.js → switch-D8lSFzq4.js} +1 -1
- package/dist/assets/{tabs-custom-D4Gs3BGM.js → tabs-custom-CsFrOXUS.js} +1 -1
- package/dist/assets/useConfig-Cjlx5C-1.js +6 -0
- package/dist/assets/{useConfirmDialog-AMeSTA83.js → useConfirmDialog-D3eI0Hfj.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/api/config.ts +159 -2
- package/src/api/types.ts +16 -0
- package/src/components/chat/ChatPage.tsx +44 -46
- package/dist/assets/useConfig-R5uGhZtD.js +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import type { SessionEntryView, SessionMessageView } from '@/api/types';
|
|
3
|
-
import {
|
|
3
|
+
import { sendChatTurnStream } from '@/api/config';
|
|
4
|
+
import { useConfig, useDeleteSession, useSessionHistory, useSessions } from '@/hooks/useConfig';
|
|
4
5
|
import { useConfirmDialog } from '@/hooks/useConfirmDialog';
|
|
5
6
|
import { Button } from '@/components/ui/button';
|
|
6
7
|
import { Input } from '@/components/ui/input';
|
|
@@ -12,21 +13,6 @@ import { formatDateTime, t } from '@/lib/i18n';
|
|
|
12
13
|
import { MessageSquareText, Plus, RefreshCw, Search, Send, Trash2 } from 'lucide-react';
|
|
13
14
|
|
|
14
15
|
const CHAT_SESSION_STORAGE_KEY = 'nextclaw.ui.chat.activeSession';
|
|
15
|
-
const STREAM_FRAME_MS = 18;
|
|
16
|
-
|
|
17
|
-
function streamChunkSize(remaining: number): number {
|
|
18
|
-
if (remaining > 2400) return 120;
|
|
19
|
-
if (remaining > 1200) return 72;
|
|
20
|
-
if (remaining > 600) return 40;
|
|
21
|
-
if (remaining > 220) return 20;
|
|
22
|
-
return 8;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function delay(ms: number): Promise<void> {
|
|
26
|
-
return new Promise((resolve) => {
|
|
27
|
-
window.setTimeout(resolve, ms);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
16
|
|
|
31
17
|
function readStoredSessionKey(): string | null {
|
|
32
18
|
if (typeof window === 'undefined') {
|
|
@@ -84,6 +70,7 @@ export function ChatPage() {
|
|
|
84
70
|
const [selectedAgentId, setSelectedAgentId] = useState('main');
|
|
85
71
|
const [optimisticUserMessage, setOptimisticUserMessage] = useState<SessionMessageView | null>(null);
|
|
86
72
|
const [streamingAssistantMessage, setStreamingAssistantMessage] = useState<SessionMessageView | null>(null);
|
|
73
|
+
const [isSending, setIsSending] = useState(false);
|
|
87
74
|
|
|
88
75
|
const { confirm, ConfirmDialog } = useConfirmDialog();
|
|
89
76
|
const threadRef = useRef<HTMLDivElement | null>(null);
|
|
@@ -93,7 +80,6 @@ export function ChatPage() {
|
|
|
93
80
|
const sessionsQuery = useSessions({ q: query.trim() || undefined, limit: 120, activeMinutes: 0 });
|
|
94
81
|
const historyQuery = useSessionHistory(selectedSessionKey, 300);
|
|
95
82
|
const deleteSession = useDeleteSession();
|
|
96
|
-
const sendChatTurn = useSendChatTurn();
|
|
97
83
|
|
|
98
84
|
const agentOptions = useMemo(() => {
|
|
99
85
|
const list = configQuery.data?.agents.list ?? [];
|
|
@@ -113,7 +99,7 @@ export function ChatPage() {
|
|
|
113
99
|
);
|
|
114
100
|
|
|
115
101
|
const historyMessages = useMemo(() => historyQuery.data?.messages ?? [], [historyQuery.data?.messages]);
|
|
116
|
-
const isGenerating =
|
|
102
|
+
const isGenerating = isSending;
|
|
117
103
|
const mergedMessages = useMemo(() => {
|
|
118
104
|
if (!optimisticUserMessage && !streamingAssistantMessage) {
|
|
119
105
|
return historyMessages;
|
|
@@ -154,7 +140,7 @@ export function ChatPage() {
|
|
|
154
140
|
return;
|
|
155
141
|
}
|
|
156
142
|
element.scrollTop = element.scrollHeight;
|
|
157
|
-
}, [mergedMessages,
|
|
143
|
+
}, [mergedMessages, isSending, selectedSessionKey]);
|
|
158
144
|
|
|
159
145
|
useEffect(() => {
|
|
160
146
|
return () => {
|
|
@@ -164,6 +150,7 @@ export function ChatPage() {
|
|
|
164
150
|
|
|
165
151
|
const createNewSession = () => {
|
|
166
152
|
streamRunIdRef.current += 1;
|
|
153
|
+
setIsSending(false);
|
|
167
154
|
setStreamingAssistantMessage(null);
|
|
168
155
|
const next = buildNewSessionKey(selectedAgentId);
|
|
169
156
|
setSelectedSessionKey(next);
|
|
@@ -187,6 +174,7 @@ export function ChatPage() {
|
|
|
187
174
|
{
|
|
188
175
|
onSuccess: async () => {
|
|
189
176
|
streamRunIdRef.current += 1;
|
|
177
|
+
setIsSending(false);
|
|
190
178
|
setStreamingAssistantMessage(null);
|
|
191
179
|
setSelectedSessionKey(null);
|
|
192
180
|
setOptimisticUserMessage(null);
|
|
@@ -215,46 +203,56 @@ export function ChatPage() {
|
|
|
215
203
|
content: message,
|
|
216
204
|
timestamp: new Date().toISOString()
|
|
217
205
|
});
|
|
206
|
+
setIsSending(true);
|
|
218
207
|
|
|
219
208
|
try {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
209
|
+
const runId = streamRunIdRef.current;
|
|
210
|
+
let streamText = '';
|
|
211
|
+
const streamTimestamp = new Date().toISOString();
|
|
212
|
+
|
|
213
|
+
const result = await sendChatTurnStream({
|
|
214
|
+
message,
|
|
215
|
+
sessionKey,
|
|
216
|
+
agentId: selectedAgentId,
|
|
217
|
+
channel: 'ui',
|
|
218
|
+
chatId: 'web-ui'
|
|
219
|
+
}, {
|
|
220
|
+
onReady: (event) => {
|
|
221
|
+
if (runId !== streamRunIdRef.current) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (event.sessionKey && event.sessionKey !== selectedSessionKey) {
|
|
225
|
+
setSelectedSessionKey(event.sessionKey);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
onDelta: (event) => {
|
|
229
|
+
if (runId !== streamRunIdRef.current) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
streamText += event.delta;
|
|
233
|
+
setStreamingAssistantMessage({
|
|
234
|
+
role: 'assistant',
|
|
235
|
+
content: streamText,
|
|
236
|
+
timestamp: streamTimestamp
|
|
237
|
+
});
|
|
227
238
|
}
|
|
228
239
|
});
|
|
240
|
+
if (runId !== streamRunIdRef.current) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
229
243
|
setOptimisticUserMessage(null);
|
|
230
244
|
if (result.sessionKey !== sessionKey) {
|
|
231
245
|
setSelectedSessionKey(result.sessionKey);
|
|
232
246
|
}
|
|
233
|
-
const replyText = typeof result.reply === 'string' ? result.reply : '';
|
|
234
|
-
let previewRunId: number | null = null;
|
|
235
|
-
if (replyText.trim()) {
|
|
236
|
-
previewRunId = ++streamRunIdRef.current;
|
|
237
|
-
const timestamp = new Date().toISOString();
|
|
238
|
-
let cursor = 0;
|
|
239
|
-
while (cursor < replyText.length && previewRunId === streamRunIdRef.current) {
|
|
240
|
-
cursor = Math.min(replyText.length, cursor + streamChunkSize(replyText.length - cursor));
|
|
241
|
-
setStreamingAssistantMessage({
|
|
242
|
-
role: 'assistant',
|
|
243
|
-
content: replyText.slice(0, cursor),
|
|
244
|
-
timestamp
|
|
245
|
-
});
|
|
246
|
-
await delay(STREAM_FRAME_MS);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
247
|
await sessionsQuery.refetch();
|
|
250
248
|
if (hadActiveSession) {
|
|
251
249
|
await historyQuery.refetch();
|
|
252
250
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
251
|
+
setStreamingAssistantMessage(null);
|
|
252
|
+
setIsSending(false);
|
|
256
253
|
} catch {
|
|
257
254
|
streamRunIdRef.current += 1;
|
|
255
|
+
setIsSending(false);
|
|
258
256
|
setStreamingAssistantMessage(null);
|
|
259
257
|
setOptimisticUserMessage(null);
|
|
260
258
|
setDraft(message);
|
|
@@ -393,7 +391,7 @@ export function ChatPage() {
|
|
|
393
391
|
{mergedMessages.length === 0 ? (
|
|
394
392
|
<div className="text-sm text-gray-500">{t('chatNoMessages')}</div>
|
|
395
393
|
) : (
|
|
396
|
-
<ChatThread messages={mergedMessages} isSending={
|
|
394
|
+
<ChatThread messages={mergedMessages} isSending={isSending && !streamingAssistantMessage} />
|
|
397
395
|
)}
|
|
398
396
|
</>
|
|
399
397
|
)}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as d,j as g,aq as c,ar as a,as as i,ae as o}from"./vendor-H2M3a_4Z.js";import{c as l,i as s,t}from"./index-D_vv0E-O.js";const y=d.forwardRef(({className:e,type:n,...r},u)=>g.jsx("input",{type:n,className:l("flex h-9 w-full rounded-xl border border-gray-200/80 bg-white px-3.5 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-primary/40 focus:border-primary/40 transition-colors disabled:cursor-not-allowed disabled:opacity-50",e),ref:u,...r}));y.displayName="Input";async function p(){const e=await s.get("/api/config");if(!e.ok)throw new Error(e.error.message);return e.data}async function m(){const e=await s.get("/api/config/meta");if(!e.ok)throw new Error(e.error.message);return e.data}async function w(){const e=await s.get("/api/config/schema");if(!e.ok)throw new Error(e.error.message);return e.data}async function h(e){const n=await s.put("/api/config/model",e);if(!n.ok)throw new Error(n.error.message);return n.data}async function S(e,n){const r=await s.put(`/api/config/providers/${e}`,n);if(!r.ok)throw new Error(r.error.message);return r.data}async function q(e,n){const r=await s.put(`/api/config/channels/${e}`,n);if(!r.ok)throw new Error(r.error.message);return r.data}async function v(e){const n=await s.put("/api/config/runtime",e);if(!n.ok)throw new Error(n.error.message);return n.data}async function C(e){const n=await s.put("/api/config/secrets",e);if(!n.ok)throw new Error(n.error.message);return n.data}async function E(e,n){const r=await s.post(`/api/config/actions/${e}/execute`,n);if(!r.ok)throw new Error(r.error.message);return r.data}async function F(e){var f;const n=new URLSearchParams;(f=e==null?void 0:e.q)!=null&&f.trim()&&n.set("q",e.q.trim()),typeof(e==null?void 0:e.limit)=="number"&&Number.isFinite(e.limit)&&n.set("limit",String(Math.max(0,Math.trunc(e.limit)))),typeof(e==null?void 0:e.activeMinutes)=="number"&&Number.isFinite(e.activeMinutes)&&n.set("activeMinutes",String(Math.max(0,Math.trunc(e.activeMinutes))));const r=n.toString(),u=await s.get(r?"/api/sessions?"+r:"/api/sessions");if(!u.ok)throw new Error(u.error.message);return u.data}async function b(e,n=200){const r=await s.get(`/api/sessions/${encodeURIComponent(e)}/history?limit=${Math.max(1,Math.trunc(n))}`);if(!r.ok)throw new Error(r.error.message);return r.data}async function x(e,n){const r=await s.put(`/api/sessions/${encodeURIComponent(e)}`,n);if(!r.ok)throw new Error(r.error.message);return r.data}async function k(e){const n=await s.delete(`/api/sessions/${encodeURIComponent(e)}`);if(!n.ok)throw new Error(n.error.message);return n.data}async function K(e){const n=await s.post("/api/chat/turn",e);if(!n.ok)throw new Error(n.error.message);return n.data}async function M(e){const n=new URLSearchParams;e!=null&&e.all&&n.set("all","1");const r=n.toString(),u=await s.get(r?"/api/cron?"+r:"/api/cron");if(!u.ok)throw new Error(u.error.message);return u.data}async function Q(e){const n=await s.delete(`/api/cron/${encodeURIComponent(e)}`);if(!n.ok)throw new Error(n.error.message);return n.data}async function U(e,n){const r=await s.put(`/api/cron/${encodeURIComponent(e)}/enable`,n);if(!r.ok)throw new Error(r.error.message);return r.data}async function R(e,n){const r=await s.post(`/api/cron/${encodeURIComponent(e)}/run`,n);if(!r.ok)throw new Error(r.error.message);return r.data}function $(){return c({queryKey:["config"],queryFn:p,staleTime:3e4,refetchOnWindowFocus:!0})}function T(){return c({queryKey:["config-meta"],queryFn:m,staleTime:1/0})}function J(){return c({queryKey:["config-schema"],queryFn:w,staleTime:1/0})}function j(){const e=a();return i({mutationFn:h,onSuccess:()=>{e.invalidateQueries({queryKey:["config"]}),o.success(t("configSaved"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function N(){const e=a();return i({mutationFn:({provider:n,data:r})=>S(n,r),onSuccess:()=>{e.invalidateQueries({queryKey:["config"]}),o.success(t("configSaved"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function P(){const e=a();return i({mutationFn:({channel:n,data:r})=>q(n,r),onSuccess:()=>{e.invalidateQueries({queryKey:["config"]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function D(){const e=a();return i({mutationFn:({data:n})=>v(n),onSuccess:()=>{e.invalidateQueries({queryKey:["config"]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function H(){const e=a();return i({mutationFn:({data:n})=>C(n),onSuccess:()=>{e.invalidateQueries({queryKey:["config"]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function L(){return i({mutationFn:({actionId:e,data:n})=>E(e,n),onError:e=>{o.error(t("error")+": "+e.message)}})}function B(e){return c({queryKey:["sessions",e],queryFn:()=>F(e),staleTime:1e4})}function O(e,n=200){return c({queryKey:["session-history",e,n],queryFn:()=>b(e,n),enabled:!!e,staleTime:5e3})}function W(){const e=a();return i({mutationFn:({key:n,data:r})=>x(n,r),onSuccess:(n,r)=>{e.invalidateQueries({queryKey:["sessions"]}),e.invalidateQueries({queryKey:["session-history",r.key]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function _(){const e=a();return i({mutationFn:({key:n})=>k(n),onSuccess:()=>{e.invalidateQueries({queryKey:["sessions"]}),e.invalidateQueries({queryKey:["session-history"]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function z(){return i({mutationFn:({data:e})=>K(e),onError:e=>{o.error(t("chatSendFailed")+": "+e.message)}})}function G(e={all:!0}){return c({queryKey:["cron",e],queryFn:()=>M(e),staleTime:1e4})}function V(){const e=a();return i({mutationFn:({id:n})=>Q(n),onSuccess:()=>{e.invalidateQueries({queryKey:["cron"]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function X(){const e=a();return i({mutationFn:({id:n,enabled:r})=>U(n,{enabled:r}),onSuccess:()=>{e.invalidateQueries({queryKey:["cron"]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}function Y(){const e=a();return i({mutationFn:({id:n,force:r})=>R(n,{force:r}),onSuccess:()=>{e.invalidateQueries({queryKey:["cron"]}),o.success(t("configSavedApplied"))},onError:n=>{o.error(t("configSaveFailed")+": "+n.message)}})}export{y as I,J as a,j as b,B as c,O as d,_ as e,z as f,T as g,N as h,P as i,L as j,D as k,W as l,G as m,V as n,X as o,Y as p,H as q,$ as u};
|