@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.
Files changed (27) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/assets/{ChannelsList-AKnD2r1L.js → ChannelsList-CAuBEcOr.js} +1 -1
  3. package/dist/assets/{ChatPage-DidO_pAN.js → ChatPage-Bxs3X5OC.js} +13 -13
  4. package/dist/assets/{CronConfig-C1pm-oKA.js → CronConfig-jOEPCnpf.js} +1 -1
  5. package/dist/assets/{DocBrowser-BY90Lf6L.js → DocBrowser-wov_cBSN.js} +1 -1
  6. package/dist/assets/{MarketplacePage-BRtmhP3G.js → MarketplacePage-Cob6DGoO.js} +1 -1
  7. package/dist/assets/{ModelConfig-Dga1Ko7_.js → ModelConfig-C_Y3UDYr.js} +1 -1
  8. package/dist/assets/{ProvidersList-DvCoBTrT.js → ProvidersList-6A6N2eDT.js} +1 -1
  9. package/dist/assets/{RuntimeConfig-2aqBJ6Xn.js → RuntimeConfig-B3k_dMdJ.js} +1 -1
  10. package/dist/assets/{SecretsConfig-wlnh__z0.js → SecretsConfig-BeCRCCEW.js} +2 -2
  11. package/dist/assets/{SessionsConfig-CN2WymbH.js → SessionsConfig-M32Qm7cL.js} +1 -1
  12. package/dist/assets/{action-link-DLZDwUfD.js → action-link-DsAjsb68.js} +1 -1
  13. package/dist/assets/{card-D3dD-I5t.js → card-uTj7-9XS.js} +1 -1
  14. package/dist/assets/{dialog-DZ0VC-RD.js → dialog-FRtXcCmk.js} +1 -1
  15. package/dist/assets/{index-D_vv0E-O.js → index-Dw8Ss2WH.js} +2 -2
  16. package/dist/assets/{label-CJIvvG6o.js → label-DAhEgM6-.js} +1 -1
  17. package/dist/assets/{page-layout-CLgr0qym.js → page-layout-DMpNQawS.js} +1 -1
  18. package/dist/assets/{switch-C-0Q8OH2.js → switch-D8lSFzq4.js} +1 -1
  19. package/dist/assets/{tabs-custom-D4Gs3BGM.js → tabs-custom-CsFrOXUS.js} +1 -1
  20. package/dist/assets/useConfig-Cjlx5C-1.js +6 -0
  21. package/dist/assets/{useConfirmDialog-AMeSTA83.js → useConfirmDialog-D3eI0Hfj.js} +1 -1
  22. package/dist/index.html +1 -1
  23. package/package.json +1 -1
  24. package/src/api/config.ts +159 -2
  25. package/src/api/types.ts +16 -0
  26. package/src/components/chat/ChatPage.tsx +44 -46
  27. 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 { useConfig, useDeleteSession, useSendChatTurn, useSessionHistory, useSessions } from '@/hooks/useConfig';
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 = sendChatTurn.isPending || Boolean(streamingAssistantMessage);
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, sendChatTurn.isPending, selectedSessionKey]);
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 result = await sendChatTurn.mutateAsync({
221
- data: {
222
- message,
223
- sessionKey,
224
- agentId: selectedAgentId,
225
- channel: 'ui',
226
- chatId: 'web-ui'
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
- if (previewRunId && previewRunId === streamRunIdRef.current) {
254
- setStreamingAssistantMessage(null);
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={sendChatTurn.isPending && !streamingAssistantMessage} />
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};