@nextclaw/ui 0.5.26 → 0.5.29
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 +22 -0
- package/dist/assets/{ChannelsList-D0Wk08Ki.js → ChannelsList-DEr4kE7H.js} +1 -1
- package/dist/assets/ChatPage-DI2euxZy.js +32 -0
- package/dist/assets/{CronConfig-D-3Y8kWb.js → CronConfig-DAlt-x5i.js} +1 -1
- package/dist/assets/{DocBrowser-BSPKhqrK.js → DocBrowser-TrMsdXgx.js} +1 -1
- package/dist/assets/{MarketplacePage-Dkm2FTtN.js → MarketplacePage-Dwm527F7.js} +1 -1
- package/dist/assets/{ModelConfig-2cpAmvGq.js → ModelConfig-srggzgfA.js} +1 -1
- package/dist/assets/{ProvidersList-Dot21pAy.js → ProvidersList-8kFCDiqC.js} +1 -1
- package/dist/assets/{RuntimeConfig-BNw_Ms_Y.js → RuntimeConfig-CLbdKAlo.js} +1 -1
- package/dist/assets/{SecretsConfig-z8M3PDJP.js → SecretsConfig-DXCdR0Be.js} +1 -1
- package/dist/assets/{SessionsConfig-XVHZ-FG5.js → SessionsConfig-iKpz3Sts.js} +1 -1
- package/dist/assets/{action-link-CpPJJN-z.js → action-link-w4jS8X9q.js} +1 -1
- package/dist/assets/{card-DsZ2Am92.js → card-CVj65Dvi.js} +1 -1
- package/dist/assets/chat-message-D0s61C4e.js +5 -0
- package/dist/assets/{dialog-BysNu5hM.js → dialog-lK79rlAw.js} +1 -1
- package/dist/assets/{index-Bny21Br0.js → index-BXgULtdk.js} +2 -2
- package/dist/assets/{label-q6RASlER.js → label-l-fECYi3.js} +1 -1
- package/dist/assets/{page-layout-WiVrFc8t.js → page-layout-BghxFaNt.js} +1 -1
- package/dist/assets/{switch-DM_YYUgB.js → switch-B4yFzIbc.js} +1 -1
- package/dist/assets/{tabs-custom-mlgm-IGH.js → tabs-custom-B4q02QSV.js} +1 -1
- package/dist/assets/useConfig-C9k3TmQk.js +6 -0
- package/dist/assets/{useConfirmDialog-DamaA60g.js → useConfirmDialog-C20D5SYn.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/api/config.ts +14 -1
- package/src/api/types.ts +14 -0
- package/src/components/chat/ChatPage.tsx +95 -35
- package/src/components/chat/ChatThread.tsx +58 -32
- package/src/hooks/useConfig.ts +2 -1
- package/src/lib/chat-message.ts +169 -153
- package/dist/assets/ChatPage-Deg2lBH4.js +0 -32
- package/dist/assets/chat-message-Jxa8JFA_.js +0 -9
- package/dist/assets/useConfig-BOn-kp8G.js +0 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import type { SessionEntryView,
|
|
2
|
+
import type { SessionEntryView, SessionEventView } from '@/api/types';
|
|
3
3
|
import { sendChatTurnStream } from '@/api/config';
|
|
4
4
|
import { useConfig, useDeleteSession, useSessionHistory, useSessions } from '@/hooks/useConfig';
|
|
5
5
|
import { useConfirmDialog } from '@/hooks/useConfirmDialog';
|
|
@@ -9,6 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
|
|
9
9
|
import { PageHeader, PageLayout } from '@/components/layout/page-layout';
|
|
10
10
|
import { ChatThread } from '@/components/chat/ChatThread';
|
|
11
11
|
import { cn } from '@/lib/utils';
|
|
12
|
+
import { buildFallbackEventsFromMessages } from '@/lib/chat-message';
|
|
12
13
|
import { formatDateTime, t } from '@/lib/i18n';
|
|
13
14
|
import { MessageSquareText, Plus, RefreshCw, Search, Send, Trash2 } from 'lucide-react';
|
|
14
15
|
|
|
@@ -93,9 +94,12 @@ export function ChatPage() {
|
|
|
93
94
|
const [draft, setDraft] = useState('');
|
|
94
95
|
const [selectedSessionKey, setSelectedSessionKey] = useState<string | null>(() => readStoredSessionKey());
|
|
95
96
|
const [selectedAgentId, setSelectedAgentId] = useState('main');
|
|
96
|
-
const [
|
|
97
|
-
const [
|
|
97
|
+
const [optimisticUserEvent, setOptimisticUserEvent] = useState<SessionEventView | null>(null);
|
|
98
|
+
const [streamingSessionEvents, setStreamingSessionEvents] = useState<SessionEventView[]>([]);
|
|
99
|
+
const [streamingAssistantText, setStreamingAssistantText] = useState('');
|
|
100
|
+
const [streamingAssistantTimestamp, setStreamingAssistantTimestamp] = useState<string | null>(null);
|
|
98
101
|
const [isSending, setIsSending] = useState(false);
|
|
102
|
+
const [isAwaitingAssistantOutput, setIsAwaitingAssistantOutput] = useState(false);
|
|
99
103
|
const [queuedMessages, setQueuedMessages] = useState<PendingChatMessage[]>([]);
|
|
100
104
|
|
|
101
105
|
const { confirm, ConfirmDialog } = useConfirmDialog();
|
|
@@ -143,20 +147,36 @@ export function ChatPage() {
|
|
|
143
147
|
[selectedSessionKey, sessions]
|
|
144
148
|
);
|
|
145
149
|
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
const historyData = historyQuery.data;
|
|
151
|
+
const historyMessages = historyData?.messages ?? [];
|
|
152
|
+
const historyEvents =
|
|
153
|
+
historyData?.events && historyData.events.length > 0
|
|
154
|
+
? historyData.events
|
|
155
|
+
: buildFallbackEventsFromMessages(historyMessages);
|
|
156
|
+
const mergedEvents = useMemo(() => {
|
|
157
|
+
const next = [...historyEvents];
|
|
158
|
+
if (optimisticUserEvent) {
|
|
159
|
+
next.push(optimisticUserEvent);
|
|
150
160
|
}
|
|
151
|
-
|
|
152
|
-
if (
|
|
153
|
-
next.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
next.push(...streamingSessionEvents);
|
|
162
|
+
if (streamingAssistantText.trim()) {
|
|
163
|
+
const maxSeq = next.reduce((max, event) => {
|
|
164
|
+
const seq = Number.isFinite(event.seq) ? event.seq : 0;
|
|
165
|
+
return seq > max ? seq : max;
|
|
166
|
+
}, 0);
|
|
167
|
+
next.push({
|
|
168
|
+
seq: maxSeq + 1,
|
|
169
|
+
type: 'stream.assistant_delta',
|
|
170
|
+
timestamp: streamingAssistantTimestamp ?? new Date().toISOString(),
|
|
171
|
+
message: {
|
|
172
|
+
role: 'assistant',
|
|
173
|
+
content: streamingAssistantText,
|
|
174
|
+
timestamp: streamingAssistantTimestamp ?? new Date().toISOString()
|
|
175
|
+
}
|
|
176
|
+
});
|
|
157
177
|
}
|
|
158
178
|
return next;
|
|
159
|
-
}, [
|
|
179
|
+
}, [historyEvents, optimisticUserEvent, streamingAssistantText, streamingAssistantTimestamp, streamingSessionEvents]);
|
|
160
180
|
|
|
161
181
|
useEffect(() => {
|
|
162
182
|
if (!selectedSessionKey && filteredSessions.length > 0) {
|
|
@@ -188,7 +208,7 @@ export function ChatPage() {
|
|
|
188
208
|
return;
|
|
189
209
|
}
|
|
190
210
|
element.scrollTop = element.scrollHeight;
|
|
191
|
-
}, [
|
|
211
|
+
}, [mergedEvents, isSending, selectedSessionKey]);
|
|
192
212
|
|
|
193
213
|
useEffect(() => {
|
|
194
214
|
return () => {
|
|
@@ -200,10 +220,13 @@ export function ChatPage() {
|
|
|
200
220
|
streamRunIdRef.current += 1;
|
|
201
221
|
setIsSending(false);
|
|
202
222
|
setQueuedMessages([]);
|
|
203
|
-
|
|
223
|
+
setOptimisticUserEvent(null);
|
|
224
|
+
setStreamingSessionEvents([]);
|
|
225
|
+
setStreamingAssistantText('');
|
|
226
|
+
setStreamingAssistantTimestamp(null);
|
|
227
|
+
setIsAwaitingAssistantOutput(false);
|
|
204
228
|
const next = buildNewSessionKey(selectedAgentId);
|
|
205
229
|
setSelectedSessionKey(next);
|
|
206
|
-
setOptimisticUserMessage(null);
|
|
207
230
|
};
|
|
208
231
|
|
|
209
232
|
const handleDeleteSession = async () => {
|
|
@@ -225,9 +248,12 @@ export function ChatPage() {
|
|
|
225
248
|
streamRunIdRef.current += 1;
|
|
226
249
|
setIsSending(false);
|
|
227
250
|
setQueuedMessages([]);
|
|
228
|
-
|
|
251
|
+
setOptimisticUserEvent(null);
|
|
252
|
+
setStreamingSessionEvents([]);
|
|
253
|
+
setStreamingAssistantText('');
|
|
254
|
+
setStreamingAssistantTimestamp(null);
|
|
255
|
+
setIsAwaitingAssistantOutput(false);
|
|
229
256
|
setSelectedSessionKey(null);
|
|
230
|
-
setOptimisticUserMessage(null);
|
|
231
257
|
await sessionsQuery.refetch();
|
|
232
258
|
}
|
|
233
259
|
}
|
|
@@ -238,17 +264,26 @@ export function ChatPage() {
|
|
|
238
264
|
streamRunIdRef.current += 1;
|
|
239
265
|
const runId = streamRunIdRef.current;
|
|
240
266
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
267
|
+
setStreamingSessionEvents([]);
|
|
268
|
+
setStreamingAssistantText('');
|
|
269
|
+
setStreamingAssistantTimestamp(null);
|
|
270
|
+
setOptimisticUserEvent({
|
|
271
|
+
seq: 0,
|
|
272
|
+
type: 'message.user.optimistic',
|
|
273
|
+
timestamp: new Date().toISOString(),
|
|
274
|
+
message: {
|
|
275
|
+
role: 'user',
|
|
276
|
+
content: item.message,
|
|
277
|
+
timestamp: new Date().toISOString()
|
|
278
|
+
}
|
|
246
279
|
});
|
|
247
280
|
setIsSending(true);
|
|
281
|
+
setIsAwaitingAssistantOutput(true);
|
|
248
282
|
|
|
249
283
|
try {
|
|
250
284
|
let streamText = '';
|
|
251
285
|
const streamTimestamp = new Date().toISOString();
|
|
286
|
+
setStreamingAssistantTimestamp(streamTimestamp);
|
|
252
287
|
|
|
253
288
|
const result = await sendChatTurnStream({
|
|
254
289
|
message: item.message,
|
|
@@ -270,17 +305,36 @@ export function ChatPage() {
|
|
|
270
305
|
return;
|
|
271
306
|
}
|
|
272
307
|
streamText += event.delta;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
308
|
+
setStreamingAssistantText(streamText);
|
|
309
|
+
setIsAwaitingAssistantOutput(false);
|
|
310
|
+
},
|
|
311
|
+
onSessionEvent: (event) => {
|
|
312
|
+
if (runId !== streamRunIdRef.current) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (event.data.message?.role === 'user') {
|
|
316
|
+
setOptimisticUserEvent(null);
|
|
317
|
+
}
|
|
318
|
+
setStreamingSessionEvents((prev) => {
|
|
319
|
+
const next = [...prev];
|
|
320
|
+
const hit = next.findIndex((item) => item.seq === event.data.seq);
|
|
321
|
+
if (hit >= 0) {
|
|
322
|
+
next[hit] = event.data;
|
|
323
|
+
} else {
|
|
324
|
+
next.push(event.data);
|
|
325
|
+
}
|
|
326
|
+
return next;
|
|
277
327
|
});
|
|
328
|
+
if (event.data.message?.role === 'assistant') {
|
|
329
|
+
setStreamingAssistantText('');
|
|
330
|
+
setIsAwaitingAssistantOutput(false);
|
|
331
|
+
}
|
|
278
332
|
}
|
|
279
333
|
});
|
|
280
334
|
if (runId !== streamRunIdRef.current) {
|
|
281
335
|
return;
|
|
282
336
|
}
|
|
283
|
-
|
|
337
|
+
setOptimisticUserEvent(null);
|
|
284
338
|
if (result.sessionKey !== item.sessionKey) {
|
|
285
339
|
setSelectedSessionKey(result.sessionKey);
|
|
286
340
|
}
|
|
@@ -289,7 +343,10 @@ export function ChatPage() {
|
|
|
289
343
|
if (!activeSessionKey || activeSessionKey === item.sessionKey || activeSessionKey === result.sessionKey) {
|
|
290
344
|
await historyQuery.refetch();
|
|
291
345
|
}
|
|
292
|
-
|
|
346
|
+
setStreamingSessionEvents([]);
|
|
347
|
+
setStreamingAssistantText('');
|
|
348
|
+
setStreamingAssistantTimestamp(null);
|
|
349
|
+
setIsAwaitingAssistantOutput(false);
|
|
293
350
|
setIsSending(false);
|
|
294
351
|
} catch {
|
|
295
352
|
if (runId !== streamRunIdRef.current) {
|
|
@@ -297,8 +354,11 @@ export function ChatPage() {
|
|
|
297
354
|
}
|
|
298
355
|
streamRunIdRef.current += 1;
|
|
299
356
|
setIsSending(false);
|
|
300
|
-
|
|
301
|
-
|
|
357
|
+
setOptimisticUserEvent(null);
|
|
358
|
+
setStreamingSessionEvents([]);
|
|
359
|
+
setStreamingAssistantText('');
|
|
360
|
+
setStreamingAssistantTimestamp(null);
|
|
361
|
+
setIsAwaitingAssistantOutput(false);
|
|
302
362
|
if (options?.restoreDraftOnError) {
|
|
303
363
|
setDraft((prev) => prev.trim().length === 0 ? item.message : prev);
|
|
304
364
|
}
|
|
@@ -482,14 +542,14 @@ export function ChatPage() {
|
|
|
482
542
|
<div className="text-xs mt-1">{t('chatNoSessionHint')}</div>
|
|
483
543
|
</div>
|
|
484
544
|
</div>
|
|
485
|
-
) : historyQuery.isLoading ? (
|
|
545
|
+
) : historyQuery.isLoading && mergedEvents.length === 0 && !isSending && !isAwaitingAssistantOutput && !streamingAssistantText.trim() ? (
|
|
486
546
|
<div className="text-sm text-gray-500">{t('chatHistoryLoading')}</div>
|
|
487
547
|
) : (
|
|
488
548
|
<>
|
|
489
|
-
{
|
|
549
|
+
{mergedEvents.length === 0 ? (
|
|
490
550
|
<div className="text-sm text-gray-500">{t('chatNoMessages')}</div>
|
|
491
551
|
) : (
|
|
492
|
-
<ChatThread
|
|
552
|
+
<ChatThread events={mergedEvents} isSending={isSending && isAwaitingAssistantOutput} />
|
|
493
553
|
)}
|
|
494
554
|
</>
|
|
495
555
|
)}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
import type { SessionMessageView } from '@/api/types';
|
|
2
|
+
import type { SessionEventView, SessionMessageView } from '@/api/types';
|
|
3
3
|
import { cn } from '@/lib/utils';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
buildChatTimeline,
|
|
6
6
|
extractMessageText,
|
|
7
7
|
extractToolCards,
|
|
8
|
-
|
|
8
|
+
normalizeChatRole,
|
|
9
9
|
type ChatRole,
|
|
10
|
+
type ChatTimelineAssistantFlowItem,
|
|
10
11
|
type ToolCard
|
|
11
12
|
} from '@/lib/chat-message';
|
|
12
13
|
import { formatDateTime, t } from '@/lib/i18n';
|
|
@@ -16,7 +17,7 @@ import remarkGfm from 'remark-gfm';
|
|
|
16
17
|
import { Bot, Clock3, FileSearch, Globe, Search, SendHorizontal, Terminal, User, Wrench } from 'lucide-react';
|
|
17
18
|
|
|
18
19
|
type ChatThreadProps = {
|
|
19
|
-
|
|
20
|
+
events: SessionEventView[];
|
|
20
21
|
isSending: boolean;
|
|
21
22
|
className?: string;
|
|
22
23
|
};
|
|
@@ -142,12 +143,26 @@ function ToolCardView({ card }: { card: ToolCard }) {
|
|
|
142
143
|
);
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
function
|
|
146
|
-
|
|
146
|
+
function ReasoningBlock({ reasoning, isUser }: { reasoning: string; isUser: boolean }) {
|
|
147
|
+
return (
|
|
148
|
+
<details className="mt-3">
|
|
149
|
+
<summary className={cn('cursor-pointer text-xs', isUser ? 'text-primary-100' : 'text-gray-500')}>
|
|
150
|
+
{t('chatReasoning')}
|
|
151
|
+
</summary>
|
|
152
|
+
<pre className={cn('mt-2 text-[11px] whitespace-pre-wrap break-words rounded-lg p-2', isUser ? 'bg-primary-700/60' : 'bg-gray-100')}>
|
|
153
|
+
{reasoning}
|
|
154
|
+
</pre>
|
|
155
|
+
</details>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function MessageCard({ message }: { message: SessionMessageView }) {
|
|
160
|
+
const role = normalizeChatRole(message);
|
|
161
|
+
const primaryText = extractMessageText(message.content).trim();
|
|
162
|
+
const primaryReasoning = typeof message.reasoning_content === 'string' ? message.reasoning_content.trim() : '';
|
|
147
163
|
const toolCards = extractToolCards(message);
|
|
148
|
-
const reasoning = typeof message.reasoning_content === 'string' ? message.reasoning_content.trim() : '';
|
|
149
|
-
const shouldRenderText = Boolean(text) && !(role === 'tool' && toolCards.length > 0);
|
|
150
164
|
const isUser = role === 'user';
|
|
165
|
+
const shouldRenderPrimaryText = Boolean(primaryText) && !(role === 'tool' && toolCards.length > 0);
|
|
151
166
|
|
|
152
167
|
return (
|
|
153
168
|
<div
|
|
@@ -160,19 +175,10 @@ function MessageCard({ message, role }: { message: SessionMessageView; role: Cha
|
|
|
160
175
|
: 'bg-orange-50/70 text-gray-900 border-orange-200/80'
|
|
161
176
|
)}
|
|
162
177
|
>
|
|
163
|
-
{
|
|
164
|
-
{
|
|
165
|
-
<details className="mt-3">
|
|
166
|
-
<summary className={cn('cursor-pointer text-xs', isUser ? 'text-primary-100' : 'text-gray-500')}>
|
|
167
|
-
{t('chatReasoning')}
|
|
168
|
-
</summary>
|
|
169
|
-
<pre className={cn('mt-2 text-[11px] whitespace-pre-wrap break-words rounded-lg p-2', isUser ? 'bg-primary-700/60' : 'bg-gray-100')}>
|
|
170
|
-
{reasoning}
|
|
171
|
-
</pre>
|
|
172
|
-
</details>
|
|
173
|
-
)}
|
|
178
|
+
{shouldRenderPrimaryText && <MarkdownBlock text={primaryText} role={role} />}
|
|
179
|
+
{primaryReasoning && <ReasoningBlock reasoning={primaryReasoning} isUser={isUser} />}
|
|
174
180
|
{toolCards.length > 0 && (
|
|
175
|
-
<div className=
|
|
181
|
+
<div className={cn('space-y-2', (shouldRenderPrimaryText || primaryReasoning) && 'mt-3')}>
|
|
176
182
|
{toolCards.map((card, index) => (
|
|
177
183
|
<ToolCardView key={`${card.kind}-${card.name}-${card.callId ?? index}`} card={card} />
|
|
178
184
|
))}
|
|
@@ -182,26 +188,46 @@ function MessageCard({ message, role }: { message: SessionMessageView; role: Cha
|
|
|
182
188
|
);
|
|
183
189
|
}
|
|
184
190
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
function AssistantFlowCard({ item }: { item: ChatTimelineAssistantFlowItem }) {
|
|
192
|
+
return (
|
|
193
|
+
<div className="rounded-2xl border px-4 py-3 shadow-sm bg-white text-gray-900 border-gray-200">
|
|
194
|
+
{item.primaryText && <MarkdownBlock text={item.primaryText} role="assistant" />}
|
|
195
|
+
{item.primaryReasoning && <ReasoningBlock reasoning={item.primaryReasoning} isUser={false} />}
|
|
196
|
+
{item.toolCards.length > 0 && (
|
|
197
|
+
<div className={cn('space-y-2', (item.primaryText || item.primaryReasoning) && 'mt-3')}>
|
|
198
|
+
{item.toolCards.map((card, index) => (
|
|
199
|
+
<ToolCardView key={`${card.kind}-${card.name}-${card.callId ?? index}`} card={card} />
|
|
200
|
+
))}
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
{item.followupReasoning && <ReasoningBlock reasoning={item.followupReasoning} isUser={false} />}
|
|
204
|
+
{item.followupText && <div className="mt-3"><MarkdownBlock text={item.followupText} role="assistant" /></div>}
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function ChatThread({ events, isSending, className }: ChatThreadProps) {
|
|
210
|
+
const timeline = useMemo(() => buildChatTimeline(events), [events]);
|
|
188
211
|
|
|
189
212
|
return (
|
|
190
213
|
<div className={cn('space-y-5', className)}>
|
|
191
|
-
{
|
|
192
|
-
const
|
|
214
|
+
{timeline.map((item) => {
|
|
215
|
+
const role = item.kind === 'assistant_flow' ? 'assistant' : item.role;
|
|
216
|
+
const isUser = role === 'user';
|
|
193
217
|
return (
|
|
194
|
-
<div key={
|
|
195
|
-
{!isUser && <RoleAvatar role={
|
|
218
|
+
<div key={item.key} className={cn('flex gap-3', isUser ? 'justify-end' : 'justify-start')}>
|
|
219
|
+
{!isUser && <RoleAvatar role={role} />}
|
|
196
220
|
<div className={cn('max-w-[88%] min-w-[280px] space-y-2', isUser && 'flex flex-col items-end')}>
|
|
197
|
-
{
|
|
198
|
-
<
|
|
199
|
-
)
|
|
221
|
+
{item.kind === 'assistant_flow' ? (
|
|
222
|
+
<AssistantFlowCard item={item} />
|
|
223
|
+
) : (
|
|
224
|
+
<MessageCard message={item.message} />
|
|
225
|
+
)}
|
|
200
226
|
<div className={cn('text-[11px] px-1', isUser ? 'text-primary-300' : 'text-gray-400')}>
|
|
201
|
-
{roleTitle(
|
|
227
|
+
{roleTitle(role)} · {formatDateTime(item.timestamp)}
|
|
202
228
|
</div>
|
|
203
229
|
</div>
|
|
204
|
-
{isUser && <RoleAvatar role={
|
|
230
|
+
{isUser && <RoleAvatar role={role} />}
|
|
205
231
|
</div>
|
|
206
232
|
);
|
|
207
233
|
})}
|
package/src/hooks/useConfig.ts
CHANGED
|
@@ -150,7 +150,8 @@ export function useSessionHistory(key: string | null, limit = 200) {
|
|
|
150
150
|
queryKey: ['session-history', key, limit],
|
|
151
151
|
queryFn: () => fetchSessionHistory(key as string, limit),
|
|
152
152
|
enabled: Boolean(key),
|
|
153
|
-
staleTime: 5_000
|
|
153
|
+
staleTime: 5_000,
|
|
154
|
+
retry: false
|
|
154
155
|
});
|
|
155
156
|
}
|
|
156
157
|
|