@nextclaw/ui 0.6.9 → 0.6.11
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/.eslintrc.cjs +10 -0
- package/CHANGELOG.md +15 -0
- package/dist/assets/{ChannelsList-DACqpUYZ.js → ChannelsList-C49JQ-Zt.js} +1 -1
- package/dist/assets/ChatPage-DIx05c6s.js +36 -0
- package/dist/assets/{DocBrowser-D7mjKkGe.js → DocBrowser-CpOosDEI.js} +1 -1
- package/dist/assets/{LogoBadge-BlDT-g9R.js → LogoBadge-CL_8ZPXU.js} +1 -1
- package/dist/assets/MarketplacePage-BOzko5s9.js +49 -0
- package/dist/assets/{ModelConfig-DwRU5qrw.js → ModelConfig-BZ4ZfaQB.js} +1 -1
- package/dist/assets/ProvidersList-fPpJ5gl6.js +1 -0
- package/dist/assets/{RuntimeConfig-C7BRLGSC.js → RuntimeConfig-Dt9pLB9P.js} +1 -1
- package/dist/assets/{SecretsConfig-D5xZh7VF.js → SecretsConfig-C1PU0Yy8.js} +2 -2
- package/dist/assets/{SessionsConfig-ovpj_otA.js → SessionsConfig-EskBOofQ.js} +2 -2
- package/dist/assets/{card-Bf4CtrW8.js → card-C7Gtw2Vs.js} +1 -1
- package/dist/assets/index-Cn6_2To7.js +8 -0
- package/dist/assets/index-nEYGCJTC.css +1 -0
- package/dist/assets/{input-CaKJyoWZ.js → input-oBvxsnV9.js} +1 -1
- package/dist/assets/{label-BaXSWTKI.js → label-C7F8lMpQ.js} +1 -1
- package/dist/assets/{page-layout-DA6PFRtQ.js → page-layout-DO8BlScF.js} +1 -1
- package/dist/assets/session-run-status-Kg0FwAPn.js +3 -0
- package/dist/assets/{switch-Cvd5wZs-.js → switch-C6a5GyZB.js} +1 -1
- package/dist/assets/{tabs-custom-0PybLkXs.js → tabs-custom-BatFap5k.js} +1 -1
- package/dist/assets/{useConfirmDialog-DdtpSju1.js → useConfirmDialog-zJzVKMdu.js} +2 -2
- package/dist/assets/{vendor-C--HHaLf.js → vendor-TlME1INH.js} +84 -84
- package/dist/index.html +3 -3
- package/package.json +4 -2
- package/src/App.tsx +1 -2
- package/src/api/config.ts +205 -202
- package/src/api/types.ts +54 -24
- package/src/components/chat/ChatConversationPanel.tsx +102 -121
- package/src/components/chat/ChatPage.tsx +165 -437
- package/src/components/chat/ChatSidebar.tsx +30 -36
- package/src/components/chat/ChatThread.tsx +73 -131
- package/src/components/chat/chat-input/ChatInputBarView.tsx +82 -0
- package/src/components/chat/chat-input/ChatInputBottomToolbar.tsx +71 -0
- package/src/components/chat/chat-input/components/ChatInputModelStateHint.tsx +39 -0
- package/src/components/chat/chat-input/components/ChatInputSelectedSkillsSection.tsx +31 -0
- package/src/components/chat/chat-input/components/ChatInputSlashPanelSection.tsx +112 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputAttachButton.tsx +24 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputModelSelector.tsx +58 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSendControls.tsx +56 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector.tsx +40 -0
- package/src/components/chat/chat-input/useChatInputBarController.ts +313 -0
- package/src/components/chat/chat-input.types.ts +15 -0
- package/src/components/chat/chat-page-data.ts +121 -0
- package/src/components/chat/chat-page-runtime.ts +221 -0
- package/src/components/chat/chat-session-route.ts +59 -0
- package/src/components/chat/chat-stream/nextbot-parsers.ts +52 -0
- package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +413 -0
- package/src/components/chat/chat-stream/stream-event-adapter.ts +98 -0
- package/src/components/chat/chat-stream/transport.ts +159 -0
- package/src/components/chat/chat-stream/types.ts +76 -0
- package/src/components/chat/managers/chat-input.manager.ts +142 -0
- package/src/components/chat/managers/chat-run-status.manager.ts +32 -0
- package/src/components/chat/managers/chat-session-list.manager.ts +77 -0
- package/src/components/chat/managers/chat-stream-actions.manager.ts +34 -0
- package/src/components/chat/managers/chat-thread.manager.ts +86 -0
- package/src/components/chat/managers/chat-ui.manager.ts +65 -0
- package/src/components/chat/presenter/chat-presenter-context.tsx +25 -0
- package/src/components/chat/presenter/chat.presenter.ts +32 -0
- package/src/components/chat/stores/chat-input.store.ts +62 -0
- package/src/components/chat/stores/chat-run-status.store.ts +30 -0
- package/src/components/chat/stores/chat-session-list.store.ts +34 -0
- package/src/components/chat/stores/chat-thread.store.ts +52 -0
- package/src/components/chat/useChatRuntimeController.ts +134 -0
- package/src/components/chat/useChatSessionTypeState.ts +148 -0
- package/src/components/common/MaskedInput.tsx +1 -1
- package/src/components/config/ProviderForm.tsx +221 -14
- package/src/hooks/useConfig.ts +33 -2
- package/src/hooks/useObservable.ts +20 -0
- package/src/hooks/useWebSocket.ts +23 -1
- package/src/lib/chat-message.ts +2 -202
- package/src/lib/chat-runtime-utils.ts +250 -0
- package/src/lib/i18n.ts +11 -0
- package/tsconfig.json +2 -1
- package/vite.config.ts +2 -1
- package/dist/assets/ChatPage-iji0RkTR.js +0 -34
- package/dist/assets/MarketplacePage-CZq3jVgg.js +0 -49
- package/dist/assets/ProvidersList-DFxN3pjx.js +0 -1
- package/dist/assets/index-C_DhisNo.css +0 -1
- package/dist/assets/index-dKTqKCJo.js +0 -7
- package/dist/assets/session-run-status-CllIZxNf.js +0 -5
- package/src/components/chat/ChatInputBar.tsx +0 -590
- package/src/components/chat/useChatStreamController.ts +0 -591
package/src/api/types.ts
CHANGED
|
@@ -77,6 +77,7 @@ export type ProviderConnectionTestResult = {
|
|
|
77
77
|
export type ProviderAuthStartResult = {
|
|
78
78
|
provider: string;
|
|
79
79
|
kind: "device_code";
|
|
80
|
+
methodId?: string;
|
|
80
81
|
sessionId: string;
|
|
81
82
|
verificationUri: string;
|
|
82
83
|
userCode: string;
|
|
@@ -85,6 +86,10 @@ export type ProviderAuthStartResult = {
|
|
|
85
86
|
note?: string;
|
|
86
87
|
};
|
|
87
88
|
|
|
89
|
+
export type ProviderAuthStartRequest = {
|
|
90
|
+
methodId?: string;
|
|
91
|
+
};
|
|
92
|
+
|
|
88
93
|
export type ProviderAuthPollRequest = {
|
|
89
94
|
sessionId: string;
|
|
90
95
|
};
|
|
@@ -141,6 +146,8 @@ export type SessionEntryView = {
|
|
|
141
146
|
updatedAt: string;
|
|
142
147
|
label?: string;
|
|
143
148
|
preferredModel?: string;
|
|
149
|
+
sessionType: string;
|
|
150
|
+
sessionTypeMutable: boolean;
|
|
144
151
|
messageCount: number;
|
|
145
152
|
lastRole?: string;
|
|
146
153
|
lastTimestamp?: string;
|
|
@@ -172,6 +179,8 @@ export type SessionHistoryView = {
|
|
|
172
179
|
key: string;
|
|
173
180
|
totalMessages: number;
|
|
174
181
|
totalEvents: number;
|
|
182
|
+
sessionType: string;
|
|
183
|
+
sessionTypeMutable: boolean;
|
|
175
184
|
metadata: Record<string, unknown>;
|
|
176
185
|
messages: SessionMessageView[];
|
|
177
186
|
events: SessionEventView[];
|
|
@@ -180,6 +189,7 @@ export type SessionHistoryView = {
|
|
|
180
189
|
export type SessionPatchUpdate = {
|
|
181
190
|
label?: string | null;
|
|
182
191
|
preferredModel?: string | null;
|
|
192
|
+
sessionType?: string | null;
|
|
183
193
|
clearHistory?: boolean;
|
|
184
194
|
};
|
|
185
195
|
|
|
@@ -203,11 +213,42 @@ export type ChatTurnView = {
|
|
|
203
213
|
durationMs: number;
|
|
204
214
|
};
|
|
205
215
|
|
|
216
|
+
export type ChatTurnStreamReadyEvent = {
|
|
217
|
+
sessionKey: string;
|
|
218
|
+
requestedAt?: string;
|
|
219
|
+
runId?: string;
|
|
220
|
+
stopSupported?: boolean;
|
|
221
|
+
stopReason?: string;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export type ChatTurnStreamDeltaEvent = {
|
|
225
|
+
delta: string;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export type ChatTurnStreamSessionEvent = {
|
|
229
|
+
data: SessionEventView;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export type ChatTurnStreamErrorEvent = {
|
|
233
|
+
code?: string;
|
|
234
|
+
message?: string;
|
|
235
|
+
};
|
|
236
|
+
|
|
206
237
|
export type ChatCapabilitiesView = {
|
|
207
238
|
stopSupported: boolean;
|
|
208
239
|
stopReason?: string;
|
|
209
240
|
};
|
|
210
241
|
|
|
242
|
+
export type ChatSessionTypeOptionView = {
|
|
243
|
+
value: string;
|
|
244
|
+
label: string;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export type ChatSessionTypesView = {
|
|
248
|
+
defaultType: string;
|
|
249
|
+
options: ChatSessionTypeOptionView[];
|
|
250
|
+
};
|
|
251
|
+
|
|
211
252
|
export type ChatCommandOptionView = {
|
|
212
253
|
name: string;
|
|
213
254
|
description: string;
|
|
@@ -262,30 +303,6 @@ export type ChatRunListView = {
|
|
|
262
303
|
total: number;
|
|
263
304
|
};
|
|
264
305
|
|
|
265
|
-
export type ChatTurnStreamReadyEvent = {
|
|
266
|
-
event: "ready";
|
|
267
|
-
sessionKey: string;
|
|
268
|
-
requestedAt: string;
|
|
269
|
-
runId?: string;
|
|
270
|
-
stopSupported?: boolean;
|
|
271
|
-
stopReason?: string;
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
export type ChatTurnStreamDeltaEvent = {
|
|
275
|
-
event: "delta";
|
|
276
|
-
delta: string;
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
export type ChatTurnStreamSessionEvent = {
|
|
280
|
-
event: "session_event";
|
|
281
|
-
data: SessionEventView;
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
export type ChatTurnStreamFinalEvent = {
|
|
285
|
-
event: "final";
|
|
286
|
-
data: ChatTurnView;
|
|
287
|
-
};
|
|
288
|
-
|
|
289
306
|
export type CronScheduleView =
|
|
290
307
|
| { kind: "at"; atMs?: number | null }
|
|
291
308
|
| { kind: "every"; everyMs?: number | null }
|
|
@@ -458,6 +475,18 @@ export type ProviderSpecView = {
|
|
|
458
475
|
en?: string;
|
|
459
476
|
zh?: string;
|
|
460
477
|
};
|
|
478
|
+
methods?: Array<{
|
|
479
|
+
id: string;
|
|
480
|
+
label?: {
|
|
481
|
+
en?: string;
|
|
482
|
+
zh?: string;
|
|
483
|
+
};
|
|
484
|
+
hint?: {
|
|
485
|
+
en?: string;
|
|
486
|
+
zh?: string;
|
|
487
|
+
};
|
|
488
|
+
}>;
|
|
489
|
+
defaultMethodId?: string;
|
|
461
490
|
supportsCliImport?: boolean;
|
|
462
491
|
};
|
|
463
492
|
defaultModels?: string[];
|
|
@@ -559,6 +588,7 @@ export type ConfigActionExecuteResult = {
|
|
|
559
588
|
export type WsEvent =
|
|
560
589
|
| { type: 'config.updated'; payload: { path: string } }
|
|
561
590
|
| { type: 'run.updated'; payload: { run: ChatRunView } }
|
|
591
|
+
| { type: 'session.updated'; payload: { sessionKey: string } }
|
|
562
592
|
| { type: 'config.reload.started'; payload?: Record<string, unknown> }
|
|
563
593
|
| { type: 'config.reload.finished'; payload?: Record<string, unknown> }
|
|
564
594
|
| { type: 'error'; payload: { message: string; code?: string } }
|
|
@@ -1,44 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { MarketplaceInstalledRecord, SessionEventView } from '@/api/types';
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
|
|
3
2
|
import { Button } from '@/components/ui/button';
|
|
4
3
|
import { ChatThread } from '@/components/chat/ChatThread';
|
|
5
|
-
import { ChatInputBar, type ChatModelOption } from '@/components/chat/ChatInputBar';
|
|
6
4
|
import { ChatWelcome } from '@/components/chat/ChatWelcome';
|
|
5
|
+
import { ChatInputBarView } from '@/components/chat/chat-input/ChatInputBarView';
|
|
6
|
+
import { usePresenter } from '@/components/chat/presenter/chat-presenter-context';
|
|
7
|
+
import { useChatThreadStore } from '@/components/chat/stores/chat-thread.store';
|
|
7
8
|
import { t } from '@/lib/i18n';
|
|
9
|
+
import { cn } from '@/lib/utils';
|
|
8
10
|
import { Trash2 } from 'lucide-react';
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
isProviderStateResolved: boolean;
|
|
12
|
-
modelOptions: ChatModelOption[];
|
|
13
|
-
selectedModel: string;
|
|
14
|
-
onSelectedModelChange: (value: string) => void;
|
|
15
|
-
onGoToProviders: () => void;
|
|
16
|
-
skillRecords: MarketplaceInstalledRecord[];
|
|
17
|
-
isSkillsLoading?: boolean;
|
|
18
|
-
selectedSkills: string[];
|
|
19
|
-
onSelectedSkillsChange: (next: string[]) => void;
|
|
20
|
-
selectedSessionKey: string | null;
|
|
21
|
-
sessionDisplayName?: string;
|
|
22
|
-
canDeleteSession: boolean;
|
|
23
|
-
isDeletePending: boolean;
|
|
24
|
-
onDeleteSession: () => void;
|
|
25
|
-
onCreateSession: () => void;
|
|
26
|
-
threadRef: MutableRefObject<HTMLDivElement | null>;
|
|
27
|
-
onThreadScroll: () => void;
|
|
28
|
-
isHistoryLoading: boolean;
|
|
29
|
-
mergedEvents: SessionEventView[];
|
|
30
|
-
isSending: boolean;
|
|
31
|
-
isAwaitingAssistantOutput: boolean;
|
|
32
|
-
streamingAssistantText: string;
|
|
33
|
-
draft: string;
|
|
34
|
-
onDraftChange: (value: string) => void;
|
|
35
|
-
onSend: () => Promise<void> | void;
|
|
36
|
-
onStop: () => Promise<void> | void;
|
|
37
|
-
canStopGeneration: boolean;
|
|
38
|
-
stopDisabledReason?: string | null;
|
|
39
|
-
sendError?: string | null;
|
|
40
|
-
queuedCount: number;
|
|
41
|
-
};
|
|
12
|
+
const STICKY_BOTTOM_THRESHOLD_PX = 10;
|
|
42
13
|
|
|
43
14
|
function ChatConversationSkeleton() {
|
|
44
15
|
return (
|
|
@@ -68,80 +39,101 @@ function ChatConversationSkeleton() {
|
|
|
68
39
|
);
|
|
69
40
|
}
|
|
70
41
|
|
|
71
|
-
export function ChatConversationPanel({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
onCreateSession,
|
|
87
|
-
threadRef,
|
|
88
|
-
onThreadScroll,
|
|
89
|
-
isHistoryLoading,
|
|
90
|
-
mergedEvents,
|
|
91
|
-
isSending,
|
|
92
|
-
isAwaitingAssistantOutput,
|
|
93
|
-
streamingAssistantText,
|
|
94
|
-
draft,
|
|
95
|
-
onDraftChange,
|
|
96
|
-
onSend,
|
|
97
|
-
onStop,
|
|
98
|
-
canStopGeneration,
|
|
99
|
-
stopDisabledReason,
|
|
100
|
-
sendError,
|
|
101
|
-
queuedCount,
|
|
102
|
-
}: ChatConversationPanelProps) {
|
|
103
|
-
const showWelcome = !selectedSessionKey && mergedEvents.length === 0;
|
|
104
|
-
const hasConfiguredModel = modelOptions.length > 0;
|
|
105
|
-
const shouldShowProviderHint = isProviderStateResolved && !hasConfiguredModel;
|
|
42
|
+
export function ChatConversationPanel() {
|
|
43
|
+
const presenter = usePresenter();
|
|
44
|
+
const snapshot = useChatThreadStore((state) => state.snapshot);
|
|
45
|
+
const fallbackThreadRef = useRef<HTMLDivElement | null>(null);
|
|
46
|
+
const threadRef = snapshot.threadRef ?? fallbackThreadRef;
|
|
47
|
+
|
|
48
|
+
// --- Sticky-to-bottom scroll state ---
|
|
49
|
+
const isStickyRef = useRef(true);
|
|
50
|
+
const isProgrammaticScrollRef = useRef(false);
|
|
51
|
+
const previousSessionKeyRef = useRef<string | null>(null);
|
|
52
|
+
const pendingInitialScrollRef = useRef(false);
|
|
53
|
+
|
|
54
|
+
const showWelcome = !snapshot.selectedSessionKey && snapshot.uiMessages.length === 0 && !snapshot.isSending;
|
|
55
|
+
const hasConfiguredModel = snapshot.modelOptions.length > 0;
|
|
56
|
+
const shouldShowProviderHint = snapshot.isProviderStateResolved && !hasConfiguredModel;
|
|
106
57
|
const hideEmptyHint =
|
|
107
|
-
isHistoryLoading &&
|
|
108
|
-
|
|
109
|
-
!isSending &&
|
|
110
|
-
!isAwaitingAssistantOutput
|
|
111
|
-
|
|
58
|
+
snapshot.isHistoryLoading &&
|
|
59
|
+
snapshot.uiMessages.length === 0 &&
|
|
60
|
+
!snapshot.isSending &&
|
|
61
|
+
!snapshot.isAwaitingAssistantOutput;
|
|
62
|
+
|
|
63
|
+
const scrollToBottom = useCallback(() => {
|
|
64
|
+
const el = threadRef.current;
|
|
65
|
+
if (!el) return;
|
|
66
|
+
isProgrammaticScrollRef.current = true;
|
|
67
|
+
el.scrollTop = el.scrollHeight;
|
|
68
|
+
}, [threadRef]);
|
|
69
|
+
|
|
70
|
+
const handleScroll = useCallback(() => {
|
|
71
|
+
// Skip sticky check for programmatic scrolls
|
|
72
|
+
if (isProgrammaticScrollRef.current) {
|
|
73
|
+
isProgrammaticScrollRef.current = false;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const el = threadRef.current;
|
|
77
|
+
if (!el) return;
|
|
78
|
+
const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
79
|
+
isStickyRef.current = distanceFromBottom <= STICKY_BOTTOM_THRESHOLD_PX;
|
|
80
|
+
}, [threadRef]);
|
|
81
|
+
|
|
82
|
+
// Session change → force sticky + schedule initial scroll
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (previousSessionKeyRef.current === snapshot.selectedSessionKey) return;
|
|
85
|
+
previousSessionKeyRef.current = snapshot.selectedSessionKey;
|
|
86
|
+
isStickyRef.current = true;
|
|
87
|
+
pendingInitialScrollRef.current = true;
|
|
88
|
+
}, [snapshot.selectedSessionKey]);
|
|
89
|
+
|
|
90
|
+
// Initial scroll after history loads for a new session
|
|
91
|
+
useLayoutEffect(() => {
|
|
92
|
+
if (!pendingInitialScrollRef.current) return;
|
|
93
|
+
if (snapshot.isHistoryLoading || snapshot.uiMessages.length === 0) return;
|
|
94
|
+
pendingInitialScrollRef.current = false;
|
|
95
|
+
scrollToBottom();
|
|
96
|
+
}, [scrollToBottom, snapshot.isHistoryLoading, snapshot.uiMessages]);
|
|
97
|
+
|
|
98
|
+
// Streaming updates: keep bottom visible while still sticky.
|
|
99
|
+
useLayoutEffect(() => {
|
|
100
|
+
if (!isStickyRef.current) return;
|
|
101
|
+
if (snapshot.uiMessages.length === 0) return;
|
|
102
|
+
scrollToBottom();
|
|
103
|
+
}, [scrollToBottom, snapshot.uiMessages]);
|
|
112
104
|
|
|
113
|
-
if (!isProviderStateResolved) {
|
|
105
|
+
if (!snapshot.isProviderStateResolved) {
|
|
114
106
|
return <ChatConversationSkeleton />;
|
|
115
107
|
}
|
|
116
108
|
|
|
117
109
|
return (
|
|
118
110
|
<section className="flex-1 min-h-0 flex flex-col overflow-hidden bg-gradient-to-b from-gray-50/60 to-white">
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
</
|
|
127
|
-
<Button
|
|
128
|
-
variant="ghost"
|
|
129
|
-
size="icon"
|
|
130
|
-
className="rounded-lg shrink-0 text-gray-400 hover:text-destructive"
|
|
131
|
-
onClick={onDeleteSession}
|
|
132
|
-
disabled={!canDeleteSession || isDeletePending}
|
|
133
|
-
>
|
|
134
|
-
<Trash2 className="h-4 w-4" />
|
|
135
|
-
</Button>
|
|
111
|
+
<div className={cn(
|
|
112
|
+
"px-5 border-b border-gray-200/60 bg-white/80 backdrop-blur-sm flex items-center justify-between shrink-0 overflow-hidden transition-all duration-200",
|
|
113
|
+
snapshot.selectedSessionKey ? "py-3 opacity-100" : "h-0 py-0 opacity-0 border-b-0"
|
|
114
|
+
)}>
|
|
115
|
+
<div className="min-w-0 flex-1">
|
|
116
|
+
<span className="text-sm font-medium text-gray-700 truncate">
|
|
117
|
+
{snapshot.sessionDisplayName || snapshot.selectedSessionKey}
|
|
118
|
+
</span>
|
|
136
119
|
</div>
|
|
137
|
-
|
|
120
|
+
<Button
|
|
121
|
+
variant="ghost"
|
|
122
|
+
size="icon"
|
|
123
|
+
className="rounded-lg shrink-0 text-gray-400 hover:text-destructive"
|
|
124
|
+
onClick={presenter.chatThreadManager.deleteSession}
|
|
125
|
+
disabled={!snapshot.canDeleteSession || snapshot.isDeletePending}
|
|
126
|
+
>
|
|
127
|
+
<Trash2 className="h-4 w-4" />
|
|
128
|
+
</Button>
|
|
129
|
+
</div>
|
|
138
130
|
|
|
139
131
|
{shouldShowProviderHint && (
|
|
140
132
|
<div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 flex items-center justify-between gap-3 shrink-0">
|
|
141
133
|
<span className="text-xs text-amber-800">{t('chatModelNoOptions')}</span>
|
|
142
134
|
<button
|
|
143
135
|
type="button"
|
|
144
|
-
onClick={
|
|
136
|
+
onClick={presenter.chatThreadManager.goToProviders}
|
|
145
137
|
className="text-xs font-semibold text-amber-900 underline-offset-2 hover:underline"
|
|
146
138
|
>
|
|
147
139
|
{t('chatGoConfigureProvider')}
|
|
@@ -149,42 +141,31 @@ export function ChatConversationPanel({
|
|
|
149
141
|
</div>
|
|
150
142
|
)}
|
|
151
143
|
|
|
152
|
-
{
|
|
153
|
-
|
|
144
|
+
{snapshot.sessionTypeUnavailable && snapshot.sessionTypeUnavailableMessage?.trim() && (
|
|
145
|
+
<div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 shrink-0">
|
|
146
|
+
<span className="text-xs text-amber-800">{snapshot.sessionTypeUnavailableMessage}</span>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
<div
|
|
151
|
+
ref={threadRef}
|
|
152
|
+
onScroll={handleScroll}
|
|
153
|
+
className="flex-1 min-h-0 overflow-y-auto custom-scrollbar"
|
|
154
|
+
>
|
|
154
155
|
{showWelcome ? (
|
|
155
|
-
<ChatWelcome onCreateSession={
|
|
156
|
+
<ChatWelcome onCreateSession={presenter.chatThreadManager.createSession} />
|
|
156
157
|
) : hideEmptyHint ? (
|
|
157
158
|
<div className="h-full" />
|
|
158
|
-
) :
|
|
159
|
+
) : snapshot.uiMessages.length === 0 ? (
|
|
159
160
|
<div className="px-5 py-5 text-sm text-gray-500">{t('chatNoMessages')}</div>
|
|
160
161
|
) : (
|
|
161
162
|
<div className="mx-auto w-full max-w-[min(1120px,100%)] px-6 py-5">
|
|
162
|
-
<ChatThread
|
|
163
|
+
<ChatThread uiMessages={snapshot.uiMessages} isSending={snapshot.isSending && snapshot.isAwaitingAssistantOutput} />
|
|
163
164
|
</div>
|
|
164
165
|
)}
|
|
165
166
|
</div>
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
<ChatInputBar
|
|
169
|
-
isProviderStateResolved={isProviderStateResolved}
|
|
170
|
-
draft={draft}
|
|
171
|
-
onDraftChange={onDraftChange}
|
|
172
|
-
onSend={onSend}
|
|
173
|
-
onStop={onStop}
|
|
174
|
-
onGoToProviders={onGoToProviders}
|
|
175
|
-
canStopGeneration={canStopGeneration}
|
|
176
|
-
stopDisabledReason={stopDisabledReason}
|
|
177
|
-
sendError={sendError}
|
|
178
|
-
isSending={isSending}
|
|
179
|
-
queuedCount={queuedCount}
|
|
180
|
-
modelOptions={modelOptions}
|
|
181
|
-
selectedModel={selectedModel}
|
|
182
|
-
onSelectedModelChange={onSelectedModelChange}
|
|
183
|
-
skillRecords={skillRecords}
|
|
184
|
-
isSkillsLoading={isSkillsLoading}
|
|
185
|
-
selectedSkills={selectedSkills}
|
|
186
|
-
onSelectedSkillsChange={onSelectedSkillsChange}
|
|
187
|
-
/>
|
|
168
|
+
<ChatInputBarView />
|
|
188
169
|
</section>
|
|
189
170
|
);
|
|
190
171
|
}
|