@huyooo/ai-chat-frontend-react 0.1.2

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/dist/index.js ADDED
@@ -0,0 +1,1421 @@
1
+ // src/adapter.ts
2
+ function createNullAdapter() {
3
+ return {
4
+ async getSessions() {
5
+ return [];
6
+ },
7
+ async createSession(options) {
8
+ return {
9
+ id: Date.now().toString(),
10
+ title: options.title,
11
+ model: options.model,
12
+ mode: options.mode,
13
+ createdAt: /* @__PURE__ */ new Date(),
14
+ updatedAt: /* @__PURE__ */ new Date()
15
+ };
16
+ },
17
+ async updateSession() {
18
+ },
19
+ async deleteSession() {
20
+ },
21
+ async getMessages() {
22
+ return [];
23
+ },
24
+ async saveMessage(options) {
25
+ return {
26
+ id: Date.now().toString(),
27
+ sessionId: options.sessionId,
28
+ role: options.role,
29
+ content: options.content,
30
+ thinking: options.thinking,
31
+ toolCalls: options.toolCalls,
32
+ searchResults: options.searchResults,
33
+ timestamp: /* @__PURE__ */ new Date()
34
+ };
35
+ },
36
+ async *sendMessage() {
37
+ yield { type: "text", data: "\u65E0\u53EF\u7528\u7684 Adapter" };
38
+ yield { type: "done", data: "" };
39
+ },
40
+ cancel() {
41
+ }
42
+ };
43
+ }
44
+
45
+ // src/hooks/useChat.ts
46
+ import { useState, useCallback, useRef } from "react";
47
+ function generateId() {
48
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
49
+ }
50
+ function convertToMessage(record) {
51
+ return {
52
+ id: record.id,
53
+ role: record.role,
54
+ content: record.content,
55
+ thinking: record.thinking || void 0,
56
+ thinkingComplete: true,
57
+ toolCalls: record.toolCalls ? JSON.parse(record.toolCalls) : void 0,
58
+ searchResults: record.searchResults ? JSON.parse(record.searchResults) : void 0,
59
+ searching: false,
60
+ timestamp: record.timestamp
61
+ };
62
+ }
63
+ function useChat(options = {}) {
64
+ const {
65
+ adapter = createNullAdapter(),
66
+ defaultModel = "anthropic/claude-opus-4.5",
67
+ defaultMode = "agent"
68
+ } = options;
69
+ const [sessions, setSessions] = useState([]);
70
+ const [currentSessionId, setCurrentSessionId] = useState(null);
71
+ const [messages, setMessages] = useState([]);
72
+ const [mode, setModeState] = useState(defaultMode);
73
+ const [model, setModelState] = useState(defaultModel);
74
+ const [webSearch, setWebSearchState] = useState(true);
75
+ const [thinking, setThinkingState] = useState(true);
76
+ const [isLoading, setIsLoading] = useState(false);
77
+ const abortControllerRef = useRef(null);
78
+ const sessionsRef = useRef(sessions);
79
+ const messagesRef = useRef(messages);
80
+ const currentSessionIdRef = useRef(currentSessionId);
81
+ const modeRef = useRef(mode);
82
+ const modelRef = useRef(model);
83
+ const webSearchRef = useRef(webSearch);
84
+ const thinkingRef = useRef(thinking);
85
+ sessionsRef.current = sessions;
86
+ messagesRef.current = messages;
87
+ currentSessionIdRef.current = currentSessionId;
88
+ modeRef.current = mode;
89
+ modelRef.current = model;
90
+ webSearchRef.current = webSearch;
91
+ thinkingRef.current = thinking;
92
+ const loadSessions = useCallback(async () => {
93
+ try {
94
+ const list = await adapter.getSessions();
95
+ setSessions(list);
96
+ if (list.length > 0 && !currentSessionIdRef.current) {
97
+ const firstSession = list[0];
98
+ setCurrentSessionId(firstSession.id);
99
+ const savedMessages = await adapter.getMessages(firstSession.id);
100
+ setMessages(savedMessages.map(convertToMessage));
101
+ setModeState(firstSession.mode);
102
+ setModelState(firstSession.model);
103
+ }
104
+ } catch (error) {
105
+ console.error("\u52A0\u8F7D\u4F1A\u8BDD\u5931\u8D25:", error);
106
+ }
107
+ }, [adapter]);
108
+ const switchSession = useCallback(async (sessionId) => {
109
+ if (currentSessionIdRef.current === sessionId) return;
110
+ setCurrentSessionId(sessionId);
111
+ try {
112
+ const savedMessages = await adapter.getMessages(sessionId);
113
+ setMessages(savedMessages.map(convertToMessage));
114
+ const session = sessionsRef.current.find((s) => s.id === sessionId);
115
+ if (session) {
116
+ setModeState(session.mode);
117
+ setModelState(session.model);
118
+ }
119
+ } catch (error) {
120
+ console.error("\u52A0\u8F7D\u6D88\u606F\u5931\u8D25:", error);
121
+ setMessages([]);
122
+ }
123
+ }, [adapter]);
124
+ const createNewSession = useCallback(async () => {
125
+ try {
126
+ const session = await adapter.createSession({
127
+ title: "\u65B0\u5BF9\u8BDD",
128
+ model: modelRef.current,
129
+ mode: modeRef.current
130
+ });
131
+ setSessions((prev) => [session, ...prev]);
132
+ setCurrentSessionId(session.id);
133
+ setMessages([]);
134
+ } catch (error) {
135
+ console.error("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25:", error);
136
+ }
137
+ }, [adapter]);
138
+ const deleteSession = useCallback(async (sessionId) => {
139
+ try {
140
+ await adapter.deleteSession(sessionId);
141
+ setSessions((prev) => prev.filter((s) => s.id !== sessionId));
142
+ if (currentSessionIdRef.current === sessionId) {
143
+ const remaining = sessionsRef.current.filter((s) => s.id !== sessionId);
144
+ if (remaining.length > 0) {
145
+ await switchSession(remaining[0].id);
146
+ } else {
147
+ setCurrentSessionId(null);
148
+ setMessages([]);
149
+ }
150
+ }
151
+ } catch (error) {
152
+ console.error("\u5220\u9664\u4F1A\u8BDD\u5931\u8D25:", error);
153
+ }
154
+ }, [adapter, switchSession]);
155
+ const deleteCurrentSession = useCallback(async () => {
156
+ if (currentSessionIdRef.current) {
157
+ await deleteSession(currentSessionIdRef.current);
158
+ }
159
+ }, [deleteSession]);
160
+ const updateMessage = useCallback((index, progress) => {
161
+ setMessages((prev) => {
162
+ const newMessages = [...prev];
163
+ const msg = { ...newMessages[index] };
164
+ if (!msg) return prev;
165
+ switch (progress.type) {
166
+ case "thinking": {
167
+ const thinkingData = progress.data;
168
+ if (thinkingData.content) {
169
+ msg.thinking = (msg.thinking || "") + thinkingData.content;
170
+ }
171
+ msg.thinkingComplete = thinkingData.isComplete;
172
+ break;
173
+ }
174
+ case "search_start":
175
+ msg.searching = true;
176
+ break;
177
+ case "search_result": {
178
+ msg.searching = false;
179
+ const searchData = progress.data;
180
+ msg.searchResults = searchData.results || [];
181
+ break;
182
+ }
183
+ case "tool_call": {
184
+ const toolData = progress.data;
185
+ if (!msg.toolCalls) msg.toolCalls = [];
186
+ msg.toolCalls = [...msg.toolCalls, {
187
+ name: toolData.name,
188
+ args: toolData.args,
189
+ status: "running"
190
+ }];
191
+ break;
192
+ }
193
+ case "tool_result": {
194
+ const resultData = progress.data;
195
+ if (msg.toolCalls) {
196
+ msg.toolCalls = msg.toolCalls.map((t) => {
197
+ if (t.name === resultData.name && t.status === "running") {
198
+ return { ...t, result: resultData.result, status: "success" };
199
+ }
200
+ return t;
201
+ });
202
+ }
203
+ break;
204
+ }
205
+ case "text_delta":
206
+ msg.content = (msg.content || "") + progress.data;
207
+ break;
208
+ case "text":
209
+ if (!msg.content) {
210
+ msg.content = progress.data;
211
+ }
212
+ break;
213
+ case "error":
214
+ msg.content = (msg.content || "") + `
215
+
216
+ \u274C \u9519\u8BEF: ${progress.data}`;
217
+ break;
218
+ }
219
+ newMessages[index] = msg;
220
+ return newMessages;
221
+ });
222
+ }, []);
223
+ const sendMessage = useCallback(async (text, images) => {
224
+ if (!text.trim() || isLoading) return;
225
+ let sessionId = currentSessionIdRef.current;
226
+ if (!sessionId) {
227
+ try {
228
+ const session = await adapter.createSession({
229
+ title: "\u65B0\u5BF9\u8BDD",
230
+ model: modelRef.current,
231
+ mode: modeRef.current
232
+ });
233
+ setSessions((prev) => [session, ...prev]);
234
+ setCurrentSessionId(session.id);
235
+ sessionId = session.id;
236
+ } catch (error) {
237
+ console.error("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25:", error);
238
+ return;
239
+ }
240
+ }
241
+ const userMsg = {
242
+ id: generateId(),
243
+ role: "user",
244
+ content: text,
245
+ images,
246
+ timestamp: /* @__PURE__ */ new Date()
247
+ };
248
+ const currentMessages = messagesRef.current;
249
+ setMessages([...currentMessages, userMsg]);
250
+ try {
251
+ await adapter.saveMessage({
252
+ sessionId,
253
+ role: "user",
254
+ content: text
255
+ });
256
+ if (currentMessages.length === 0) {
257
+ const title = text.slice(0, 20) + (text.length > 20 ? "..." : "");
258
+ await adapter.updateSession(sessionId, { title });
259
+ setSessions((prev) => prev.map(
260
+ (s) => s.id === sessionId ? { ...s, title } : s
261
+ ));
262
+ }
263
+ } catch (error) {
264
+ console.error("\u4FDD\u5B58\u6D88\u606F\u5931\u8D25:", error);
265
+ }
266
+ const assistantMsgIndex = currentMessages.length + 1;
267
+ const assistantMsg = {
268
+ id: generateId(),
269
+ role: "assistant",
270
+ content: "",
271
+ toolCalls: [],
272
+ thinkingComplete: false,
273
+ searching: false,
274
+ loading: true,
275
+ timestamp: /* @__PURE__ */ new Date()
276
+ };
277
+ setMessages((prev) => [...prev, assistantMsg]);
278
+ setIsLoading(true);
279
+ abortControllerRef.current = new AbortController();
280
+ try {
281
+ for await (const progress of adapter.sendMessage(
282
+ text,
283
+ {
284
+ mode: modeRef.current,
285
+ model: modelRef.current,
286
+ enableWebSearch: webSearchRef.current,
287
+ thinkingMode: thinkingRef.current ? "enabled" : "disabled"
288
+ },
289
+ images
290
+ )) {
291
+ if (abortControllerRef.current?.signal.aborted) break;
292
+ updateMessage(assistantMsgIndex, progress);
293
+ if (progress.type === "done" || progress.type === "error") {
294
+ break;
295
+ }
296
+ }
297
+ } catch (error) {
298
+ console.error("\u53D1\u9001\u6D88\u606F\u5931\u8D25:", error);
299
+ updateMessage(assistantMsgIndex, {
300
+ type: "error",
301
+ data: error instanceof Error ? error.message : String(error)
302
+ });
303
+ } finally {
304
+ setIsLoading(false);
305
+ setMessages((prev) => {
306
+ const newMessages = [...prev];
307
+ const finalMsg = newMessages[assistantMsgIndex];
308
+ if (finalMsg) {
309
+ newMessages[assistantMsgIndex] = { ...finalMsg, loading: false };
310
+ if (sessionId) {
311
+ adapter.saveMessage({
312
+ sessionId,
313
+ role: "assistant",
314
+ content: finalMsg.content,
315
+ thinking: finalMsg.thinking,
316
+ toolCalls: finalMsg.toolCalls ? JSON.stringify(finalMsg.toolCalls) : void 0,
317
+ searchResults: finalMsg.searchResults ? JSON.stringify(finalMsg.searchResults) : void 0
318
+ }).catch((e) => console.error("\u4FDD\u5B58\u52A9\u624B\u6D88\u606F\u5931\u8D25:", e));
319
+ }
320
+ }
321
+ return newMessages;
322
+ });
323
+ abortControllerRef.current = null;
324
+ }
325
+ }, [adapter, isLoading, updateMessage]);
326
+ const cancelRequest = useCallback(() => {
327
+ adapter.cancel();
328
+ abortControllerRef.current?.abort();
329
+ setIsLoading(false);
330
+ }, [adapter]);
331
+ const copyMessage = useCallback(async (messageId) => {
332
+ const msg = messagesRef.current.find((m) => m.id === messageId);
333
+ if (!msg) return;
334
+ try {
335
+ await navigator.clipboard.writeText(msg.content);
336
+ setMessages((prev) => prev.map(
337
+ (m) => m.id === messageId ? { ...m, copied: true } : m
338
+ ));
339
+ setTimeout(() => {
340
+ setMessages((prev) => prev.map(
341
+ (m) => m.id === messageId ? { ...m, copied: false } : m
342
+ ));
343
+ }, 2e3);
344
+ } catch (err) {
345
+ console.error("\u590D\u5236\u5931\u8D25:", err);
346
+ }
347
+ }, []);
348
+ const regenerateMessage = useCallback((messageIndex) => {
349
+ const currentMsgs = messagesRef.current;
350
+ if (messageIndex > 0 && currentMsgs[messageIndex - 1]?.role === "user") {
351
+ const userMsg = currentMsgs[messageIndex - 1];
352
+ setMessages((prev) => prev.slice(0, messageIndex - 1));
353
+ sendMessage(userMsg.content, userMsg.images);
354
+ }
355
+ }, [sendMessage]);
356
+ const setWorkingDirectory = useCallback((dir) => {
357
+ if (adapter.setWorkingDir) {
358
+ adapter.setWorkingDir(dir);
359
+ }
360
+ }, [adapter]);
361
+ const setMode = useCallback((value) => setModeState(value), []);
362
+ const setModel = useCallback((value) => setModelState(value), []);
363
+ const setWebSearch = useCallback((value) => setWebSearchState(value), []);
364
+ const setThinking = useCallback((value) => setThinkingState(value), []);
365
+ return {
366
+ // 状态
367
+ sessions,
368
+ currentSessionId,
369
+ messages,
370
+ isLoading,
371
+ mode,
372
+ model,
373
+ webSearch,
374
+ thinking,
375
+ // 会话方法
376
+ loadSessions,
377
+ switchSession,
378
+ createNewSession,
379
+ deleteSession,
380
+ deleteCurrentSession,
381
+ // 消息方法
382
+ sendMessage,
383
+ cancelRequest,
384
+ copyMessage,
385
+ regenerateMessage,
386
+ // 配置方法
387
+ setMode,
388
+ setModel,
389
+ setWebSearch,
390
+ setThinking,
391
+ // 工具方法
392
+ setWorkingDirectory
393
+ };
394
+ }
395
+
396
+ // src/components/ChatPanel.tsx
397
+ import { useEffect as useEffect3, useRef as useRef4, useCallback as useCallback4 } from "react";
398
+
399
+ // src/types/index.ts
400
+ var DEFAULT_MODELS = [
401
+ {
402
+ provider: "openrouter",
403
+ model: "anthropic/claude-opus-4.5",
404
+ displayName: "Claude Opus 4.5",
405
+ supportsTools: true,
406
+ supportsWebSearch: true,
407
+ supportedThinkingModes: ["enabled", "disabled"]
408
+ },
409
+ {
410
+ provider: "doubao",
411
+ model: "doubao-seed-1-6-251015",
412
+ displayName: "Doubao Seed",
413
+ supportsTools: true,
414
+ supportsWebSearch: true,
415
+ supportedThinkingModes: ["enabled", "disabled"]
416
+ },
417
+ {
418
+ provider: "deepseek",
419
+ model: "deepseek-v3-1-terminus",
420
+ displayName: "DeepSeek V3",
421
+ supportsTools: true,
422
+ supportsWebSearch: true,
423
+ supportedThinkingModes: ["enabled", "disabled"]
424
+ },
425
+ {
426
+ provider: "qwen",
427
+ model: "qwen3-max-preview",
428
+ displayName: "Qwen Max",
429
+ supportsTools: true,
430
+ supportsWebSearch: true,
431
+ supportedThinkingModes: ["enabled", "disabled"]
432
+ },
433
+ {
434
+ provider: "gemini",
435
+ model: "gemini-3-pro-preview",
436
+ displayName: "Gemini 3 Pro",
437
+ supportsTools: true,
438
+ supportsWebSearch: true,
439
+ supportedThinkingModes: ["enabled", "disabled"]
440
+ }
441
+ ];
442
+ var FileType = /* @__PURE__ */ ((FileType2) => {
443
+ FileType2["FOLDER"] = "folder";
444
+ FileType2["IMAGE"] = "image";
445
+ FileType2["VIDEO"] = "video";
446
+ FileType2["AUDIO"] = "audio";
447
+ FileType2["TEXT"] = "text";
448
+ FileType2["PDF"] = "pdf";
449
+ FileType2["CODE"] = "code";
450
+ FileType2["ARCHIVE"] = "archive";
451
+ FileType2["OTHER"] = "other";
452
+ return FileType2;
453
+ })(FileType || {});
454
+
455
+ // src/components/chat/ui/ChatHeader.tsx
456
+ import { useState as useState2, useRef as useRef2, useEffect, useCallback as useCallback2 } from "react";
457
+ import { Plus, Clock, MoreHorizontal, X, MessageSquare, Pencil, Trash2 } from "lucide-react";
458
+ import { jsx, jsxs } from "react/jsx-runtime";
459
+ function formatTime(date) {
460
+ if (!date) return "";
461
+ const d = new Date(date);
462
+ const now = /* @__PURE__ */ new Date();
463
+ const diff = now.getTime() - d.getTime();
464
+ const days = Math.floor(diff / (1e3 * 60 * 60 * 24));
465
+ if (days === 0) {
466
+ return d.toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
467
+ } else if (days === 1) {
468
+ return "\u6628\u5929";
469
+ } else if (days < 7) {
470
+ return `${days}\u5929\u524D`;
471
+ } else {
472
+ return d.toLocaleDateString("zh-CN", { month: "short", day: "numeric" });
473
+ }
474
+ }
475
+ var ChatHeader = ({
476
+ sessions,
477
+ currentSessionId,
478
+ showClose = false,
479
+ onNewSession,
480
+ onSwitchSession,
481
+ onDeleteSession,
482
+ onClose,
483
+ onClearAll,
484
+ onCloseOthers,
485
+ onExport,
486
+ onCopyId,
487
+ onFeedback,
488
+ onSettings
489
+ }) => {
490
+ const [historyOpen, setHistoryOpen] = useState2(false);
491
+ const [moreMenuOpen, setMoreMenuOpen] = useState2(false);
492
+ const [hiddenTabs, setHiddenTabs] = useState2(/* @__PURE__ */ new Set());
493
+ const historyRef = useRef2(null);
494
+ const moreMenuRef = useRef2(null);
495
+ const visibleSessions = sessions.filter((s) => !hiddenTabs.has(s.id));
496
+ useEffect(() => {
497
+ const handleClickOutside = (event) => {
498
+ const target = event.target;
499
+ if (historyRef.current && !historyRef.current.contains(target)) {
500
+ setHistoryOpen(false);
501
+ }
502
+ if (moreMenuRef.current && !moreMenuRef.current.contains(target)) {
503
+ setMoreMenuOpen(false);
504
+ }
505
+ };
506
+ document.addEventListener("click", handleClickOutside);
507
+ return () => document.removeEventListener("click", handleClickOutside);
508
+ }, []);
509
+ const handleSwitchSession = useCallback2(
510
+ (sessionId) => {
511
+ onSwitchSession?.(sessionId);
512
+ setHistoryOpen(false);
513
+ },
514
+ [onSwitchSession]
515
+ );
516
+ const handleHideTab = useCallback2(
517
+ (sessionId, e) => {
518
+ e.stopPropagation();
519
+ setHiddenTabs((prev) => /* @__PURE__ */ new Set([...prev, sessionId]));
520
+ if (sessionId === currentSessionId) {
521
+ const remaining = sessions.filter((s) => s.id !== sessionId && !hiddenTabs.has(s.id));
522
+ if (remaining.length > 0) {
523
+ onSwitchSession?.(remaining[0].id);
524
+ }
525
+ }
526
+ },
527
+ [currentSessionId, sessions, hiddenTabs, onSwitchSession]
528
+ );
529
+ const handleDeleteSession = (sessionId, e) => {
530
+ e.stopPropagation();
531
+ if (window.confirm("\u786E\u5B9A\u8981\u5220\u9664\u8FD9\u4E2A\u5BF9\u8BDD\u5417\uFF1F")) {
532
+ onDeleteSession?.(sessionId);
533
+ }
534
+ };
535
+ const handleMenuClick = (callback) => {
536
+ callback?.();
537
+ setMoreMenuOpen(false);
538
+ };
539
+ return /* @__PURE__ */ jsxs("div", { className: "chat-header", children: [
540
+ /* @__PURE__ */ jsx("div", { className: "chat-tabs", children: visibleSessions.length === 0 ? /* @__PURE__ */ jsx("span", { className: "chat-tab active", children: /* @__PURE__ */ jsx("span", { className: "chat-tab-title", children: "New Chat" }) }) : visibleSessions.map((session) => {
541
+ const title = session.title === "\u65B0\u5BF9\u8BDD" ? "New Chat" : session.title;
542
+ const isActive = session.id === currentSessionId;
543
+ return /* @__PURE__ */ jsxs(
544
+ "div",
545
+ {
546
+ className: `chat-tab${isActive ? " active" : ""}`,
547
+ onClick: () => handleSwitchSession(session.id),
548
+ title: session.title,
549
+ children: [
550
+ /* @__PURE__ */ jsx("span", { className: "chat-tab-title", children: title }),
551
+ /* @__PURE__ */ jsx(
552
+ "span",
553
+ {
554
+ className: "chat-tab-close",
555
+ onClick: (e) => handleHideTab(session.id, e),
556
+ title: "\u5173\u95ED\u6807\u7B7E",
557
+ children: /* @__PURE__ */ jsx(X, { size: 12 })
558
+ }
559
+ )
560
+ ]
561
+ },
562
+ session.id
563
+ );
564
+ }) }),
565
+ /* @__PURE__ */ jsxs("div", { className: "chat-header-actions", children: [
566
+ /* @__PURE__ */ jsx("button", { className: "header-btn", onClick: onNewSession, title: "\u65B0\u5EFA\u5BF9\u8BDD", children: /* @__PURE__ */ jsx(Plus, { size: 14 }) }),
567
+ /* @__PURE__ */ jsxs("div", { ref: historyRef, style: { position: "relative" }, children: [
568
+ /* @__PURE__ */ jsx(
569
+ "button",
570
+ {
571
+ className: `header-btn${historyOpen ? " active" : ""}`,
572
+ onClick: (e) => {
573
+ e.stopPropagation();
574
+ setHistoryOpen(!historyOpen);
575
+ setMoreMenuOpen(false);
576
+ },
577
+ title: "\u5386\u53F2\u8BB0\u5F55",
578
+ children: /* @__PURE__ */ jsx(Clock, { size: 14 })
579
+ }
580
+ ),
581
+ historyOpen && /* @__PURE__ */ jsx("div", { className: "history-panel", children: sessions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "history-empty", children: "\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD" }) : sessions.map((session) => {
582
+ const isCurrent = session.id === currentSessionId;
583
+ return /* @__PURE__ */ jsxs(
584
+ "div",
585
+ {
586
+ className: `history-item${isCurrent ? " active" : ""}`,
587
+ children: [
588
+ /* @__PURE__ */ jsxs(
589
+ "button",
590
+ {
591
+ className: "history-item-content",
592
+ onClick: () => handleSwitchSession(session.id),
593
+ children: [
594
+ /* @__PURE__ */ jsx(MessageSquare, { size: 12 }),
595
+ /* @__PURE__ */ jsx("span", { className: "history-item-title", children: session.title === "\u65B0\u5BF9\u8BDD" ? "New Chat" : session.title }),
596
+ /* @__PURE__ */ jsx("span", { className: "history-item-time", children: isCurrent ? "Current" : formatTime(session.updatedAt) })
597
+ ]
598
+ }
599
+ ),
600
+ /* @__PURE__ */ jsxs("div", { className: "history-item-actions", children: [
601
+ /* @__PURE__ */ jsx("button", { className: "history-action-btn", title: "\u7F16\u8F91", children: /* @__PURE__ */ jsx(Pencil, { size: 10 }) }),
602
+ /* @__PURE__ */ jsx(
603
+ "button",
604
+ {
605
+ className: "history-action-btn delete",
606
+ title: "\u5220\u9664",
607
+ onClick: (e) => handleDeleteSession(session.id, e),
608
+ children: /* @__PURE__ */ jsx(Trash2, { size: 10 })
609
+ }
610
+ )
611
+ ] })
612
+ ]
613
+ },
614
+ session.id
615
+ );
616
+ }) })
617
+ ] }),
618
+ /* @__PURE__ */ jsxs("div", { ref: moreMenuRef, style: { position: "relative" }, children: [
619
+ /* @__PURE__ */ jsx(
620
+ "button",
621
+ {
622
+ className: `header-btn${moreMenuOpen ? " active" : ""}`,
623
+ onClick: (e) => {
624
+ e.stopPropagation();
625
+ setMoreMenuOpen(!moreMenuOpen);
626
+ setHistoryOpen(false);
627
+ },
628
+ title: "\u66F4\u591A\u9009\u9879",
629
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { size: 14 })
630
+ }
631
+ ),
632
+ moreMenuOpen && /* @__PURE__ */ jsxs("div", { className: "more-menu", children: [
633
+ showClose && /* @__PURE__ */ jsxs("button", { className: "menu-item", onClick: () => handleMenuClick(onClose), children: [
634
+ /* @__PURE__ */ jsx("span", { children: "\u5173\u95ED\u5BF9\u8BDD" }),
635
+ /* @__PURE__ */ jsx("span", { className: "menu-shortcut", children: "\u2318 W" })
636
+ ] }),
637
+ onClearAll && /* @__PURE__ */ jsx("button", { className: "menu-item", onClick: () => handleMenuClick(onClearAll), children: "\u6E05\u7A7A\u6240\u6709\u5BF9\u8BDD" }),
638
+ onCloseOthers && /* @__PURE__ */ jsx("button", { className: "menu-item", onClick: () => handleMenuClick(onCloseOthers), children: "\u5173\u95ED\u5176\u4ED6\u5BF9\u8BDD" }),
639
+ (showClose || onClearAll || onCloseOthers) && /* @__PURE__ */ jsx("div", { className: "menu-divider" }),
640
+ onExport && /* @__PURE__ */ jsx("button", { className: "menu-item", onClick: () => handleMenuClick(onExport), children: "\u5BFC\u51FA\u5BF9\u8BDD" }),
641
+ onCopyId && /* @__PURE__ */ jsx("button", { className: "menu-item", onClick: () => handleMenuClick(onCopyId), children: "\u590D\u5236\u8BF7\u6C42 ID" }),
642
+ onFeedback && /* @__PURE__ */ jsx("button", { className: "menu-item", onClick: () => handleMenuClick(onFeedback), children: "\u53CD\u9988" }),
643
+ (onExport || onCopyId || onFeedback) && onSettings && /* @__PURE__ */ jsx("div", { className: "menu-divider" }),
644
+ onSettings && /* @__PURE__ */ jsx("button", { className: "menu-item", onClick: () => handleMenuClick(onSettings), children: "Agent \u8BBE\u7F6E" })
645
+ ] })
646
+ ] })
647
+ ] })
648
+ ] });
649
+ };
650
+
651
+ // src/components/chat/ui/WelcomeMessage.tsx
652
+ import { Wand2, ImageIcon, Video, Terminal } from "lucide-react";
653
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
654
+ var QUICK_ACTIONS = [
655
+ {
656
+ id: "txt2img",
657
+ Icon: Wand2,
658
+ label: "\u6587\u751F\u56FE",
659
+ desc: "AI \u7ED8\u5236\u521B\u610F\u56FE\u50CF",
660
+ prompt: "\u5E2E\u6211\u751F\u6210\u4E00\u5F20\u56FE\u7247\uFF1A",
661
+ gradient: "purple",
662
+ iconColor: "purple",
663
+ featured: true
664
+ },
665
+ {
666
+ id: "img2img",
667
+ Icon: ImageIcon,
668
+ label: "\u56FE\u751F\u56FE",
669
+ desc: "\u98CE\u683C\u8FC1\u79FB",
670
+ prompt: "\u57FA\u4E8E\u8FD9\u5F20\u56FE\u7247\u8FDB\u884C\u98CE\u683C\u8F6C\u6362",
671
+ gradient: "blue",
672
+ iconColor: "blue",
673
+ featured: false
674
+ },
675
+ {
676
+ id: "img2vid",
677
+ Icon: Video,
678
+ label: "\u56FE\u751F\u89C6\u9891",
679
+ desc: "\u52A8\u6001\u5316",
680
+ prompt: "\u5C06\u8FD9\u5F20\u56FE\u7247\u8F6C\u6362\u6210\u89C6\u9891",
681
+ gradient: "emerald",
682
+ iconColor: "emerald",
683
+ featured: false
684
+ },
685
+ {
686
+ id: "cmd",
687
+ Icon: Terminal,
688
+ label: "\u6267\u884C\u547D\u4EE4",
689
+ desc: "\u7CFB\u7EDF\u7BA1\u7406",
690
+ prompt: "\u6267\u884C\u547D\u4EE4\uFF1A",
691
+ gradient: "orange",
692
+ iconColor: "orange",
693
+ featured: true
694
+ }
695
+ ];
696
+ var WelcomeMessage = ({ onQuickAction }) => {
697
+ return /* @__PURE__ */ jsxs2("div", { className: "welcome-message", children: [
698
+ /* @__PURE__ */ jsx2("div", { className: "welcome-glow purple" }),
699
+ /* @__PURE__ */ jsx2("div", { className: "welcome-glow blue" }),
700
+ /* @__PURE__ */ jsxs2("div", { className: "welcome-title-area", children: [
701
+ /* @__PURE__ */ jsxs2("h1", { className: "welcome-title", children: [
702
+ "Create",
703
+ /* @__PURE__ */ jsx2("br", {}),
704
+ /* @__PURE__ */ jsx2("span", { className: "welcome-title-accent", children: "Everything" })
705
+ ] }),
706
+ /* @__PURE__ */ jsx2("p", { className: "welcome-subtitle", children: "\u91CA\u653E AI \u7684\u65E0\u9650\u521B\u9020\u529B" })
707
+ ] }),
708
+ /* @__PURE__ */ jsx2("div", { className: "quick-actions", children: QUICK_ACTIONS.map((action) => /* @__PURE__ */ jsxs2(
709
+ "button",
710
+ {
711
+ className: `quick-action-btn${action.featured ? " featured" : ""}`,
712
+ onClick: () => onQuickAction(action.prompt),
713
+ children: [
714
+ /* @__PURE__ */ jsx2("div", { className: `quick-action-gradient ${action.gradient}` }),
715
+ /* @__PURE__ */ jsx2(action.Icon, { className: `quick-action-icon ${action.iconColor}` }),
716
+ /* @__PURE__ */ jsxs2("div", { className: "quick-action-text", children: [
717
+ /* @__PURE__ */ jsx2("span", { className: "quick-action-label", children: action.label }),
718
+ /* @__PURE__ */ jsx2("span", { className: "quick-action-desc", children: action.desc })
719
+ ] }),
720
+ /* @__PURE__ */ jsx2("div", { className: "quick-action-glow" })
721
+ ]
722
+ },
723
+ action.id
724
+ )) }),
725
+ /* @__PURE__ */ jsx2("div", { className: "welcome-footer", children: /* @__PURE__ */ jsx2("div", { className: "welcome-footer-line" }) })
726
+ ] });
727
+ };
728
+
729
+ // src/components/chat/messages/MessageBubble.tsx
730
+ import { Copy, Check as Check2, RefreshCw } from "lucide-react";
731
+
732
+ // src/components/chat/messages/ExecutionSteps.tsx
733
+ import { useState as useState3 } from "react";
734
+ import {
735
+ ChevronDown,
736
+ ChevronUp,
737
+ Sparkles,
738
+ Globe,
739
+ FileText,
740
+ FileEdit,
741
+ Terminal as Terminal2,
742
+ Search,
743
+ Folder,
744
+ FolderPlus,
745
+ Trash2 as Trash22,
746
+ Image,
747
+ Video as Video2,
748
+ Wrench,
749
+ ExternalLink
750
+ } from "lucide-react";
751
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
752
+ var StepItem = ({
753
+ icon,
754
+ title,
755
+ status,
756
+ extra,
757
+ detail,
758
+ defaultExpanded = false
759
+ }) => {
760
+ const [expanded, setExpanded] = useState3(defaultExpanded);
761
+ const isRunning = status === "running";
762
+ const hasDetail = !!detail;
763
+ return /* @__PURE__ */ jsxs3("div", { className: "step-item", children: [
764
+ /* @__PURE__ */ jsxs3(
765
+ "button",
766
+ {
767
+ className: `step-header${isRunning ? " running" : ""}`,
768
+ onClick: () => hasDetail && setExpanded(!expanded),
769
+ disabled: !hasDetail,
770
+ style: { cursor: hasDetail ? "pointer" : "default" },
771
+ children: [
772
+ /* @__PURE__ */ jsx3("span", { className: `step-icon${isRunning ? " pulse" : ""}`, children: icon }),
773
+ /* @__PURE__ */ jsx3("span", { className: "step-title", children: title }),
774
+ hasDetail && (expanded ? /* @__PURE__ */ jsx3(ChevronUp, { className: "step-chevron", size: 12 }) : /* @__PURE__ */ jsx3(ChevronDown, { className: "step-chevron", size: 12 })),
775
+ extra && /* @__PURE__ */ jsx3("span", { className: "step-extra", children: extra })
776
+ ]
777
+ }
778
+ ),
779
+ expanded && detail && /* @__PURE__ */ jsx3("div", { className: "step-detail", children: typeof detail === "string" ? /* @__PURE__ */ jsx3("pre", { children: detail }) : detail })
780
+ ] });
781
+ };
782
+ function getToolDisplayName(name) {
783
+ const nameMap = {
784
+ read_file: "\u8BFB\u53D6\u6587\u4EF6",
785
+ write_file: "\u5199\u5165\u6587\u4EF6",
786
+ execute_command: "\u6267\u884C\u547D\u4EE4",
787
+ search_files: "\u641C\u7D22\u6587\u4EF6",
788
+ list_directory: "\u5217\u51FA\u76EE\u5F55",
789
+ create_directory: "\u521B\u5EFA\u76EE\u5F55",
790
+ delete_file: "\u5220\u9664\u6587\u4EF6",
791
+ web_search: "\u7F51\u9875\u641C\u7D22",
792
+ generate_image: "\u751F\u6210\u56FE\u7247",
793
+ image_to_video: "\u56FE\u7247\u8F6C\u89C6\u9891"
794
+ };
795
+ return nameMap[name] || name;
796
+ }
797
+ function getToolIcon(name) {
798
+ switch (name) {
799
+ case "read_file":
800
+ return /* @__PURE__ */ jsx3(FileText, { size: 14 });
801
+ case "write_file":
802
+ return /* @__PURE__ */ jsx3(FileEdit, { size: 14 });
803
+ case "execute_command":
804
+ return /* @__PURE__ */ jsx3(Terminal2, { size: 14 });
805
+ case "search_files":
806
+ return /* @__PURE__ */ jsx3(Search, { size: 14 });
807
+ case "list_directory":
808
+ return /* @__PURE__ */ jsx3(Folder, { size: 14 });
809
+ case "create_directory":
810
+ return /* @__PURE__ */ jsx3(FolderPlus, { size: 14 });
811
+ case "delete_file":
812
+ return /* @__PURE__ */ jsx3(Trash22, { size: 14 });
813
+ case "web_search":
814
+ return /* @__PURE__ */ jsx3(Globe, { size: 14 });
815
+ case "generate_image":
816
+ return /* @__PURE__ */ jsx3(Image, { size: 14 });
817
+ case "image_to_video":
818
+ return /* @__PURE__ */ jsx3(Video2, { size: 14 });
819
+ default:
820
+ return /* @__PURE__ */ jsx3(Wrench, { size: 14 });
821
+ }
822
+ }
823
+ function formatToolArgs(args) {
824
+ if (!args) return "";
825
+ return Object.entries(args).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join("\n");
826
+ }
827
+ var ExecutionSteps = ({
828
+ loading,
829
+ hasContent,
830
+ thinking,
831
+ thinkingComplete = true,
832
+ thinkingDuration,
833
+ searching,
834
+ searchResults,
835
+ toolCalls
836
+ }) => {
837
+ const hasSteps = thinking || searching || searchResults && searchResults.length > 0 || toolCalls && toolCalls.length > 0 || loading && !hasContent;
838
+ if (!hasSteps) return null;
839
+ return /* @__PURE__ */ jsxs3("div", { className: "execution-steps", children: [
840
+ loading && !hasContent && !thinking && !searching && (!toolCalls || toolCalls.length === 0) && /* @__PURE__ */ jsx3(
841
+ StepItem,
842
+ {
843
+ icon: /* @__PURE__ */ jsx3(Sparkles, { size: 14 }),
844
+ title: "\u6B63\u5728\u89C4\u5212\u4E0B\u4E00\u6B65...",
845
+ status: "running"
846
+ }
847
+ ),
848
+ thinking && /* @__PURE__ */ jsx3(
849
+ StepItem,
850
+ {
851
+ icon: /* @__PURE__ */ jsx3(Sparkles, { size: 14 }),
852
+ title: thinkingComplete ? "\u601D\u8003\u5B8C\u6210" : "\u601D\u8003\u4E2D...",
853
+ status: thinkingComplete ? "completed" : "running",
854
+ extra: thinkingDuration ? `${thinkingDuration}s` : void 0,
855
+ detail: thinking
856
+ }
857
+ ),
858
+ (searching || searchResults && searchResults.length > 0) && /* @__PURE__ */ jsx3(
859
+ StepItem,
860
+ {
861
+ icon: /* @__PURE__ */ jsx3(Globe, { size: 14 }),
862
+ title: searching ? "\u641C\u7D22\u4E2D..." : `\u641C\u7D22\u5B8C\u6210 ${searchResults?.length || 0} \u6761\u7ED3\u679C`,
863
+ status: searching ? "running" : "completed",
864
+ detail: searchResults && searchResults.length > 0 ? /* @__PURE__ */ jsx3("div", { children: searchResults.map((result, i) => /* @__PURE__ */ jsxs3(
865
+ "a",
866
+ {
867
+ href: result.url,
868
+ target: "_blank",
869
+ rel: "noopener noreferrer",
870
+ className: "search-result-item",
871
+ children: [
872
+ /* @__PURE__ */ jsxs3("div", { className: "search-result-title", children: [
873
+ /* @__PURE__ */ jsx3("span", { children: result.title }),
874
+ /* @__PURE__ */ jsx3(ExternalLink, { size: 12, style: { opacity: 0.5 } })
875
+ ] }),
876
+ /* @__PURE__ */ jsx3("div", { className: "search-result-snippet", children: result.snippet })
877
+ ]
878
+ },
879
+ i
880
+ )) }) : void 0
881
+ }
882
+ ),
883
+ toolCalls && toolCalls.map((call, index) => /* @__PURE__ */ jsx3(
884
+ StepItem,
885
+ {
886
+ icon: getToolIcon(call.name),
887
+ title: `${getToolDisplayName(call.name)}${call.status === "running" ? "..." : ""}`,
888
+ status: call.status === "running" ? "running" : call.status === "error" ? "error" : "completed",
889
+ detail: /* @__PURE__ */ jsxs3("div", { children: [
890
+ call.args && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: call.result ? 8 : 0 }, children: [
891
+ /* @__PURE__ */ jsx3("div", { style: { fontSize: 10, color: "var(--chat-text-muted)", marginBottom: 4 }, children: "\u53C2\u6570" }),
892
+ /* @__PURE__ */ jsx3("pre", { style: { margin: 0 }, children: formatToolArgs(call.args) })
893
+ ] }),
894
+ call.result && /* @__PURE__ */ jsxs3("div", { children: [
895
+ /* @__PURE__ */ jsx3("div", { style: { fontSize: 10, color: "var(--chat-text-muted)", marginBottom: 4 }, children: "\u7ED3\u679C" }),
896
+ /* @__PURE__ */ jsx3("pre", { style: { margin: 0, maxHeight: 160, overflow: "auto" }, children: call.result })
897
+ ] })
898
+ ] })
899
+ },
900
+ `tool-${index}`
901
+ ))
902
+ ] });
903
+ };
904
+
905
+ // src/components/ChatInput.tsx
906
+ import { useState as useState4, useRef as useRef3, useCallback as useCallback3, useEffect as useEffect2 } from "react";
907
+ import {
908
+ X as X2,
909
+ ChevronDown as ChevronDown2,
910
+ Check,
911
+ Globe as Globe2,
912
+ Sparkles as Sparkles2,
913
+ ImageIcon as ImageIcon2,
914
+ Square,
915
+ ArrowUp,
916
+ Zap,
917
+ MessageCircle,
918
+ AtSign,
919
+ Mic
920
+ } from "lucide-react";
921
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
922
+ var MODES = [
923
+ { value: "agent", label: "Agent", Icon: Zap },
924
+ { value: "ask", label: "Ask", Icon: MessageCircle }
925
+ ];
926
+ var ChatInput = ({
927
+ variant = "input",
928
+ value = "",
929
+ selectedImages = [],
930
+ isLoading = false,
931
+ mode = "agent",
932
+ model = "",
933
+ models = DEFAULT_MODELS,
934
+ webSearchEnabled = false,
935
+ thinkingEnabled = false,
936
+ onSend,
937
+ onRemoveImage,
938
+ onCancel,
939
+ onUploadImage,
940
+ onAtContext,
941
+ onModeChange,
942
+ onModelChange,
943
+ onWebSearchChange,
944
+ onThinkingChange
945
+ }) => {
946
+ const isMessageVariant = variant === "message";
947
+ const [inputText, setInputText] = useState4(value);
948
+ const [isFocused, setIsFocused] = useState4(false);
949
+ const [modeMenuOpen, setModeMenuOpen] = useState4(false);
950
+ const [modelMenuOpen, setModelMenuOpen] = useState4(false);
951
+ const inputRef = useRef3(null);
952
+ const containerRef = useRef3(null);
953
+ useEffect2(() => {
954
+ setInputText(value);
955
+ }, [value]);
956
+ const currentMode = MODES.find((m) => m.value === mode) || MODES[0];
957
+ const currentModel = models.find((m) => m.model === model);
958
+ const showToolbar = !isMessageVariant || isFocused;
959
+ const selectedPreview = selectedImages.slice(0, 3);
960
+ const placeholder = selectedImages.length > 0 ? "\u63CF\u8FF0\u4F60\u60F3\u8981\u7684\u6548\u679C..." : mode === "ask" ? "\u6709\u4EC0\u4E48\u95EE\u9898\u60F3\u95EE\u6211\uFF1F" : "\u63CF\u8FF0\u4EFB\u52A1\uFF0C@ \u6DFB\u52A0\u4E0A\u4E0B\u6587";
961
+ const adjustTextareaHeight = useCallback3(() => {
962
+ if (inputRef.current) {
963
+ inputRef.current.style.height = "auto";
964
+ const scrollHeight = inputRef.current.scrollHeight;
965
+ inputRef.current.style.height = `${Math.min(scrollHeight, 150)}px`;
966
+ }
967
+ }, []);
968
+ const handleSendOrCancel = useCallback3(() => {
969
+ if (isLoading) {
970
+ onCancel?.();
971
+ return;
972
+ }
973
+ const text = inputText.trim();
974
+ if (!text) return;
975
+ onSend?.(text);
976
+ if (!isMessageVariant) {
977
+ setInputText("");
978
+ if (inputRef.current) {
979
+ inputRef.current.style.height = "auto";
980
+ }
981
+ inputRef.current?.focus();
982
+ } else {
983
+ setIsFocused(false);
984
+ }
985
+ }, [isLoading, inputText, onSend, onCancel, isMessageVariant]);
986
+ const handleKeydown = useCallback3(
987
+ (event) => {
988
+ if (event.key === "Enter" && !event.shiftKey) {
989
+ event.preventDefault();
990
+ handleSendOrCancel();
991
+ } else {
992
+ setTimeout(adjustTextareaHeight, 0);
993
+ }
994
+ },
995
+ [handleSendOrCancel, adjustTextareaHeight]
996
+ );
997
+ const selectMode = useCallback3(
998
+ (value2) => {
999
+ onModeChange?.(value2);
1000
+ setModeMenuOpen(false);
1001
+ },
1002
+ [onModeChange]
1003
+ );
1004
+ const selectModel = useCallback3(
1005
+ (m) => {
1006
+ onModelChange?.(m.model);
1007
+ setModelMenuOpen(false);
1008
+ },
1009
+ [onModelChange]
1010
+ );
1011
+ const getImageUrl = (path) => {
1012
+ if (path.startsWith("app://") || path.startsWith("file://") || path.startsWith("data:") || path.startsWith("http")) {
1013
+ return path;
1014
+ }
1015
+ if (path.match(/^[A-Z]:\\/i)) {
1016
+ return `app://file${encodeURIComponent(path.replace(/\\/g, "/"))}`;
1017
+ }
1018
+ return `app://file${encodeURIComponent(path)}`;
1019
+ };
1020
+ useEffect2(() => {
1021
+ const handleClickOutside = (event) => {
1022
+ const target = event.target;
1023
+ if (!target.closest(".selector")) {
1024
+ setModeMenuOpen(false);
1025
+ setModelMenuOpen(false);
1026
+ }
1027
+ if (isMessageVariant && containerRef.current && !containerRef.current.contains(target)) {
1028
+ setIsFocused(false);
1029
+ }
1030
+ };
1031
+ document.addEventListener("click", handleClickOutside);
1032
+ return () => document.removeEventListener("click", handleClickOutside);
1033
+ }, [isMessageVariant]);
1034
+ const CurrentModeIcon = currentMode.Icon;
1035
+ return /* @__PURE__ */ jsx4("div", { className: `chat-input${isMessageVariant ? " message-variant" : ""}`, children: /* @__PURE__ */ jsxs4(
1036
+ "div",
1037
+ {
1038
+ ref: containerRef,
1039
+ className: `input-container${isFocused ? " focused" : ""}`,
1040
+ children: [
1041
+ selectedImages.length > 0 && /* @__PURE__ */ jsx4("div", { className: "attachment-preview", children: /* @__PURE__ */ jsxs4("div", { className: "preview-images", children: [
1042
+ selectedPreview.map((img, index) => /* @__PURE__ */ jsxs4("div", { className: "preview-item", children: [
1043
+ /* @__PURE__ */ jsx4(
1044
+ "img",
1045
+ {
1046
+ src: getImageUrl(img),
1047
+ className: "preview-thumb",
1048
+ alt: `\u9644\u4EF6 ${index + 1}`,
1049
+ onError: (e) => {
1050
+ e.target.style.display = "none";
1051
+ }
1052
+ }
1053
+ ),
1054
+ !isMessageVariant && /* @__PURE__ */ jsx4(
1055
+ "button",
1056
+ {
1057
+ className: "remove-btn",
1058
+ title: `\u79FB\u9664\u56FE\u7247 ${index + 1}`,
1059
+ onClick: () => onRemoveImage?.(index),
1060
+ children: /* @__PURE__ */ jsx4(X2, { size: 10 })
1061
+ }
1062
+ )
1063
+ ] }, `${img}-${index}`)),
1064
+ selectedImages.length > 3 && /* @__PURE__ */ jsxs4("div", { className: "preview-more", children: [
1065
+ "+",
1066
+ selectedImages.length - 3
1067
+ ] })
1068
+ ] }) }),
1069
+ /* @__PURE__ */ jsx4("div", { className: "input-field-wrapper", children: /* @__PURE__ */ jsx4(
1070
+ "textarea",
1071
+ {
1072
+ ref: inputRef,
1073
+ value: inputText,
1074
+ onChange: (e) => setInputText(e.target.value),
1075
+ onKeyDown: handleKeydown,
1076
+ onInput: adjustTextareaHeight,
1077
+ onFocus: () => setIsFocused(true),
1078
+ placeholder,
1079
+ rows: 1,
1080
+ className: "input-field"
1081
+ }
1082
+ ) }),
1083
+ showToolbar && /* @__PURE__ */ jsxs4("div", { className: "input-controls", children: [
1084
+ /* @__PURE__ */ jsxs4("div", { className: "input-left", children: [
1085
+ /* @__PURE__ */ jsxs4(
1086
+ "div",
1087
+ {
1088
+ className: "selector mode-selector",
1089
+ onClick: (e) => {
1090
+ e.stopPropagation();
1091
+ setModeMenuOpen(!modeMenuOpen);
1092
+ setModelMenuOpen(false);
1093
+ },
1094
+ children: [
1095
+ /* @__PURE__ */ jsx4(CurrentModeIcon, { size: 12 }),
1096
+ /* @__PURE__ */ jsx4("span", { children: currentMode.label }),
1097
+ /* @__PURE__ */ jsx4(ChevronDown2, { size: 10, className: "chevron" }),
1098
+ modeMenuOpen && /* @__PURE__ */ jsx4("div", { className: "dropdown-menu", onClick: (e) => e.stopPropagation(), children: MODES.map((m) => /* @__PURE__ */ jsxs4(
1099
+ "button",
1100
+ {
1101
+ className: `dropdown-item${mode === m.value ? " active" : ""}`,
1102
+ onClick: () => selectMode(m.value),
1103
+ children: [
1104
+ /* @__PURE__ */ jsx4(m.Icon, { size: 14 }),
1105
+ /* @__PURE__ */ jsx4("span", { children: m.label }),
1106
+ mode === m.value && /* @__PURE__ */ jsx4(Check, { size: 14, className: "check-icon" })
1107
+ ]
1108
+ },
1109
+ m.value
1110
+ )) })
1111
+ ]
1112
+ }
1113
+ ),
1114
+ /* @__PURE__ */ jsxs4(
1115
+ "div",
1116
+ {
1117
+ className: "selector model-selector",
1118
+ onClick: (e) => {
1119
+ e.stopPropagation();
1120
+ setModelMenuOpen(!modelMenuOpen);
1121
+ setModeMenuOpen(false);
1122
+ },
1123
+ children: [
1124
+ /* @__PURE__ */ jsx4("span", { children: currentModel?.displayName || "Auto" }),
1125
+ /* @__PURE__ */ jsx4(ChevronDown2, { size: 10, className: "chevron" }),
1126
+ modelMenuOpen && /* @__PURE__ */ jsx4("div", { className: "dropdown-menu", onClick: (e) => e.stopPropagation(), children: models.map((m) => /* @__PURE__ */ jsxs4(
1127
+ "button",
1128
+ {
1129
+ className: `dropdown-item${model === m.model ? " active" : ""}`,
1130
+ onClick: () => selectModel(m),
1131
+ children: [
1132
+ /* @__PURE__ */ jsx4("span", { children: m.displayName }),
1133
+ model === m.model && /* @__PURE__ */ jsx4(Check, { size: 14, className: "check-icon" })
1134
+ ]
1135
+ },
1136
+ m.model
1137
+ )) })
1138
+ ]
1139
+ }
1140
+ )
1141
+ ] }),
1142
+ /* @__PURE__ */ jsxs4("div", { className: "input-right", children: [
1143
+ /* @__PURE__ */ jsx4("button", { className: "icon-btn", title: "\u63D0\u53CA\u4E0A\u4E0B\u6587 (@)", onClick: onAtContext, children: /* @__PURE__ */ jsx4(AtSign, { size: 14 }) }),
1144
+ /* @__PURE__ */ jsx4(
1145
+ "button",
1146
+ {
1147
+ className: `toggle-btn${thinkingEnabled ? " active" : ""}`,
1148
+ title: "\u6DF1\u5EA6\u601D\u8003",
1149
+ onClick: () => onThinkingChange?.(!thinkingEnabled),
1150
+ children: /* @__PURE__ */ jsx4(Sparkles2, { size: 14 })
1151
+ }
1152
+ ),
1153
+ /* @__PURE__ */ jsx4(
1154
+ "button",
1155
+ {
1156
+ className: `toggle-btn${webSearchEnabled ? " active" : ""}`,
1157
+ title: "\u8054\u7F51\u641C\u7D22",
1158
+ onClick: () => onWebSearchChange?.(!webSearchEnabled),
1159
+ children: /* @__PURE__ */ jsx4(Globe2, { size: 14 })
1160
+ }
1161
+ ),
1162
+ /* @__PURE__ */ jsx4("button", { className: "icon-btn", title: "\u4E0A\u4F20\u56FE\u7247", onClick: onUploadImage, children: /* @__PURE__ */ jsx4(ImageIcon2, { size: 14 }) }),
1163
+ inputText.trim() || isLoading ? /* @__PURE__ */ jsx4(
1164
+ "button",
1165
+ {
1166
+ className: `send-btn${isLoading ? " loading" : ""}`,
1167
+ title: isLoading ? "\u505C\u6B62" : isMessageVariant ? "\u91CD\u65B0\u53D1\u9001" : "\u53D1\u9001",
1168
+ onClick: handleSendOrCancel,
1169
+ children: isLoading ? /* @__PURE__ */ jsx4(Square, { size: 14 }) : /* @__PURE__ */ jsx4(ArrowUp, { size: 14 })
1170
+ }
1171
+ ) : /* @__PURE__ */ jsx4("button", { className: "icon-btn", title: "\u8BED\u97F3\u8F93\u5165", children: /* @__PURE__ */ jsx4(Mic, { size: 14 }) })
1172
+ ] })
1173
+ ] })
1174
+ ]
1175
+ }
1176
+ ) });
1177
+ };
1178
+
1179
+ // src/components/chat/messages/MessageBubble.tsx
1180
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1181
+ function defaultRenderMarkdown(content) {
1182
+ const parts = content.split(/(```[\s\S]*?```)/g);
1183
+ return parts.map((part, i) => {
1184
+ if (part.startsWith("```") && part.endsWith("```")) {
1185
+ const code = part.slice(3, -3);
1186
+ const firstLine = code.indexOf("\n");
1187
+ const lang = firstLine > 0 ? code.slice(0, firstLine).trim() : "";
1188
+ const codeContent = firstLine > 0 ? code.slice(firstLine + 1) : code;
1189
+ return /* @__PURE__ */ jsx5("pre", { children: /* @__PURE__ */ jsx5("code", { children: codeContent }) }, i);
1190
+ }
1191
+ const inlineParts = part.split(/(`[^`]+`)/g);
1192
+ return /* @__PURE__ */ jsx5("span", { children: inlineParts.map((p, j) => {
1193
+ if (p.startsWith("`") && p.endsWith("`")) {
1194
+ return /* @__PURE__ */ jsx5("code", { children: p.slice(1, -1) }, j);
1195
+ }
1196
+ return p;
1197
+ }) }, i);
1198
+ });
1199
+ }
1200
+ var MessageBubble = ({
1201
+ role,
1202
+ content,
1203
+ images,
1204
+ thinking,
1205
+ thinkingComplete = true,
1206
+ thinkingDuration,
1207
+ searchResults,
1208
+ searching,
1209
+ toolCalls,
1210
+ copied,
1211
+ loading,
1212
+ onCopy,
1213
+ onRegenerate,
1214
+ onSend,
1215
+ renderMarkdown = defaultRenderMarkdown
1216
+ }) => {
1217
+ const isUser = role === "user";
1218
+ return /* @__PURE__ */ jsx5("div", { className: "message-bubble", children: isUser ? /* @__PURE__ */ jsx5(
1219
+ ChatInput,
1220
+ {
1221
+ variant: "message",
1222
+ value: content,
1223
+ selectedImages: images,
1224
+ onSend
1225
+ }
1226
+ ) : (
1227
+ /* AI 消息 */
1228
+ /* @__PURE__ */ jsxs5("div", { className: "assistant-message", children: [
1229
+ /* @__PURE__ */ jsx5(
1230
+ ExecutionSteps,
1231
+ {
1232
+ loading,
1233
+ hasContent: !!content,
1234
+ thinking,
1235
+ thinkingComplete,
1236
+ thinkingDuration,
1237
+ searching,
1238
+ searchResults,
1239
+ toolCalls
1240
+ }
1241
+ ),
1242
+ content && /* @__PURE__ */ jsx5("div", { className: "message-content", children: renderMarkdown(content) }),
1243
+ content && !loading && /* @__PURE__ */ jsxs5("div", { className: "message-actions", children: [
1244
+ /* @__PURE__ */ jsx5("button", { className: `action-btn${copied ? " copied" : ""}`, onClick: onCopy, title: "\u590D\u5236", children: copied ? /* @__PURE__ */ jsx5(Check2, { size: 14 }) : /* @__PURE__ */ jsx5(Copy, { size: 14 }) }),
1245
+ /* @__PURE__ */ jsx5("button", { className: "action-btn", onClick: onRegenerate, title: "\u91CD\u65B0\u751F\u6210", children: /* @__PURE__ */ jsx5(RefreshCw, { size: 14 }) })
1246
+ ] })
1247
+ ] })
1248
+ ) });
1249
+ };
1250
+
1251
+ // src/components/ChatPanel.tsx
1252
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1253
+ var ChatPanel = ({
1254
+ adapter,
1255
+ workingDir,
1256
+ defaultModel = "anthropic/claude-opus-4.5",
1257
+ defaultMode = "agent",
1258
+ models = DEFAULT_MODELS,
1259
+ hideHeader = false,
1260
+ onClose,
1261
+ className = "",
1262
+ renderMarkdown
1263
+ }) => {
1264
+ const messagesRef = useRef4(null);
1265
+ const {
1266
+ sessions,
1267
+ currentSessionId,
1268
+ messages,
1269
+ isLoading,
1270
+ mode,
1271
+ model,
1272
+ webSearch,
1273
+ thinking,
1274
+ loadSessions,
1275
+ switchSession,
1276
+ createNewSession,
1277
+ deleteSession,
1278
+ sendMessage,
1279
+ cancelRequest,
1280
+ copyMessage,
1281
+ regenerateMessage,
1282
+ setMode,
1283
+ setModel,
1284
+ setWebSearch,
1285
+ setThinking,
1286
+ setWorkingDirectory
1287
+ } = useChat({
1288
+ adapter,
1289
+ defaultModel,
1290
+ defaultMode
1291
+ });
1292
+ useEffect3(() => {
1293
+ loadSessions();
1294
+ }, [loadSessions]);
1295
+ useEffect3(() => {
1296
+ if (workingDir) {
1297
+ setWorkingDirectory(workingDir);
1298
+ }
1299
+ }, [workingDir, setWorkingDirectory]);
1300
+ const scrollToBottom = useCallback4(() => {
1301
+ if (messagesRef.current) {
1302
+ messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
1303
+ }
1304
+ }, []);
1305
+ useEffect3(() => {
1306
+ scrollToBottom();
1307
+ }, [messages, scrollToBottom]);
1308
+ const handleSend = useCallback4(
1309
+ (text) => {
1310
+ sendMessage(text);
1311
+ },
1312
+ [sendMessage]
1313
+ );
1314
+ const handleQuickAction = useCallback4(
1315
+ (text) => {
1316
+ sendMessage(text);
1317
+ },
1318
+ [sendMessage]
1319
+ );
1320
+ const handleResend = useCallback4(
1321
+ (index, text) => {
1322
+ sendMessage(text);
1323
+ },
1324
+ [sendMessage]
1325
+ );
1326
+ const handleClose = useCallback4(() => {
1327
+ onClose?.();
1328
+ }, [onClose]);
1329
+ const handleClearAll = useCallback4(() => {
1330
+ console.log("\u6E05\u7A7A\u6240\u6709\u5BF9\u8BDD");
1331
+ }, []);
1332
+ const handleCloseOthers = useCallback4(() => {
1333
+ console.log("\u5173\u95ED\u5176\u4ED6\u5BF9\u8BDD");
1334
+ }, []);
1335
+ const handleExport = useCallback4(() => {
1336
+ console.log("\u5BFC\u51FA\u5BF9\u8BDD");
1337
+ }, []);
1338
+ const handleCopyId = useCallback4(() => {
1339
+ if (currentSessionId) {
1340
+ navigator.clipboard.writeText(currentSessionId);
1341
+ console.log("\u5DF2\u590D\u5236\u8BF7\u6C42 ID:", currentSessionId);
1342
+ }
1343
+ }, [currentSessionId]);
1344
+ const handleFeedback = useCallback4(() => {
1345
+ console.log("\u53CD\u9988");
1346
+ }, []);
1347
+ const handleSettings = useCallback4(() => {
1348
+ console.log("Agent \u8BBE\u7F6E");
1349
+ }, []);
1350
+ return /* @__PURE__ */ jsxs6("div", { className: `chat-panel ${className}`.trim(), children: [
1351
+ !hideHeader && /* @__PURE__ */ jsx6(
1352
+ ChatHeader,
1353
+ {
1354
+ sessions,
1355
+ currentSessionId,
1356
+ showClose: !!onClose,
1357
+ onNewSession: createNewSession,
1358
+ onSwitchSession: switchSession,
1359
+ onDeleteSession: deleteSession,
1360
+ onClose: handleClose,
1361
+ onClearAll: handleClearAll,
1362
+ onCloseOthers: handleCloseOthers,
1363
+ onExport: handleExport,
1364
+ onCopyId: handleCopyId,
1365
+ onFeedback: handleFeedback,
1366
+ onSettings: handleSettings
1367
+ }
1368
+ ),
1369
+ /* @__PURE__ */ jsx6("div", { ref: messagesRef, className: "messages-container", children: messages.length === 0 ? /* @__PURE__ */ jsx6(WelcomeMessage, { onQuickAction: handleQuickAction }) : messages.map((msg, index) => /* @__PURE__ */ jsx6(
1370
+ MessageBubble,
1371
+ {
1372
+ role: msg.role,
1373
+ content: msg.content,
1374
+ images: msg.images,
1375
+ thinking: msg.thinking,
1376
+ thinkingComplete: msg.thinkingComplete,
1377
+ searchResults: msg.searchResults,
1378
+ searching: msg.searching,
1379
+ toolCalls: msg.toolCalls,
1380
+ copied: msg.copied,
1381
+ loading: msg.loading,
1382
+ onCopy: () => copyMessage(msg.id),
1383
+ onRegenerate: () => regenerateMessage(index),
1384
+ onSend: (text) => handleResend(index, text),
1385
+ renderMarkdown
1386
+ },
1387
+ msg.id
1388
+ )) }),
1389
+ /* @__PURE__ */ jsx6(
1390
+ ChatInput,
1391
+ {
1392
+ selectedImages: [],
1393
+ isLoading,
1394
+ mode,
1395
+ model,
1396
+ models,
1397
+ webSearchEnabled: webSearch,
1398
+ thinkingEnabled: thinking,
1399
+ onSend: handleSend,
1400
+ onCancel: cancelRequest,
1401
+ onModeChange: setMode,
1402
+ onModelChange: setModel,
1403
+ onWebSearchChange: setWebSearch,
1404
+ onThinkingChange: setThinking
1405
+ }
1406
+ )
1407
+ ] });
1408
+ };
1409
+ export {
1410
+ ChatHeader,
1411
+ ChatInput,
1412
+ ChatPanel,
1413
+ DEFAULT_MODELS,
1414
+ ExecutionSteps,
1415
+ FileType,
1416
+ MessageBubble,
1417
+ WelcomeMessage,
1418
+ createNullAdapter,
1419
+ useChat
1420
+ };
1421
+ //# sourceMappingURL=index.js.map