@huyooo/ai-chat-frontend-react 0.1.2 → 0.1.3
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.d.ts +76 -138
- package/dist/index.js +36 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/adapter.ts +38 -72
- package/src/components/ChatPanel.tsx +1 -2
- package/src/hooks/useChat.ts +5 -6
- package/src/index.ts +21 -14
- package/src/types/index.ts +22 -44
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,75 @@
|
|
|
1
|
+
import { ChatMode as ChatMode$1, ThinkingMode, ChatAdapter, ModelConfig as ModelConfig$1, SessionRecord as SessionRecord$1 } from '@huyooo/ai-chat-bridge-electron/renderer';
|
|
2
|
+
export { ChatAdapter, ChatMode, ChatOptions, ChatProgress, ChatProgressType, MessageRecord, ModelConfig, ModelProvider, SessionRecord, ThinkingMode, createElectronAdapter } from '@huyooo/ai-chat-bridge-electron/renderer';
|
|
1
3
|
import { FC, ReactNode } from 'react';
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Chat Adapter 辅助类型和工具
|
|
7
|
+
* 核心 ChatAdapter 接口从 bridge-electron 导入
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** 思考数据 */
|
|
11
|
+
interface ThinkingData {
|
|
12
|
+
content: string;
|
|
13
|
+
isComplete: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** 工具调用数据 */
|
|
16
|
+
interface ToolCallData {
|
|
17
|
+
name: string;
|
|
18
|
+
args: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** 工具结果数据 */
|
|
21
|
+
interface ToolResultData {
|
|
22
|
+
name: string;
|
|
23
|
+
result: string;
|
|
24
|
+
}
|
|
25
|
+
/** 图片数据 */
|
|
26
|
+
interface ImageData {
|
|
27
|
+
base64: string;
|
|
28
|
+
mimeType: string;
|
|
29
|
+
}
|
|
30
|
+
/** 发送消息选项 */
|
|
31
|
+
interface SendMessageOptions {
|
|
32
|
+
mode: ChatMode$1;
|
|
33
|
+
model: string;
|
|
34
|
+
enableWebSearch: boolean;
|
|
35
|
+
thinkingMode: ThinkingMode;
|
|
36
|
+
}
|
|
37
|
+
/** 创建会话选项 */
|
|
38
|
+
interface CreateSessionOptions {
|
|
39
|
+
title: string;
|
|
40
|
+
model: string;
|
|
41
|
+
mode: ChatMode$1;
|
|
42
|
+
}
|
|
43
|
+
/** 更新会话选项 */
|
|
44
|
+
interface UpdateSessionOptions {
|
|
45
|
+
title?: string;
|
|
46
|
+
model?: string;
|
|
47
|
+
mode?: ChatMode$1;
|
|
48
|
+
}
|
|
49
|
+
/** 保存消息选项 */
|
|
50
|
+
interface SaveMessageOptions {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
role: 'user' | 'assistant';
|
|
53
|
+
content: string;
|
|
54
|
+
thinking?: string;
|
|
55
|
+
toolCalls?: string;
|
|
56
|
+
searchResults?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 创建空 Adapter(用于测试或无后端场景)
|
|
60
|
+
* 返回一个最小实现的 ChatAdapter
|
|
61
|
+
*/
|
|
62
|
+
declare function createNullAdapter(): ChatAdapter;
|
|
63
|
+
|
|
3
64
|
/**
|
|
4
65
|
* AI Chat 前端类型定义
|
|
5
|
-
*
|
|
66
|
+
* 核心类型(ChatAdapter, SessionRecord, MessageRecord 等)从 bridge-electron 导出
|
|
67
|
+
* 这里只定义前端特有的类型
|
|
6
68
|
*/
|
|
69
|
+
|
|
70
|
+
type ChatMode = ChatMode$1;
|
|
71
|
+
type ModelConfig = ModelConfig$1;
|
|
72
|
+
type SessionRecord = SessionRecord$1;
|
|
7
73
|
/** 搜索结果 */
|
|
8
74
|
interface SearchResult {
|
|
9
75
|
title: string;
|
|
@@ -17,22 +83,7 @@ interface ToolCall {
|
|
|
17
83
|
result?: string;
|
|
18
84
|
status: 'running' | 'success' | 'error';
|
|
19
85
|
}
|
|
20
|
-
/**
|
|
21
|
-
type ModelProvider = 'openrouter' | 'doubao' | 'deepseek' | 'qwen' | 'gemini' | 'ark';
|
|
22
|
-
/** 模型配置 */
|
|
23
|
-
interface ModelConfig {
|
|
24
|
-
provider: ModelProvider;
|
|
25
|
-
model: string;
|
|
26
|
-
displayName: string;
|
|
27
|
-
supportsTools: boolean;
|
|
28
|
-
supportsWebSearch: boolean;
|
|
29
|
-
supportedThinkingModes: ThinkingMode[];
|
|
30
|
-
}
|
|
31
|
-
/** 思考模式 */
|
|
32
|
-
type ThinkingMode = 'enabled' | 'disabled';
|
|
33
|
-
/** 聊天模式 */
|
|
34
|
-
type ChatMode = 'agent' | 'ask';
|
|
35
|
-
/** 聊天消息 */
|
|
86
|
+
/** 聊天消息(前端显示用) */
|
|
36
87
|
interface ChatMessage {
|
|
37
88
|
id: string;
|
|
38
89
|
role: 'user' | 'assistant';
|
|
@@ -47,28 +98,8 @@ interface ChatMessage {
|
|
|
47
98
|
loading?: boolean;
|
|
48
99
|
timestamp?: Date;
|
|
49
100
|
}
|
|
50
|
-
/** 会话记录 */
|
|
51
|
-
interface SessionRecord {
|
|
52
|
-
id: string;
|
|
53
|
-
title: string;
|
|
54
|
-
model: string;
|
|
55
|
-
mode: ChatMode;
|
|
56
|
-
createdAt: Date;
|
|
57
|
-
updatedAt: Date;
|
|
58
|
-
}
|
|
59
|
-
/** 消息记录 */
|
|
60
|
-
interface MessageRecord {
|
|
61
|
-
id: string;
|
|
62
|
-
sessionId: string;
|
|
63
|
-
role: 'user' | 'assistant';
|
|
64
|
-
content: string;
|
|
65
|
-
thinking?: string;
|
|
66
|
-
toolCalls?: string;
|
|
67
|
-
searchResults?: string;
|
|
68
|
-
timestamp: Date;
|
|
69
|
-
}
|
|
70
101
|
/** 默认模型列表 */
|
|
71
|
-
declare const DEFAULT_MODELS: ModelConfig[];
|
|
102
|
+
declare const DEFAULT_MODELS: ModelConfig$1[];
|
|
72
103
|
/** @deprecated 使用 SessionRecord */
|
|
73
104
|
interface ChatSession {
|
|
74
105
|
id: string;
|
|
@@ -108,99 +139,6 @@ interface DiffStat {
|
|
|
108
139
|
type: 'modified' | 'added' | 'deleted';
|
|
109
140
|
}
|
|
110
141
|
|
|
111
|
-
/**
|
|
112
|
-
* Chat Adapter 接口定义
|
|
113
|
-
* 解耦前端组件与后端通信方式
|
|
114
|
-
*
|
|
115
|
-
* 与 Vue 版本保持一致
|
|
116
|
-
*/
|
|
117
|
-
|
|
118
|
-
/** 聊天进度类型 */
|
|
119
|
-
type ChatProgressType = 'thinking' | 'search_start' | 'search_result' | 'tool_call' | 'tool_result' | 'text_delta' | 'text' | 'done' | 'error';
|
|
120
|
-
/** 思考数据 */
|
|
121
|
-
interface ThinkingData {
|
|
122
|
-
content: string;
|
|
123
|
-
isComplete: boolean;
|
|
124
|
-
}
|
|
125
|
-
/** 工具调用数据 */
|
|
126
|
-
interface ToolCallData {
|
|
127
|
-
name: string;
|
|
128
|
-
args: Record<string, unknown>;
|
|
129
|
-
}
|
|
130
|
-
/** 工具结果数据 */
|
|
131
|
-
interface ToolResultData {
|
|
132
|
-
name: string;
|
|
133
|
-
result: string;
|
|
134
|
-
}
|
|
135
|
-
/** 图片数据 */
|
|
136
|
-
interface ImageData {
|
|
137
|
-
base64: string;
|
|
138
|
-
mimeType: string;
|
|
139
|
-
}
|
|
140
|
-
/** 聊天进度事件 */
|
|
141
|
-
interface ChatProgress {
|
|
142
|
-
type: ChatProgressType;
|
|
143
|
-
data: string | ThinkingData | ToolCallData | ToolResultData | {
|
|
144
|
-
results: SearchResult[];
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/** 发送消息选项 */
|
|
148
|
-
interface SendMessageOptions {
|
|
149
|
-
mode: ChatMode;
|
|
150
|
-
model: string;
|
|
151
|
-
enableWebSearch: boolean;
|
|
152
|
-
thinkingMode: ThinkingMode;
|
|
153
|
-
}
|
|
154
|
-
/** 创建会话选项 */
|
|
155
|
-
interface CreateSessionOptions {
|
|
156
|
-
title: string;
|
|
157
|
-
model: string;
|
|
158
|
-
mode: ChatMode;
|
|
159
|
-
}
|
|
160
|
-
/** 更新会话选项 */
|
|
161
|
-
interface UpdateSessionOptions {
|
|
162
|
-
title?: string;
|
|
163
|
-
model?: string;
|
|
164
|
-
mode?: ChatMode;
|
|
165
|
-
}
|
|
166
|
-
/** 保存消息选项 */
|
|
167
|
-
interface SaveMessageOptions {
|
|
168
|
-
sessionId: string;
|
|
169
|
-
role: 'user' | 'assistant';
|
|
170
|
-
content: string;
|
|
171
|
-
thinking?: string;
|
|
172
|
-
toolCalls?: string;
|
|
173
|
-
searchResults?: string;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Chat Adapter 接口
|
|
177
|
-
* 所有后端通信实现都需要实现此接口
|
|
178
|
-
*/
|
|
179
|
-
interface ChatAdapter {
|
|
180
|
-
/** 获取所有会话 */
|
|
181
|
-
getSessions(): Promise<SessionRecord[]>;
|
|
182
|
-
/** 创建新会话 */
|
|
183
|
-
createSession(options: CreateSessionOptions): Promise<SessionRecord>;
|
|
184
|
-
/** 更新会话 */
|
|
185
|
-
updateSession(sessionId: string, options: UpdateSessionOptions): Promise<void>;
|
|
186
|
-
/** 删除会话 */
|
|
187
|
-
deleteSession(sessionId: string): Promise<void>;
|
|
188
|
-
/** 获取会话消息 */
|
|
189
|
-
getMessages(sessionId: string): Promise<MessageRecord[]>;
|
|
190
|
-
/** 保存消息 */
|
|
191
|
-
saveMessage(options: SaveMessageOptions): Promise<MessageRecord>;
|
|
192
|
-
/** 发送消息并获取流式响应 */
|
|
193
|
-
sendMessage(content: string, options: SendMessageOptions, images?: string[]): AsyncGenerator<ChatProgress, void, unknown>;
|
|
194
|
-
/** 取消当前请求 */
|
|
195
|
-
cancel(): void;
|
|
196
|
-
/** 设置工作目录 */
|
|
197
|
-
setWorkingDir?(dir: string): void;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* 创建空 Adapter(用于测试或无后端场景)
|
|
201
|
-
*/
|
|
202
|
-
declare function createNullAdapter(): ChatAdapter;
|
|
203
|
-
|
|
204
142
|
/**
|
|
205
143
|
* useChat Hook
|
|
206
144
|
* 管理聊天状态和与后端的通信
|
|
@@ -215,17 +153,17 @@ interface UseChatOptions {
|
|
|
215
153
|
/** 默认模型 */
|
|
216
154
|
defaultModel?: string;
|
|
217
155
|
/** 默认模式 */
|
|
218
|
-
defaultMode?: ChatMode;
|
|
156
|
+
defaultMode?: ChatMode$1;
|
|
219
157
|
}
|
|
220
158
|
/**
|
|
221
159
|
* 聊天状态管理 Hook
|
|
222
160
|
*/
|
|
223
161
|
declare function useChat(options?: UseChatOptions): {
|
|
224
|
-
sessions: SessionRecord[];
|
|
162
|
+
sessions: SessionRecord$1[];
|
|
225
163
|
currentSessionId: string | null;
|
|
226
164
|
messages: ChatMessage[];
|
|
227
165
|
isLoading: boolean;
|
|
228
|
-
mode: ChatMode;
|
|
166
|
+
mode: ChatMode$1;
|
|
229
167
|
model: string;
|
|
230
168
|
webSearch: boolean;
|
|
231
169
|
thinking: boolean;
|
|
@@ -238,7 +176,7 @@ declare function useChat(options?: UseChatOptions): {
|
|
|
238
176
|
cancelRequest: () => void;
|
|
239
177
|
copyMessage: (messageId: string) => Promise<void>;
|
|
240
178
|
regenerateMessage: (messageIndex: number) => void;
|
|
241
|
-
setMode: (value: ChatMode) => void;
|
|
179
|
+
setMode: (value: ChatMode$1) => void;
|
|
242
180
|
setModel: (value: string) => void;
|
|
243
181
|
setWebSearch: (value: boolean) => void;
|
|
244
182
|
setThinking: (value: boolean) => void;
|
|
@@ -258,9 +196,9 @@ interface ChatPanelProps {
|
|
|
258
196
|
/** 默认模型 */
|
|
259
197
|
defaultModel?: string;
|
|
260
198
|
/** 默认模式 */
|
|
261
|
-
defaultMode?: ChatMode;
|
|
199
|
+
defaultMode?: ChatMode$1;
|
|
262
200
|
/** 可用模型列表 */
|
|
263
|
-
models?: ModelConfig[];
|
|
201
|
+
models?: ModelConfig$1[];
|
|
264
202
|
/** 隐藏标题栏 */
|
|
265
203
|
hideHeader?: boolean;
|
|
266
204
|
/** 关闭回调(有此属性时显示关闭按钮) */
|
|
@@ -398,4 +336,4 @@ interface ExecutionStepsProps {
|
|
|
398
336
|
}
|
|
399
337
|
declare const ExecutionSteps: FC<ExecutionStepsProps>;
|
|
400
338
|
|
|
401
|
-
export { type AiModel,
|
|
339
|
+
export { type AiModel, ChatHeader, ChatInput, type ChatMessage, ChatPanel, type ChatSession, type CreateSessionOptions, DEFAULT_MODELS, type DiffStat, ExecutionSteps, FileType, type ImageData, type MediaOperation, MessageBubble, type SaveMessageOptions, type SearchResult, type SendMessageOptions, type ThinkingData, type ToolCall, type ToolCallData, type ToolResultData, type UpdateSessionOptions, type UseChatOptions, WelcomeMessage, createNullAdapter, useChat };
|
package/dist/index.js
CHANGED
|
@@ -1,38 +1,62 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createElectronAdapter } from "@huyooo/ai-chat-bridge-electron/renderer";
|
|
3
|
+
|
|
1
4
|
// src/adapter.ts
|
|
2
5
|
function createNullAdapter() {
|
|
3
6
|
return {
|
|
7
|
+
async getModels() {
|
|
8
|
+
return [];
|
|
9
|
+
},
|
|
4
10
|
async getSessions() {
|
|
5
11
|
return [];
|
|
6
12
|
},
|
|
7
|
-
async
|
|
13
|
+
async getSession() {
|
|
14
|
+
return null;
|
|
15
|
+
},
|
|
16
|
+
async createSession(params) {
|
|
8
17
|
return {
|
|
9
18
|
id: Date.now().toString(),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
appId: null,
|
|
20
|
+
userId: null,
|
|
21
|
+
title: params?.title || "\u65B0\u5BF9\u8BDD",
|
|
22
|
+
model: params?.model || "",
|
|
23
|
+
mode: params?.mode || "agent",
|
|
13
24
|
createdAt: /* @__PURE__ */ new Date(),
|
|
14
25
|
updatedAt: /* @__PURE__ */ new Date()
|
|
15
26
|
};
|
|
16
27
|
},
|
|
17
28
|
async updateSession() {
|
|
29
|
+
return null;
|
|
18
30
|
},
|
|
19
31
|
async deleteSession() {
|
|
20
32
|
},
|
|
21
33
|
async getMessages() {
|
|
22
34
|
return [];
|
|
23
35
|
},
|
|
24
|
-
async saveMessage(
|
|
36
|
+
async saveMessage(params) {
|
|
25
37
|
return {
|
|
26
38
|
id: Date.now().toString(),
|
|
27
|
-
sessionId:
|
|
28
|
-
role:
|
|
29
|
-
content:
|
|
30
|
-
thinking:
|
|
31
|
-
toolCalls:
|
|
32
|
-
searchResults:
|
|
39
|
+
sessionId: params.sessionId,
|
|
40
|
+
role: params.role,
|
|
41
|
+
content: params.content,
|
|
42
|
+
thinking: params.thinking || null,
|
|
43
|
+
toolCalls: params.toolCalls || null,
|
|
44
|
+
searchResults: params.searchResults || null,
|
|
45
|
+
operationIds: null,
|
|
33
46
|
timestamp: /* @__PURE__ */ new Date()
|
|
34
47
|
};
|
|
35
48
|
},
|
|
49
|
+
async deleteMessagesAfter() {
|
|
50
|
+
},
|
|
51
|
+
async getOperations() {
|
|
52
|
+
return [];
|
|
53
|
+
},
|
|
54
|
+
async getTrashItems() {
|
|
55
|
+
return [];
|
|
56
|
+
},
|
|
57
|
+
async restoreFromTrash() {
|
|
58
|
+
return void 0;
|
|
59
|
+
},
|
|
36
60
|
async *sendMessage() {
|
|
37
61
|
yield { type: "text", data: "\u65E0\u53EF\u7528\u7684 Adapter" };
|
|
38
62
|
yield { type: "done", data: "" };
|
|
@@ -1415,6 +1439,7 @@ export {
|
|
|
1415
1439
|
FileType,
|
|
1416
1440
|
MessageBubble,
|
|
1417
1441
|
WelcomeMessage,
|
|
1442
|
+
createElectronAdapter,
|
|
1418
1443
|
createNullAdapter,
|
|
1419
1444
|
useChat
|
|
1420
1445
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter.ts","../src/hooks/useChat.ts","../src/components/ChatPanel.tsx","../src/types/index.ts","../src/components/chat/ui/ChatHeader.tsx","../src/components/chat/ui/WelcomeMessage.tsx","../src/components/chat/messages/MessageBubble.tsx","../src/components/chat/messages/ExecutionSteps.tsx","../src/components/ChatInput.tsx"],"sourcesContent":["/**\n * Chat Adapter 接口定义\n * 解耦前端组件与后端通信方式\n * \n * 与 Vue 版本保持一致\n */\n\nimport type { SessionRecord, MessageRecord, ChatMode, ThinkingMode, SearchResult } from './types'\n\n/** 聊天进度类型 */\nexport type ChatProgressType =\n | 'thinking'\n | 'search_start'\n | 'search_result'\n | 'tool_call'\n | 'tool_result'\n | 'text_delta'\n | 'text'\n | 'done'\n | 'error'\n\n/** 思考数据 */\nexport interface ThinkingData {\n content: string\n isComplete: boolean\n}\n\n/** 工具调用数据 */\nexport interface ToolCallData {\n name: string\n args: Record<string, unknown>\n}\n\n/** 工具结果数据 */\nexport interface ToolResultData {\n name: string\n result: string\n}\n\n/** 图片数据 */\nexport interface ImageData {\n base64: string\n mimeType: string\n}\n\n/** 聊天进度事件 */\nexport interface ChatProgress {\n type: ChatProgressType\n data: string | ThinkingData | ToolCallData | ToolResultData | { results: SearchResult[] }\n}\n\n/** 发送消息选项 */\nexport interface SendMessageOptions {\n mode: ChatMode\n model: string\n enableWebSearch: boolean\n thinkingMode: ThinkingMode\n}\n\n/** 创建会话选项 */\nexport interface CreateSessionOptions {\n title: string\n model: string\n mode: ChatMode\n}\n\n/** 更新会话选项 */\nexport interface UpdateSessionOptions {\n title?: string\n model?: string\n mode?: ChatMode\n}\n\n/** 保存消息选项 */\nexport interface SaveMessageOptions {\n sessionId: string\n role: 'user' | 'assistant'\n content: string\n thinking?: string\n toolCalls?: string\n searchResults?: string\n}\n\n/**\n * Chat Adapter 接口\n * 所有后端通信实现都需要实现此接口\n */\nexport interface ChatAdapter {\n /** 获取所有会话 */\n getSessions(): Promise<SessionRecord[]>\n \n /** 创建新会话 */\n createSession(options: CreateSessionOptions): Promise<SessionRecord>\n \n /** 更新会话 */\n updateSession(sessionId: string, options: UpdateSessionOptions): Promise<void>\n \n /** 删除会话 */\n deleteSession(sessionId: string): Promise<void>\n \n /** 获取会话消息 */\n getMessages(sessionId: string): Promise<MessageRecord[]>\n \n /** 保存消息 */\n saveMessage(options: SaveMessageOptions): Promise<MessageRecord>\n \n /** 发送消息并获取流式响应 */\n sendMessage(\n content: string,\n options: SendMessageOptions,\n images?: string[]\n ): AsyncGenerator<ChatProgress, void, unknown>\n \n /** 取消当前请求 */\n cancel(): void\n \n /** 设置工作目录 */\n setWorkingDir?(dir: string): void\n}\n\n/**\n * 创建空 Adapter(用于测试或无后端场景)\n */\nexport function createNullAdapter(): ChatAdapter {\n return {\n async getSessions() {\n return []\n },\n async createSession(options) {\n return {\n id: Date.now().toString(),\n title: options.title,\n model: options.model,\n mode: options.mode,\n createdAt: new Date(),\n updatedAt: new Date(),\n }\n },\n async updateSession() {},\n async deleteSession() {},\n async getMessages() {\n return []\n },\n async saveMessage(options) {\n return {\n id: Date.now().toString(),\n sessionId: options.sessionId,\n role: options.role,\n content: options.content,\n thinking: options.thinking,\n toolCalls: options.toolCalls,\n searchResults: options.searchResults,\n timestamp: new Date(),\n }\n },\n async *sendMessage() {\n yield { type: 'text', data: '无可用的 Adapter' }\n yield { type: 'done', data: '' }\n },\n cancel() {},\n }\n}\n","/**\n * useChat Hook\n * 管理聊天状态和与后端的通信\n * 使用 Adapter 模式解耦后端通信\n * \n * 与 Vue 版本 useChat composable 保持一致\n */\n\nimport { useState, useCallback, useRef } from 'react'\nimport type { ChatAdapter, ChatProgress } from '../adapter'\nimport { createNullAdapter } from '../adapter'\nimport type {\n ChatMessage,\n ChatMode,\n SessionRecord,\n MessageRecord,\n SearchResult,\n ToolCall,\n} from '../types'\n\n/** 生成唯一 ID */\nfunction generateId(): string {\n return Date.now().toString(36) + Math.random().toString(36).substr(2)\n}\n\n/** 转换存储的消息为显示格式 */\nfunction convertToMessage(record: MessageRecord): ChatMessage {\n return {\n id: record.id,\n role: record.role,\n content: record.content,\n thinking: record.thinking || undefined,\n thinkingComplete: true,\n toolCalls: record.toolCalls ? JSON.parse(record.toolCalls) : undefined,\n searchResults: record.searchResults ? JSON.parse(record.searchResults) : undefined,\n searching: false,\n timestamp: record.timestamp,\n }\n}\n\nexport interface UseChatOptions {\n /** Adapter 实例 */\n adapter?: ChatAdapter\n /** 默认模型 */\n defaultModel?: string\n /** 默认模式 */\n defaultMode?: ChatMode\n}\n\n/**\n * 聊天状态管理 Hook\n */\nexport function useChat(options: UseChatOptions = {}) {\n const {\n adapter = createNullAdapter(),\n defaultModel = 'anthropic/claude-opus-4.5',\n defaultMode = 'agent',\n } = options\n\n // 会话状态\n const [sessions, setSessions] = useState<SessionRecord[]>([])\n const [currentSessionId, setCurrentSessionId] = useState<string | null>(null)\n const [messages, setMessages] = useState<ChatMessage[]>([])\n\n // 配置状态\n const [mode, setModeState] = useState<ChatMode>(defaultMode)\n const [model, setModelState] = useState(defaultModel)\n const [webSearch, setWebSearchState] = useState(true)\n const [thinking, setThinkingState] = useState(true)\n\n // 加载状态\n const [isLoading, setIsLoading] = useState(false)\n\n // 取消控制器\n const abortControllerRef = useRef<AbortController | null>(null)\n\n // 用于在回调中访问最新状态\n const sessionsRef = useRef(sessions)\n const messagesRef = useRef(messages)\n const currentSessionIdRef = useRef(currentSessionId)\n const modeRef = useRef(mode)\n const modelRef = useRef(model)\n const webSearchRef = useRef(webSearch)\n const thinkingRef = useRef(thinking)\n\n // 同步 ref\n sessionsRef.current = sessions\n messagesRef.current = messages\n currentSessionIdRef.current = currentSessionId\n modeRef.current = mode\n modelRef.current = model\n webSearchRef.current = webSearch\n thinkingRef.current = thinking\n\n /** 加载会话列表 */\n const loadSessions = useCallback(async () => {\n try {\n const list = await adapter.getSessions()\n setSessions(list)\n // 如果有会话且没有当前会话,选择最新的\n if (list.length > 0 && !currentSessionIdRef.current) {\n const firstSession = list[0]\n setCurrentSessionId(firstSession.id)\n const savedMessages = await adapter.getMessages(firstSession.id)\n setMessages(savedMessages.map(convertToMessage))\n setModeState(firstSession.mode)\n setModelState(firstSession.model)\n }\n } catch (error) {\n console.error('加载会话失败:', error)\n }\n }, [adapter])\n\n /** 切换会话 */\n const switchSession = useCallback(async (sessionId: string) => {\n if (currentSessionIdRef.current === sessionId) return\n\n setCurrentSessionId(sessionId)\n\n try {\n const savedMessages = await adapter.getMessages(sessionId)\n setMessages(savedMessages.map(convertToMessage))\n\n // 更新配置\n const session = sessionsRef.current.find((s) => s.id === sessionId)\n if (session) {\n setModeState(session.mode)\n setModelState(session.model)\n }\n } catch (error) {\n console.error('加载消息失败:', error)\n setMessages([])\n }\n }, [adapter])\n\n /** 创建新会话 */\n const createNewSession = useCallback(async () => {\n try {\n const session = await adapter.createSession({\n title: '新对话',\n model: modelRef.current,\n mode: modeRef.current,\n })\n setSessions(prev => [session, ...prev])\n setCurrentSessionId(session.id)\n setMessages([])\n } catch (error) {\n console.error('创建会话失败:', error)\n }\n }, [adapter])\n\n /** 删除会话 */\n const deleteSession = useCallback(async (sessionId: string) => {\n try {\n await adapter.deleteSession(sessionId)\n setSessions(prev => prev.filter((s) => s.id !== sessionId))\n\n // 如果删除的是当前会话,切换到下一个\n if (currentSessionIdRef.current === sessionId) {\n const remaining = sessionsRef.current.filter((s) => s.id !== sessionId)\n if (remaining.length > 0) {\n await switchSession(remaining[0].id)\n } else {\n setCurrentSessionId(null)\n setMessages([])\n }\n }\n } catch (error) {\n console.error('删除会话失败:', error)\n }\n }, [adapter, switchSession])\n\n /** 删除当前会话 */\n const deleteCurrentSession = useCallback(async () => {\n if (currentSessionIdRef.current) {\n await deleteSession(currentSessionIdRef.current)\n }\n }, [deleteSession])\n\n /** 更新消息 */\n const updateMessage = useCallback((index: number, progress: ChatProgress) => {\n setMessages(prev => {\n const newMessages = [...prev]\n const msg = { ...newMessages[index] }\n if (!msg) return prev\n\n switch (progress.type) {\n case 'thinking': {\n const thinkingData = progress.data as { content: string; isComplete: boolean }\n if (thinkingData.content) {\n msg.thinking = (msg.thinking || '') + thinkingData.content\n }\n msg.thinkingComplete = thinkingData.isComplete\n break\n }\n\n case 'search_start':\n msg.searching = true\n break\n\n case 'search_result': {\n msg.searching = false\n const searchData = progress.data as { results: SearchResult[] }\n msg.searchResults = searchData.results || []\n break\n }\n\n case 'tool_call': {\n const toolData = progress.data as { name: string; args: Record<string, unknown> }\n if (!msg.toolCalls) msg.toolCalls = []\n msg.toolCalls = [...msg.toolCalls, {\n name: toolData.name,\n args: toolData.args,\n status: 'running' as const,\n }]\n break\n }\n\n case 'tool_result': {\n const resultData = progress.data as { name: string; result: string }\n if (msg.toolCalls) {\n msg.toolCalls = msg.toolCalls.map((t: ToolCall) => {\n if (t.name === resultData.name && t.status === 'running') {\n return { ...t, result: resultData.result, status: 'success' as const }\n }\n return t\n })\n }\n break\n }\n\n case 'text_delta':\n msg.content = (msg.content || '') + (progress.data as string)\n break\n\n case 'text':\n if (!msg.content) {\n msg.content = progress.data as string\n }\n break\n\n case 'error':\n msg.content = (msg.content || '') + `\\n\\n❌ 错误: ${progress.data}`\n break\n }\n\n newMessages[index] = msg\n return newMessages\n })\n }, [])\n\n /** 发送消息 */\n const sendMessage = useCallback(async (text: string, images?: string[]) => {\n if (!text.trim() || isLoading) return\n\n // 如果没有当前会话,先创建\n let sessionId = currentSessionIdRef.current\n if (!sessionId) {\n try {\n const session = await adapter.createSession({\n title: '新对话',\n model: modelRef.current,\n mode: modeRef.current,\n })\n setSessions(prev => [session, ...prev])\n setCurrentSessionId(session.id)\n sessionId = session.id\n } catch (error) {\n console.error('创建会话失败:', error)\n return\n }\n }\n\n // 添加用户消息\n const userMsg: ChatMessage = {\n id: generateId(),\n role: 'user',\n content: text,\n images,\n timestamp: new Date(),\n }\n \n const currentMessages = messagesRef.current\n setMessages([...currentMessages, userMsg])\n\n // 保存用户消息\n try {\n await adapter.saveMessage({\n sessionId,\n role: 'user',\n content: text,\n })\n\n // 更新会话标题(如果是第一条消息)\n if (currentMessages.length === 0) {\n const title = text.slice(0, 20) + (text.length > 20 ? '...' : '')\n await adapter.updateSession(sessionId, { title })\n setSessions(prev => prev.map((s) =>\n s.id === sessionId ? { ...s, title } : s\n ))\n }\n } catch (error) {\n console.error('保存消息失败:', error)\n }\n\n // 创建助手消息\n const assistantMsgIndex = currentMessages.length + 1\n const assistantMsg: ChatMessage = {\n id: generateId(),\n role: 'assistant',\n content: '',\n toolCalls: [],\n thinkingComplete: false,\n searching: false,\n loading: true,\n timestamp: new Date(),\n }\n setMessages(prev => [...prev, assistantMsg])\n\n setIsLoading(true)\n abortControllerRef.current = new AbortController()\n\n try {\n // 使用异步迭代器接收消息流\n for await (const progress of adapter.sendMessage(\n text,\n {\n mode: modeRef.current,\n model: modelRef.current,\n enableWebSearch: webSearchRef.current,\n thinkingMode: thinkingRef.current ? 'enabled' : 'disabled',\n },\n images\n )) {\n // 检查是否被取消\n if (abortControllerRef.current?.signal.aborted) break\n\n updateMessage(assistantMsgIndex, progress)\n\n if (progress.type === 'done' || progress.type === 'error') {\n break\n }\n }\n } catch (error) {\n console.error('发送消息失败:', error)\n updateMessage(assistantMsgIndex, {\n type: 'error',\n data: error instanceof Error ? error.message : String(error),\n })\n } finally {\n setIsLoading(false)\n\n // 标记加载完成并保存\n setMessages(prev => {\n const newMessages = [...prev]\n const finalMsg = newMessages[assistantMsgIndex]\n if (finalMsg) {\n newMessages[assistantMsgIndex] = { ...finalMsg, loading: false }\n\n // 保存助手消息\n if (sessionId) {\n adapter.saveMessage({\n sessionId,\n role: 'assistant',\n content: finalMsg.content,\n thinking: finalMsg.thinking,\n toolCalls: finalMsg.toolCalls ? JSON.stringify(finalMsg.toolCalls) : undefined,\n searchResults: finalMsg.searchResults\n ? JSON.stringify(finalMsg.searchResults)\n : undefined,\n }).catch((e: Error) => console.error('保存助手消息失败:', e))\n }\n }\n return newMessages\n })\n\n abortControllerRef.current = null\n }\n }, [adapter, isLoading, updateMessage])\n\n /** 取消请求 */\n const cancelRequest = useCallback(() => {\n adapter.cancel()\n abortControllerRef.current?.abort()\n setIsLoading(false)\n }, [adapter])\n\n /** 复制消息 */\n const copyMessage = useCallback(async (messageId: string) => {\n const msg = messagesRef.current.find((m) => m.id === messageId)\n if (!msg) return\n\n try {\n await navigator.clipboard.writeText(msg.content)\n setMessages(prev => prev.map((m) =>\n m.id === messageId ? { ...m, copied: true } : m\n ))\n setTimeout(() => {\n setMessages(prev => prev.map((m) =>\n m.id === messageId ? { ...m, copied: false } : m\n ))\n }, 2000)\n } catch (err) {\n console.error('复制失败:', err)\n }\n }, [])\n\n /** 重新生成消息 */\n const regenerateMessage = useCallback((messageIndex: number) => {\n const currentMsgs = messagesRef.current\n if (messageIndex > 0 && currentMsgs[messageIndex - 1]?.role === 'user') {\n const userMsg = currentMsgs[messageIndex - 1]\n setMessages(prev => prev.slice(0, messageIndex - 1))\n sendMessage(userMsg.content, userMsg.images)\n }\n }, [sendMessage])\n\n /** 设置工作目录 */\n const setWorkingDirectory = useCallback((dir: string) => {\n if (adapter.setWorkingDir) {\n adapter.setWorkingDir(dir)\n }\n }, [adapter])\n\n // 配置方法\n const setMode = useCallback((value: ChatMode) => setModeState(value), [])\n const setModel = useCallback((value: string) => setModelState(value), [])\n const setWebSearch = useCallback((value: boolean) => setWebSearchState(value), [])\n const setThinking = useCallback((value: boolean) => setThinkingState(value), [])\n\n return {\n // 状态\n sessions,\n currentSessionId,\n messages,\n isLoading,\n mode,\n model,\n webSearch,\n thinking,\n\n // 会话方法\n loadSessions,\n switchSession,\n createNewSession,\n deleteSession,\n deleteCurrentSession,\n\n // 消息方法\n sendMessage,\n cancelRequest,\n copyMessage,\n regenerateMessage,\n\n // 配置方法\n setMode,\n setModel,\n setWebSearch,\n setThinking,\n\n // 工具方法\n setWorkingDirectory,\n }\n}\n","/**\n * ChatPanel Component\n * 与 Vue 版本 ChatPanel.vue 保持一致\n */\n\nimport { useEffect, useRef, useCallback, type FC, type ReactNode } from 'react'\nimport { useChat } from '../hooks/useChat'\nimport type { ChatAdapter } from '../adapter'\nimport type { ModelConfig, ChatMode } from '../types'\nimport { DEFAULT_MODELS } from '../types'\nimport { ChatHeader } from './chat/ui/ChatHeader'\nimport { WelcomeMessage } from './chat/ui/WelcomeMessage'\nimport { MessageBubble } from './chat/messages/MessageBubble'\nimport { ChatInput } from './ChatInput'\n\ninterface ChatPanelProps {\n /** Adapter 实例 */\n adapter?: ChatAdapter\n /** 工作目录 */\n workingDir?: string\n /** 默认模型 */\n defaultModel?: string\n /** 默认模式 */\n defaultMode?: ChatMode\n /** 可用模型列表 */\n models?: ModelConfig[]\n /** 隐藏标题栏 */\n hideHeader?: boolean\n /** 关闭回调(有此属性时显示关闭按钮) */\n onClose?: () => void\n /** 自定义类名 */\n className?: string\n /** 自定义 Markdown 渲染器 */\n renderMarkdown?: (content: string) => ReactNode\n}\n\nexport const ChatPanel: FC<ChatPanelProps> = ({\n adapter,\n workingDir,\n defaultModel = 'anthropic/claude-opus-4.5',\n defaultMode = 'agent',\n models = DEFAULT_MODELS,\n hideHeader = false,\n onClose,\n className = '',\n renderMarkdown,\n}) => {\n const messagesRef = useRef<HTMLDivElement>(null)\n\n const {\n sessions,\n currentSessionId,\n messages,\n isLoading,\n mode,\n model,\n webSearch,\n thinking,\n loadSessions,\n switchSession,\n createNewSession,\n deleteSession,\n sendMessage,\n cancelRequest,\n copyMessage,\n regenerateMessage,\n setMode,\n setModel,\n setWebSearch,\n setThinking,\n setWorkingDirectory,\n } = useChat({\n adapter,\n defaultModel,\n defaultMode,\n })\n\n // 选中的图片\n // const [selectedImages, setSelectedImages] = useState<string[]>([])\n\n // 初始化\n useEffect(() => {\n loadSessions()\n }, [loadSessions])\n\n // 工作目录变化时更新\n useEffect(() => {\n if (workingDir) {\n setWorkingDirectory(workingDir)\n }\n }, [workingDir, setWorkingDirectory])\n\n // 滚动到底部\n const scrollToBottom = useCallback(() => {\n if (messagesRef.current) {\n messagesRef.current.scrollTop = messagesRef.current.scrollHeight\n }\n }, [])\n\n // 消息变化时滚动\n useEffect(() => {\n scrollToBottom()\n }, [messages, scrollToBottom])\n\n // 发送消息\n const handleSend = useCallback(\n (text: string) => {\n sendMessage(text)\n },\n [sendMessage]\n )\n\n // 快捷操作\n const handleQuickAction = useCallback(\n (text: string) => {\n sendMessage(text)\n },\n [sendMessage]\n )\n\n // 重新发送(编辑后)\n const handleResend = useCallback(\n (index: number, text: string) => {\n // 删除当前消息及后续消息,然后重新发送\n // 这里简化处理,实际需要更完善的逻辑\n sendMessage(text)\n },\n [sendMessage]\n )\n\n // 关闭\n const handleClose = useCallback(() => {\n onClose?.()\n }, [onClose])\n\n // 清空所有对话\n const handleClearAll = useCallback(() => {\n console.log('清空所有对话')\n }, [])\n\n // 关闭其他对话\n const handleCloseOthers = useCallback(() => {\n console.log('关闭其他对话')\n }, [])\n\n // 导出对话\n const handleExport = useCallback(() => {\n console.log('导出对话')\n }, [])\n\n // 复制请求 ID\n const handleCopyId = useCallback(() => {\n if (currentSessionId) {\n navigator.clipboard.writeText(currentSessionId)\n console.log('已复制请求 ID:', currentSessionId)\n }\n }, [currentSessionId])\n\n // 反馈\n const handleFeedback = useCallback(() => {\n console.log('反馈')\n }, [])\n\n // 设置\n const handleSettings = useCallback(() => {\n console.log('Agent 设置')\n }, [])\n\n return (\n <div className={`chat-panel ${className}`.trim()}>\n {/* 顶部标题栏 */}\n {!hideHeader && (\n <ChatHeader\n sessions={sessions}\n currentSessionId={currentSessionId}\n showClose={!!onClose}\n onNewSession={createNewSession}\n onSwitchSession={switchSession}\n onDeleteSession={deleteSession}\n onClose={handleClose}\n onClearAll={handleClearAll}\n onCloseOthers={handleCloseOthers}\n onExport={handleExport}\n onCopyId={handleCopyId}\n onFeedback={handleFeedback}\n onSettings={handleSettings}\n />\n )}\n\n {/* 消息列表 */}\n <div ref={messagesRef} className=\"messages-container\">\n {messages.length === 0 ? (\n <WelcomeMessage onQuickAction={handleQuickAction} />\n ) : (\n messages.map((msg, index) => (\n <MessageBubble\n key={msg.id}\n role={msg.role}\n content={msg.content}\n images={msg.images}\n thinking={msg.thinking}\n thinkingComplete={msg.thinkingComplete}\n searchResults={msg.searchResults}\n searching={msg.searching}\n toolCalls={msg.toolCalls}\n copied={msg.copied}\n loading={msg.loading}\n onCopy={() => copyMessage(msg.id)}\n onRegenerate={() => regenerateMessage(index)}\n onSend={(text) => handleResend(index, text)}\n renderMarkdown={renderMarkdown}\n />\n ))\n )}\n </div>\n\n {/* 输入区域 */}\n <ChatInput\n selectedImages={[]}\n isLoading={isLoading}\n mode={mode}\n model={model}\n models={models}\n webSearchEnabled={webSearch}\n thinkingEnabled={thinking}\n onSend={handleSend}\n onCancel={cancelRequest}\n onModeChange={setMode}\n onModelChange={setModel}\n onWebSearchChange={setWebSearch}\n onThinkingChange={setThinking}\n />\n </div>\n )\n}\n","/**\n * AI Chat 前端类型定义\n * 与 Vue 版本保持一致\n */\n\n/** 搜索结果 */\nexport interface SearchResult {\n title: string\n url: string\n snippet: string\n}\n\n/** 工具调用 */\nexport interface ToolCall {\n name: string\n args?: Record<string, unknown>\n result?: string\n status: 'running' | 'success' | 'error'\n}\n\n/** 模型提供商 */\nexport type ModelProvider = 'openrouter' | 'doubao' | 'deepseek' | 'qwen' | 'gemini' | 'ark'\n\n/** 模型配置 */\nexport interface ModelConfig {\n provider: ModelProvider\n model: string\n displayName: string\n supportsTools: boolean\n supportsWebSearch: boolean\n supportedThinkingModes: ThinkingMode[]\n}\n\n/** 思考模式 */\nexport type ThinkingMode = 'enabled' | 'disabled'\n\n/** 聊天模式 */\nexport type ChatMode = 'agent' | 'ask'\n\n/** 聊天消息 */\nexport interface ChatMessage {\n id: string\n role: 'user' | 'assistant'\n content: string\n images?: string[]\n thinking?: string\n thinkingComplete?: boolean\n searchResults?: SearchResult[]\n searching?: boolean\n toolCalls?: ToolCall[]\n copied?: boolean\n loading?: boolean\n timestamp?: Date\n}\n\n/** 会话记录 */\nexport interface SessionRecord {\n id: string\n title: string\n model: string\n mode: ChatMode\n createdAt: Date\n updatedAt: Date\n}\n\n/** 消息记录 */\nexport interface MessageRecord {\n id: string\n sessionId: string\n role: 'user' | 'assistant'\n content: string\n thinking?: string\n toolCalls?: string\n searchResults?: string\n timestamp: Date\n}\n\n/** 默认模型列表 */\nexport const DEFAULT_MODELS: ModelConfig[] = [\n {\n provider: 'openrouter',\n model: 'anthropic/claude-opus-4.5',\n displayName: 'Claude Opus 4.5',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'doubao',\n model: 'doubao-seed-1-6-251015',\n displayName: 'Doubao Seed',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'deepseek',\n model: 'deepseek-v3-1-terminus',\n displayName: 'DeepSeek V3',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'qwen',\n model: 'qwen3-max-preview',\n displayName: 'Qwen Max',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'gemini',\n model: 'gemini-3-pro-preview',\n displayName: 'Gemini 3 Pro',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n]\n\n// ================ 以下为向后兼容的类型 ================\n\n/** @deprecated 使用 SessionRecord */\nexport interface ChatSession {\n id: string\n title: string\n messages: ChatMessage[]\n createdAt: Date\n updatedAt: Date\n}\n\n/** 音视频操作类型 */\nexport interface MediaOperation {\n id: string\n type: 'clip' | 'transcode' | 'merge' | 'extract_audio' | 'add_subtitle' | 'analyze'\n description: string\n sourceFiles: string[]\n targetFile?: string\n parameters?: Record<string, unknown>\n status?: 'pending' | 'applied' | 'rejected'\n preview?: string\n}\n\n/** @deprecated 使用字符串枚举 */\nexport type AiModel = 'gemini-3-pro-preview' | 'gemini-3-pro-image-preview'\n\nexport enum FileType {\n FOLDER = 'folder',\n IMAGE = 'image',\n VIDEO = 'video',\n AUDIO = 'audio',\n TEXT = 'text',\n PDF = 'pdf',\n CODE = 'code',\n ARCHIVE = 'archive',\n OTHER = 'other'\n}\n\nexport interface DiffStat {\n file: string\n additions: number\n deletions: number\n type: 'modified' | 'added' | 'deleted'\n}\n","/**\n * ChatHeader Component\n * 与 Vue 版本 ChatHeader.vue 保持一致\n */\n\nimport { useState, useRef, useEffect, useCallback, type FC } from 'react'\nimport { Plus, Clock, MoreHorizontal, X, MessageSquare, Pencil, Trash2 } from 'lucide-react'\nimport type { SessionRecord } from '../../../types'\n\ninterface ChatHeaderProps {\n /** 当前会话列表 */\n sessions: SessionRecord[]\n /** 当前会话 ID */\n currentSessionId?: string | null\n /** 是否显示关闭按钮 */\n showClose?: boolean\n /** 创建新会话 */\n onNewSession?: () => void\n /** 切换会话 */\n onSwitchSession?: (sessionId: string) => void\n /** 删除会话 */\n onDeleteSession?: (sessionId: string) => void\n /** 关闭面板 */\n onClose?: () => void\n /** 清空所有对话 */\n onClearAll?: () => void\n /** 关闭其他对话 */\n onCloseOthers?: () => void\n /** 导出对话 */\n onExport?: () => void\n /** 复制请求 ID */\n onCopyId?: () => void\n /** 反馈 */\n onFeedback?: () => void\n /** Agent 设置 */\n onSettings?: () => void\n}\n\n/** 格式化时间 */\nfunction formatTime(date: Date | string | undefined): string {\n if (!date) return ''\n const d = new Date(date)\n const now = new Date()\n const diff = now.getTime() - d.getTime()\n const days = Math.floor(diff / (1000 * 60 * 60 * 24))\n \n if (days === 0) {\n return d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })\n } else if (days === 1) {\n return '昨天'\n } else if (days < 7) {\n return `${days}天前`\n } else {\n return d.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })\n }\n}\n\nexport const ChatHeader: FC<ChatHeaderProps> = ({\n sessions,\n currentSessionId,\n showClose = false,\n onNewSession,\n onSwitchSession,\n onDeleteSession,\n onClose,\n onClearAll,\n onCloseOthers,\n onExport,\n onCopyId,\n onFeedback,\n onSettings,\n}) => {\n const [historyOpen, setHistoryOpen] = useState(false)\n const [moreMenuOpen, setMoreMenuOpen] = useState(false)\n const [hiddenTabs, setHiddenTabs] = useState<Set<string>>(new Set())\n\n const historyRef = useRef<HTMLDivElement>(null)\n const moreMenuRef = useRef<HTMLDivElement>(null)\n\n // 可见的会话\n const visibleSessions = sessions.filter((s) => !hiddenTabs.has(s.id))\n\n // 点击外部关闭菜单\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as HTMLElement\n if (historyRef.current && !historyRef.current.contains(target)) {\n setHistoryOpen(false)\n }\n if (moreMenuRef.current && !moreMenuRef.current.contains(target)) {\n setMoreMenuOpen(false)\n }\n }\n\n document.addEventListener('click', handleClickOutside)\n return () => document.removeEventListener('click', handleClickOutside)\n }, [])\n\n // 切换会话\n const handleSwitchSession = useCallback(\n (sessionId: string) => {\n onSwitchSession?.(sessionId)\n setHistoryOpen(false)\n },\n [onSwitchSession]\n )\n\n // 隐藏 tab\n const handleHideTab = useCallback(\n (sessionId: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setHiddenTabs((prev) => new Set([...prev, sessionId]))\n if (sessionId === currentSessionId) {\n const remaining = sessions.filter((s) => s.id !== sessionId && !hiddenTabs.has(s.id))\n if (remaining.length > 0) {\n onSwitchSession?.(remaining[0].id)\n }\n }\n },\n [currentSessionId, sessions, hiddenTabs, onSwitchSession]\n )\n\n // 删除会话\n const handleDeleteSession = (sessionId: string, e: React.MouseEvent) => {\n e.stopPropagation()\n if (window.confirm('确定要删除这个对话吗?')) {\n onDeleteSession?.(sessionId)\n }\n }\n\n // 菜单项点击\n const handleMenuClick = (callback?: () => void) => {\n callback?.()\n setMoreMenuOpen(false)\n }\n\n return (\n <div className=\"chat-header\">\n {/* 左侧:Tabs */}\n <div className=\"chat-tabs\">\n {visibleSessions.length === 0 ? (\n <span className=\"chat-tab active\">\n <span className=\"chat-tab-title\">New Chat</span>\n </span>\n ) : (\n visibleSessions.map((session) => {\n const title = session.title === '新对话' ? 'New Chat' : session.title\n const isActive = session.id === currentSessionId\n return (\n <div\n key={session.id}\n className={`chat-tab${isActive ? ' active' : ''}`}\n onClick={() => handleSwitchSession(session.id)}\n title={session.title}\n >\n <span className=\"chat-tab-title\">{title}</span>\n <span\n className=\"chat-tab-close\"\n onClick={(e) => handleHideTab(session.id, e)}\n title=\"关闭标签\"\n >\n <X size={12} />\n </span>\n </div>\n )\n })\n )}\n </div>\n\n {/* 右侧:操作按钮 */}\n <div className=\"chat-header-actions\">\n {/* 新建会话 */}\n <button className=\"header-btn\" onClick={onNewSession} title=\"新建对话\">\n <Plus size={14} />\n </button>\n\n {/* 历史记录 */}\n <div ref={historyRef} style={{ position: 'relative' }}>\n <button\n className={`header-btn${historyOpen ? ' active' : ''}`}\n onClick={(e) => {\n e.stopPropagation()\n setHistoryOpen(!historyOpen)\n setMoreMenuOpen(false)\n }}\n title=\"历史记录\"\n >\n <Clock size={14} />\n </button>\n\n {/* 历史记录面板 */}\n {historyOpen && (\n <div className=\"history-panel\">\n {sessions.length === 0 ? (\n <div className=\"history-empty\">暂无历史对话</div>\n ) : (\n sessions.map((session) => {\n const isCurrent = session.id === currentSessionId\n return (\n <div\n key={session.id}\n className={`history-item${isCurrent ? ' active' : ''}`}\n >\n <button\n className=\"history-item-content\"\n onClick={() => handleSwitchSession(session.id)}\n >\n <MessageSquare size={12} />\n <span className=\"history-item-title\">\n {session.title === '新对话' ? 'New Chat' : session.title}\n </span>\n <span className=\"history-item-time\">\n {isCurrent ? 'Current' : formatTime(session.updatedAt)}\n </span>\n </button>\n <div className=\"history-item-actions\">\n <button className=\"history-action-btn\" title=\"编辑\">\n <Pencil size={10} />\n </button>\n <button\n className=\"history-action-btn delete\"\n title=\"删除\"\n onClick={(e) => handleDeleteSession(session.id, e)}\n >\n <Trash2 size={10} />\n </button>\n </div>\n </div>\n )\n })\n )}\n </div>\n )}\n </div>\n\n {/* 更多选项 */}\n <div ref={moreMenuRef} style={{ position: 'relative' }}>\n <button\n className={`header-btn${moreMenuOpen ? ' active' : ''}`}\n onClick={(e) => {\n e.stopPropagation()\n setMoreMenuOpen(!moreMenuOpen)\n setHistoryOpen(false)\n }}\n title=\"更多选项\"\n >\n <MoreHorizontal size={14} />\n </button>\n\n {/* 更多选项菜单 */}\n {moreMenuOpen && (\n <div className=\"more-menu\">\n {showClose && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onClose)}>\n <span>关闭对话</span>\n <span className=\"menu-shortcut\">⌘ W</span>\n </button>\n )}\n {onClearAll && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onClearAll)}>\n 清空所有对话\n </button>\n )}\n {onCloseOthers && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onCloseOthers)}>\n 关闭其他对话\n </button>\n )}\n \n {(showClose || onClearAll || onCloseOthers) && <div className=\"menu-divider\" />}\n \n {onExport && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onExport)}>\n 导出对话\n </button>\n )}\n {onCopyId && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onCopyId)}>\n 复制请求 ID\n </button>\n )}\n {onFeedback && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onFeedback)}>\n 反馈\n </button>\n )}\n \n {(onExport || onCopyId || onFeedback) && onSettings && <div className=\"menu-divider\" />}\n \n {onSettings && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onSettings)}>\n Agent 设置\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n","/**\n * WelcomeMessage Component\n * 与 Vue 版本 WelcomeMessage.vue 保持一致\n */\n\nimport { type FC } from 'react'\nimport { Wand2, ImageIcon, Video, Terminal } from 'lucide-react'\n\ninterface WelcomeMessageProps {\n /** 快捷操作回调 */\n onQuickAction: (text: string) => void\n}\n\n/** 快捷操作配置 */\nconst QUICK_ACTIONS = [\n {\n id: 'txt2img',\n Icon: Wand2,\n label: '文生图',\n desc: 'AI 绘制创意图像',\n prompt: '帮我生成一张图片:',\n gradient: 'purple',\n iconColor: 'purple',\n featured: true,\n },\n {\n id: 'img2img',\n Icon: ImageIcon,\n label: '图生图',\n desc: '风格迁移',\n prompt: '基于这张图片进行风格转换',\n gradient: 'blue',\n iconColor: 'blue',\n featured: false,\n },\n {\n id: 'img2vid',\n Icon: Video,\n label: '图生视频',\n desc: '动态化',\n prompt: '将这张图片转换成视频',\n gradient: 'emerald',\n iconColor: 'emerald',\n featured: false,\n },\n {\n id: 'cmd',\n Icon: Terminal,\n label: '执行命令',\n desc: '系统管理',\n prompt: '执行命令:',\n gradient: 'orange',\n iconColor: 'orange',\n featured: true,\n },\n]\n\nexport const WelcomeMessage: FC<WelcomeMessageProps> = ({ onQuickAction }) => {\n return (\n <div className=\"welcome-message\">\n {/* 动态极光背景 */}\n <div className=\"welcome-glow purple\" />\n <div className=\"welcome-glow blue\" />\n\n {/* 标题区域 */}\n <div className=\"welcome-title-area\">\n <h1 className=\"welcome-title\">\n Create\n <br />\n <span className=\"welcome-title-accent\">Everything</span>\n </h1>\n <p className=\"welcome-subtitle\">释放 AI 的无限创造力</p>\n </div>\n\n {/* 快捷操作网格 */}\n <div className=\"quick-actions\">\n {QUICK_ACTIONS.map((action) => (\n <button\n key={action.id}\n className={`quick-action-btn${action.featured ? ' featured' : ''}`}\n onClick={() => onQuickAction(action.prompt)}\n >\n {/* 卡片背景渐变 */}\n <div className={`quick-action-gradient ${action.gradient}`} />\n\n {/* 图标 */}\n <action.Icon className={`quick-action-icon ${action.iconColor}`} />\n\n {/* 文字 */}\n <div className=\"quick-action-text\">\n <span className=\"quick-action-label\">{action.label}</span>\n <span className=\"quick-action-desc\">{action.desc}</span>\n </div>\n\n {/* 装饰性光斑 */}\n <div className=\"quick-action-glow\" />\n </button>\n ))}\n </div>\n\n {/* 底部装饰 */}\n <div className=\"welcome-footer\">\n <div className=\"welcome-footer-line\" />\n </div>\n </div>\n )\n}\n","/**\n * MessageBubble Component\n * 与 Vue 版本 MessageBubble.vue 保持一致\n */\n\nimport { type FC, type ReactNode } from 'react'\nimport { Copy, Check, RefreshCw } from 'lucide-react'\nimport { ExecutionSteps } from './ExecutionSteps'\nimport { ChatInput } from '../../ChatInput'\nimport type { SearchResult, ToolCall } from '../../../types'\n\ninterface MessageBubbleProps {\n role: 'user' | 'assistant'\n content: string\n images?: string[]\n thinking?: string\n thinkingComplete?: boolean\n thinkingDuration?: number\n searchResults?: SearchResult[]\n searching?: boolean\n toolCalls?: ToolCall[]\n copied?: boolean\n loading?: boolean\n onCopy?: () => void\n onRegenerate?: () => void\n /** 编辑用户消息后重新发送 */\n onSend?: (text: string) => void\n /** 自定义 Markdown 渲染器 */\n renderMarkdown?: (content: string) => ReactNode\n}\n\n/** 默认 Markdown 渲染(简单处理) */\nfunction defaultRenderMarkdown(content: string): ReactNode {\n // 简单的 Markdown 处理:代码块\n const parts = content.split(/(```[\\s\\S]*?```)/g)\n \n return parts.map((part, i) => {\n if (part.startsWith('```') && part.endsWith('```')) {\n const code = part.slice(3, -3)\n const firstLine = code.indexOf('\\n')\n const lang = firstLine > 0 ? code.slice(0, firstLine).trim() : ''\n const codeContent = firstLine > 0 ? code.slice(firstLine + 1) : code\n return (\n <pre key={i}>\n <code>{codeContent}</code>\n </pre>\n )\n }\n // 处理行内代码\n const inlineParts = part.split(/(`[^`]+`)/g)\n return (\n <span key={i}>\n {inlineParts.map((p, j) => {\n if (p.startsWith('`') && p.endsWith('`')) {\n return <code key={j}>{p.slice(1, -1)}</code>\n }\n return p\n })}\n </span>\n )\n })\n}\n\nexport const MessageBubble: FC<MessageBubbleProps> = ({\n role,\n content,\n images,\n thinking,\n thinkingComplete = true,\n thinkingDuration,\n searchResults,\n searching,\n toolCalls,\n copied,\n loading,\n onCopy,\n onRegenerate,\n onSend,\n renderMarkdown = defaultRenderMarkdown,\n}) => {\n const isUser = role === 'user'\n\n return (\n <div className=\"message-bubble\">\n {/* 用户消息 - 复用 ChatInput 组件 */}\n {isUser ? (\n <ChatInput\n variant=\"message\"\n value={content}\n selectedImages={images}\n onSend={onSend}\n />\n ) : (\n /* AI 消息 */\n <div className=\"assistant-message\">\n {/* 执行步骤列表 */}\n <ExecutionSteps\n loading={loading}\n hasContent={!!content}\n thinking={thinking}\n thinkingComplete={thinkingComplete}\n thinkingDuration={thinkingDuration}\n searching={searching}\n searchResults={searchResults}\n toolCalls={toolCalls}\n />\n\n {/* 消息内容 */}\n {content && (\n <div className=\"message-content\">\n {renderMarkdown(content)}\n </div>\n )}\n\n {/* 操作按钮 */}\n {content && !loading && (\n <div className=\"message-actions\">\n <button className={`action-btn${copied ? ' copied' : ''}`} onClick={onCopy} title=\"复制\">\n {copied ? <Check size={14} /> : <Copy size={14} />}\n </button>\n <button className=\"action-btn\" onClick={onRegenerate} title=\"重新生成\">\n <RefreshCw size={14} />\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n","/**\n * ExecutionSteps Component\n * 与 Vue 版本 ExecutionSteps.vue 保持一致\n */\n\nimport { useState, type FC, type ReactNode } from 'react'\nimport {\n ChevronDown,\n ChevronUp,\n Sparkles,\n Globe,\n FileText,\n FileEdit,\n Terminal,\n Search,\n Folder,\n FolderPlus,\n Trash2,\n Image,\n Video,\n Wrench,\n ExternalLink,\n} from 'lucide-react'\nimport type { SearchResult, ToolCall } from '../../../types'\n\ninterface ExecutionStepsProps {\n /** 是否正在加载 */\n loading?: boolean\n /** 是否有消息内容 */\n hasContent?: boolean\n /** 思考内容 */\n thinking?: string\n /** 思考是否完成 */\n thinkingComplete?: boolean\n /** 思考耗时 */\n thinkingDuration?: number\n /** 是否正在搜索 */\n searching?: boolean\n /** 搜索结果 */\n searchResults?: SearchResult[]\n /** 工具调用列表 */\n toolCalls?: ToolCall[]\n}\n\n/** 步骤项组件 */\ninterface StepItemProps {\n icon: ReactNode\n title: string\n status: 'running' | 'completed' | 'error'\n extra?: string\n detail?: ReactNode\n defaultExpanded?: boolean\n}\n\nconst StepItem: FC<StepItemProps> = ({\n icon,\n title,\n status,\n extra,\n detail,\n defaultExpanded = false,\n}) => {\n const [expanded, setExpanded] = useState(defaultExpanded)\n const isRunning = status === 'running'\n const hasDetail = !!detail\n\n return (\n <div className=\"step-item\">\n <button\n className={`step-header${isRunning ? ' running' : ''}`}\n onClick={() => hasDetail && setExpanded(!expanded)}\n disabled={!hasDetail}\n style={{ cursor: hasDetail ? 'pointer' : 'default' }}\n >\n <span className={`step-icon${isRunning ? ' pulse' : ''}`}>{icon}</span>\n <span className=\"step-title\">{title}</span>\n {hasDetail && (\n expanded ? <ChevronUp className=\"step-chevron\" size={12} /> : <ChevronDown className=\"step-chevron\" size={12} />\n )}\n {extra && <span className=\"step-extra\">{extra}</span>}\n </button>\n\n {expanded && detail && (\n <div className=\"step-detail\">\n {typeof detail === 'string' ? <pre>{detail}</pre> : detail}\n </div>\n )}\n </div>\n )\n}\n\n/** 获取工具调用的显示名称 */\nfunction getToolDisplayName(name: string): string {\n const nameMap: Record<string, string> = {\n read_file: '读取文件',\n write_file: '写入文件',\n execute_command: '执行命令',\n search_files: '搜索文件',\n list_directory: '列出目录',\n create_directory: '创建目录',\n delete_file: '删除文件',\n web_search: '网页搜索',\n generate_image: '生成图片',\n image_to_video: '图片转视频',\n }\n return nameMap[name] || name\n}\n\n/** 获取工具调用的图标 */\nfunction getToolIcon(name: string) {\n switch (name) {\n case 'read_file': return <FileText size={14} />\n case 'write_file': return <FileEdit size={14} />\n case 'execute_command': return <Terminal size={14} />\n case 'search_files': return <Search size={14} />\n case 'list_directory': return <Folder size={14} />\n case 'create_directory': return <FolderPlus size={14} />\n case 'delete_file': return <Trash2 size={14} />\n case 'web_search': return <Globe size={14} />\n case 'generate_image': return <Image size={14} />\n case 'image_to_video': return <Video size={14} />\n default: return <Wrench size={14} />\n }\n}\n\n/** 格式化工具调用参数 */\nfunction formatToolArgs(args?: Record<string, unknown>): string {\n if (!args) return ''\n return Object.entries(args)\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join('\\n')\n}\n\nexport const ExecutionSteps: FC<ExecutionStepsProps> = ({\n loading,\n hasContent,\n thinking,\n thinkingComplete = true,\n thinkingDuration,\n searching,\n searchResults,\n toolCalls,\n}) => {\n // 判断是否有任何执行步骤\n const hasSteps =\n thinking ||\n searching ||\n (searchResults && searchResults.length > 0) ||\n (toolCalls && toolCalls.length > 0) ||\n (loading && !hasContent)\n\n if (!hasSteps) return null\n\n return (\n <div className=\"execution-steps\">\n {/* 正在规划 */}\n {loading && !hasContent && !thinking && !searching && (!toolCalls || toolCalls.length === 0) && (\n <StepItem\n icon={<Sparkles size={14} />}\n title=\"正在规划下一步...\"\n status=\"running\"\n />\n )}\n\n {/* 思考过程 */}\n {thinking && (\n <StepItem\n icon={<Sparkles size={14} />}\n title={thinkingComplete ? '思考完成' : '思考中...'}\n status={thinkingComplete ? 'completed' : 'running'}\n extra={thinkingDuration ? `${thinkingDuration}s` : undefined}\n detail={thinking}\n />\n )}\n\n {/* 搜索 */}\n {(searching || (searchResults && searchResults.length > 0)) && (\n <StepItem\n icon={<Globe size={14} />}\n title={searching ? '搜索中...' : `搜索完成 ${searchResults?.length || 0} 条结果`}\n status={searching ? 'running' : 'completed'}\n detail={\n searchResults && searchResults.length > 0 ? (\n <div>\n {searchResults.map((result, i) => (\n <a\n key={i}\n href={result.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"search-result-item\"\n >\n <div className=\"search-result-title\">\n <span>{result.title}</span>\n <ExternalLink size={12} style={{ opacity: 0.5 }} />\n </div>\n <div className=\"search-result-snippet\">{result.snippet}</div>\n </a>\n ))}\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* 工具调用 */}\n {toolCalls &&\n toolCalls.map((call, index) => (\n <StepItem\n key={`tool-${index}`}\n icon={getToolIcon(call.name)}\n title={`${getToolDisplayName(call.name)}${call.status === 'running' ? '...' : ''}`}\n status={call.status === 'running' ? 'running' : call.status === 'error' ? 'error' : 'completed'}\n detail={\n <div>\n {call.args && (\n <div style={{ marginBottom: call.result ? 8 : 0 }}>\n <div style={{ fontSize: 10, color: 'var(--chat-text-muted)', marginBottom: 4 }}>参数</div>\n <pre style={{ margin: 0 }}>{formatToolArgs(call.args)}</pre>\n </div>\n )}\n {call.result && (\n <div>\n <div style={{ fontSize: 10, color: 'var(--chat-text-muted)', marginBottom: 4 }}>结果</div>\n <pre style={{ margin: 0, maxHeight: 160, overflow: 'auto' }}>{call.result}</pre>\n </div>\n )}\n </div>\n }\n />\n ))}\n </div>\n )\n}\n","/**\n * ChatInput Component\n * 与 Vue 版本 ChatInput.vue 保持一致\n */\n\nimport { useState, useRef, useCallback, useEffect, type FC } from 'react'\nimport {\n X,\n ChevronDown,\n Check,\n Globe,\n Sparkles,\n ImageIcon,\n Square,\n ArrowUp,\n Zap,\n MessageCircle,\n AtSign,\n Mic,\n} from 'lucide-react'\nimport type { ChatMode, ModelConfig } from '../types'\nimport { DEFAULT_MODELS } from '../types'\n\ninterface ChatInputProps {\n /** 变体模式:input-底部输入框,message-历史消息 */\n variant?: 'input' | 'message'\n /** 受控值(用于历史消息编辑) */\n value?: string\n selectedImages?: string[]\n isLoading?: boolean\n mode?: ChatMode\n model?: string\n models?: ModelConfig[]\n webSearchEnabled?: boolean\n thinkingEnabled?: boolean\n onSend?: (text: string) => void\n onRemoveImage?: (index: number) => void\n onCancel?: () => void\n onUploadImage?: () => void\n onAtContext?: () => void\n onModeChange?: (mode: ChatMode) => void\n onModelChange?: (model: string) => void\n onWebSearchChange?: (enabled: boolean) => void\n onThinkingChange?: (enabled: boolean) => void\n}\n\n/** 模式配置 */\nconst MODES = [\n { value: 'agent' as const, label: 'Agent', Icon: Zap },\n { value: 'ask' as const, label: 'Ask', Icon: MessageCircle },\n]\n\nexport const ChatInput: FC<ChatInputProps> = ({\n variant = 'input',\n value = '',\n selectedImages = [],\n isLoading = false,\n mode = 'agent',\n model = '',\n models = DEFAULT_MODELS,\n webSearchEnabled = false,\n thinkingEnabled = false,\n onSend,\n onRemoveImage,\n onCancel,\n onUploadImage,\n onAtContext,\n onModeChange,\n onModelChange,\n onWebSearchChange,\n onThinkingChange,\n}) => {\n const isMessageVariant = variant === 'message'\n\n const [inputText, setInputText] = useState(value)\n const [isFocused, setIsFocused] = useState(false)\n const [modeMenuOpen, setModeMenuOpen] = useState(false)\n const [modelMenuOpen, setModelMenuOpen] = useState(false)\n\n const inputRef = useRef<HTMLTextAreaElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n // 同步外部 value\n useEffect(() => {\n setInputText(value)\n }, [value])\n\n const currentMode = MODES.find((m) => m.value === mode) || MODES[0]\n const currentModel = models.find((m) => m.model === model)\n\n // 是否显示工具栏\n const showToolbar = !isMessageVariant || isFocused\n\n // 预览\n const selectedPreview = selectedImages.slice(0, 3)\n\n // 占位符\n const placeholder = selectedImages.length > 0\n ? '描述你想要的效果...'\n : mode === 'ask'\n ? '有什么问题想问我?'\n : '描述任务,@ 添加上下文'\n\n // 自动调整高度\n const adjustTextareaHeight = useCallback(() => {\n if (inputRef.current) {\n inputRef.current.style.height = 'auto'\n const scrollHeight = inputRef.current.scrollHeight\n inputRef.current.style.height = `${Math.min(scrollHeight, 150)}px`\n }\n }, [])\n\n // 发送或取消\n const handleSendOrCancel = useCallback(() => {\n if (isLoading) {\n onCancel?.()\n return\n }\n\n const text = inputText.trim()\n if (!text) return\n\n onSend?.(text)\n\n if (!isMessageVariant) {\n setInputText('')\n if (inputRef.current) {\n inputRef.current.style.height = 'auto'\n }\n inputRef.current?.focus()\n } else {\n setIsFocused(false)\n }\n }, [isLoading, inputText, onSend, onCancel, isMessageVariant])\n\n // 键盘事件\n const handleKeydown = useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault()\n handleSendOrCancel()\n } else {\n setTimeout(adjustTextareaHeight, 0)\n }\n },\n [handleSendOrCancel, adjustTextareaHeight]\n )\n\n // 选择模式\n const selectMode = useCallback(\n (value: ChatMode) => {\n onModeChange?.(value)\n setModeMenuOpen(false)\n },\n [onModeChange]\n )\n\n // 选择模型\n const selectModel = useCallback(\n (m: ModelConfig) => {\n onModelChange?.(m.model)\n setModelMenuOpen(false)\n },\n [onModelChange]\n )\n\n // 图片 URL 处理\n const getImageUrl = (path: string): string => {\n if (\n path.startsWith('app://') ||\n path.startsWith('file://') ||\n path.startsWith('data:') ||\n path.startsWith('http')\n ) {\n return path\n }\n if (path.match(/^[A-Z]:\\\\/i)) {\n return `app://file${encodeURIComponent(path.replace(/\\\\/g, '/'))}`\n }\n return `app://file${encodeURIComponent(path)}`\n }\n\n // 点击外部关闭菜单\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as HTMLElement\n if (!target.closest('.selector')) {\n setModeMenuOpen(false)\n setModelMenuOpen(false)\n }\n if (\n isMessageVariant &&\n containerRef.current &&\n !containerRef.current.contains(target)\n ) {\n setIsFocused(false)\n }\n }\n\n document.addEventListener('click', handleClickOutside)\n return () => document.removeEventListener('click', handleClickOutside)\n }, [isMessageVariant])\n\n const CurrentModeIcon = currentMode.Icon\n\n return (\n <div className={`chat-input${isMessageVariant ? ' message-variant' : ''}`}>\n <div\n ref={containerRef}\n className={`input-container${isFocused ? ' focused' : ''}`}\n >\n {/* 附件预览 */}\n {selectedImages.length > 0 && (\n <div className=\"attachment-preview\">\n <div className=\"preview-images\">\n {selectedPreview.map((img, index) => (\n <div key={`${img}-${index}`} className=\"preview-item\">\n <img\n src={getImageUrl(img)}\n className=\"preview-thumb\"\n alt={`附件 ${index + 1}`}\n onError={(e) => {\n (e.target as HTMLImageElement).style.display = 'none'\n }}\n />\n {!isMessageVariant && (\n <button\n className=\"remove-btn\"\n title={`移除图片 ${index + 1}`}\n onClick={() => onRemoveImage?.(index)}\n >\n <X size={10} />\n </button>\n )}\n </div>\n ))}\n {selectedImages.length > 3 && (\n <div className=\"preview-more\">+{selectedImages.length - 3}</div>\n )}\n </div>\n </div>\n )}\n\n {/* 输入框 */}\n <div className=\"input-field-wrapper\">\n <textarea\n ref={inputRef}\n value={inputText}\n onChange={(e) => setInputText(e.target.value)}\n onKeyDown={handleKeydown}\n onInput={adjustTextareaHeight}\n onFocus={() => setIsFocused(true)}\n placeholder={placeholder}\n rows={1}\n className=\"input-field\"\n />\n </div>\n\n {/* 底部控制栏 */}\n {showToolbar && (\n <div className=\"input-controls\">\n {/* 左侧:模式和模型选择 */}\n <div className=\"input-left\">\n {/* 模式选择 */}\n <div\n className=\"selector mode-selector\"\n onClick={(e) => {\n e.stopPropagation()\n setModeMenuOpen(!modeMenuOpen)\n setModelMenuOpen(false)\n }}\n >\n <CurrentModeIcon size={12} />\n <span>{currentMode.label}</span>\n <ChevronDown size={10} className=\"chevron\" />\n\n {modeMenuOpen && (\n <div className=\"dropdown-menu\" onClick={(e) => e.stopPropagation()}>\n {MODES.map((m) => (\n <button\n key={m.value}\n className={`dropdown-item${mode === m.value ? ' active' : ''}`}\n onClick={() => selectMode(m.value)}\n >\n <m.Icon size={14} />\n <span>{m.label}</span>\n {mode === m.value && <Check size={14} className=\"check-icon\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* 模型选择 */}\n <div\n className=\"selector model-selector\"\n onClick={(e) => {\n e.stopPropagation()\n setModelMenuOpen(!modelMenuOpen)\n setModeMenuOpen(false)\n }}\n >\n <span>{currentModel?.displayName || 'Auto'}</span>\n <ChevronDown size={10} className=\"chevron\" />\n\n {modelMenuOpen && (\n <div className=\"dropdown-menu\" onClick={(e) => e.stopPropagation()}>\n {models.map((m) => (\n <button\n key={m.model}\n className={`dropdown-item${model === m.model ? ' active' : ''}`}\n onClick={() => selectModel(m)}\n >\n <span>{m.displayName}</span>\n {model === m.model && <Check size={14} className=\"check-icon\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* 右侧:功能按钮 */}\n <div className=\"input-right\">\n <button className=\"icon-btn\" title=\"提及上下文 (@)\" onClick={onAtContext}>\n <AtSign size={14} />\n </button>\n\n <button\n className={`toggle-btn${thinkingEnabled ? ' active' : ''}`}\n title=\"深度思考\"\n onClick={() => onThinkingChange?.(!thinkingEnabled)}\n >\n <Sparkles size={14} />\n </button>\n\n <button\n className={`toggle-btn${webSearchEnabled ? ' active' : ''}`}\n title=\"联网搜索\"\n onClick={() => onWebSearchChange?.(!webSearchEnabled)}\n >\n <Globe size={14} />\n </button>\n\n <button className=\"icon-btn\" title=\"上传图片\" onClick={onUploadImage}>\n <ImageIcon size={14} />\n </button>\n\n {inputText.trim() || isLoading ? (\n <button\n className={`send-btn${isLoading ? ' loading' : ''}`}\n title={isLoading ? '停止' : isMessageVariant ? '重新发送' : '发送'}\n onClick={handleSendOrCancel}\n >\n {isLoading ? <Square size={14} /> : <ArrowUp size={14} />}\n </button>\n ) : (\n <button className=\"icon-btn\" title=\"语音输入\">\n <Mic size={14} />\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useRef","useCallback","FileType","useState","useRef","useCallback","jsx","jsxs","Check","useState","Terminal","Trash2","Video","jsx","jsxs","useState","useRef","useCallback","useEffect","X","ChevronDown","Globe","Sparkles","ImageIcon","jsx","jsxs","useState","useRef","useEffect","useCallback","value","X","ChevronDown","Sparkles","Globe","ImageIcon","jsx","jsxs","Check","jsx","jsxs","useRef","useEffect","useCallback"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hooks/useChat.ts","../src/components/ChatPanel.tsx","../src/types/index.ts","../src/components/chat/ui/ChatHeader.tsx","../src/components/chat/ui/WelcomeMessage.tsx","../src/components/chat/messages/MessageBubble.tsx","../src/components/chat/messages/ExecutionSteps.tsx","../src/components/ChatInput.tsx"],"sourcesContent":["/**\n * @huyooo/ai-chat-frontend-react\n *\n * AI Chat 前端组件库 - React 版本\n *\n * 使用 adapter 模式,与后端通信方式解耦\n * 与 Vue 版本 (@huyooo/ai-chat-frontend-vue) 保持一致的 API\n */\n\n// 从 bridge-electron 重新导出核心类型,确保类型一致性\nexport type {\n ChatAdapter,\n ChatProgress,\n ChatProgressType,\n ChatOptions,\n ChatMode,\n ModelConfig,\n ModelProvider,\n ThinkingMode,\n SessionRecord,\n MessageRecord,\n} from '@huyooo/ai-chat-bridge-electron/renderer'\n\n// 导出 createElectronAdapter 便于使用\nexport { createElectronAdapter } from '@huyooo/ai-chat-bridge-electron/renderer'\n\n// 导出本地定义的辅助类型\nexport type {\n ThinkingData,\n ToolCallData,\n ToolResultData,\n ImageData,\n SendMessageOptions,\n CreateSessionOptions,\n UpdateSessionOptions,\n SaveMessageOptions,\n} from './adapter'\nexport { createNullAdapter } from './adapter'\n\n// 导出 hooks\nexport { useChat } from './hooks/useChat'\nexport type { UseChatOptions } from './hooks/useChat'\n\n// 导出主组件\nexport { ChatPanel } from './components/ChatPanel'\n\n// 导出输入组件\nexport { ChatInput } from './components/ChatInput'\n\n// 导出 Header 组件\nexport { ChatHeader } from './components/chat/ui/ChatHeader'\n\n// 导出欢迎消息组件\nexport { WelcomeMessage } from './components/chat/ui/WelcomeMessage'\n\n// 导出消息组件\nexport { MessageBubble } from './components/chat/messages/MessageBubble'\nexport { ExecutionSteps } from './components/chat/messages/ExecutionSteps'\n\n// 导出前端特有类型(不与 bridge-electron 重复)\nexport type {\n ChatMessage,\n SearchResult,\n ToolCall,\n // 向后兼容\n ChatSession,\n MediaOperation,\n AiModel,\n DiffStat,\n} from './types'\nexport { DEFAULT_MODELS, FileType } from './types'\n\n/**\n * 使用说明:\n *\n * 1. 导入样式:\n * import '@huyooo/ai-chat-frontend-react/style.css'\n *\n * 2. 创建 adapter 和使用组件(一行导入):\n * import { ChatPanel, createElectronAdapter } from '@huyooo/ai-chat-frontend-react'\n * const adapter = createElectronAdapter()\n * <ChatPanel adapter={adapter} />\n *\n * 3. 或使用 useChat hook 自定义 UI:\n * import { useChat, createElectronAdapter } from '@huyooo/ai-chat-frontend-react'\n * const adapter = createElectronAdapter()\n * const chat = useChat({ adapter })\n */\n","/**\n * Chat Adapter 辅助类型和工具\n * 核心 ChatAdapter 接口从 bridge-electron 导入\n */\n\nimport type { ChatAdapter, ChatMode, ThinkingMode } from '@huyooo/ai-chat-bridge-electron/renderer'\nimport type { SearchResult } from './types'\n\n/** 思考数据 */\nexport interface ThinkingData {\n content: string\n isComplete: boolean\n}\n\n/** 工具调用数据 */\nexport interface ToolCallData {\n name: string\n args: Record<string, unknown>\n}\n\n/** 工具结果数据 */\nexport interface ToolResultData {\n name: string\n result: string\n}\n\n/** 图片数据 */\nexport interface ImageData {\n base64: string\n mimeType: string\n}\n\n/** 发送消息选项 */\nexport interface SendMessageOptions {\n mode: ChatMode\n model: string\n enableWebSearch: boolean\n thinkingMode: ThinkingMode\n}\n\n/** 创建会话选项 */\nexport interface CreateSessionOptions {\n title: string\n model: string\n mode: ChatMode\n}\n\n/** 更新会话选项 */\nexport interface UpdateSessionOptions {\n title?: string\n model?: string\n mode?: ChatMode\n}\n\n/** 保存消息选项 */\nexport interface SaveMessageOptions {\n sessionId: string\n role: 'user' | 'assistant'\n content: string\n thinking?: string\n toolCalls?: string\n searchResults?: string\n}\n\n/**\n * 创建空 Adapter(用于测试或无后端场景)\n * 返回一个最小实现的 ChatAdapter\n */\nexport function createNullAdapter(): ChatAdapter {\n return {\n async getModels() {\n return []\n },\n async getSessions() {\n return []\n },\n async getSession() {\n return null\n },\n async createSession(params) {\n return {\n id: Date.now().toString(),\n appId: null,\n userId: null,\n title: params?.title || '新对话',\n model: params?.model || '',\n mode: (params?.mode || 'agent') as ChatMode,\n createdAt: new Date(),\n updatedAt: new Date(),\n }\n },\n async updateSession() {\n return null\n },\n async deleteSession() {},\n async getMessages() {\n return []\n },\n async saveMessage(params) {\n return {\n id: Date.now().toString(),\n sessionId: params.sessionId,\n role: params.role,\n content: params.content,\n thinking: params.thinking || null,\n toolCalls: params.toolCalls || null,\n searchResults: params.searchResults || null,\n operationIds: null,\n timestamp: new Date(),\n }\n },\n async deleteMessagesAfter() {},\n async getOperations() {\n return []\n },\n async getTrashItems() {\n return []\n },\n async restoreFromTrash() {\n return undefined\n },\n async *sendMessage() {\n yield { type: 'text', data: '无可用的 Adapter' }\n yield { type: 'done', data: '' }\n },\n cancel() {},\n }\n}\n","/**\n * useChat Hook\n * 管理聊天状态和与后端的通信\n * 使用 Adapter 模式解耦后端通信\n * \n * 与 Vue 版本 useChat composable 保持一致\n */\n\nimport { useState, useCallback, useRef } from 'react'\nimport type {\n ChatAdapter,\n ChatProgress,\n ChatMode,\n SessionRecord,\n MessageRecord,\n} from '@huyooo/ai-chat-bridge-electron/renderer'\nimport { createNullAdapter } from '../adapter'\nimport type { ChatMessage, SearchResult, ToolCall } from '../types'\n\n/** 生成唯一 ID */\nfunction generateId(): string {\n return Date.now().toString(36) + Math.random().toString(36).substr(2)\n}\n\n/** 转换存储的消息为显示格式 */\nfunction convertToMessage(record: MessageRecord): ChatMessage {\n return {\n id: record.id,\n role: record.role,\n content: record.content,\n thinking: record.thinking || undefined,\n thinkingComplete: true,\n toolCalls: record.toolCalls ? JSON.parse(record.toolCalls) : undefined,\n searchResults: record.searchResults ? JSON.parse(record.searchResults) : undefined,\n searching: false,\n timestamp: record.timestamp,\n }\n}\n\nexport interface UseChatOptions {\n /** Adapter 实例 */\n adapter?: ChatAdapter\n /** 默认模型 */\n defaultModel?: string\n /** 默认模式 */\n defaultMode?: ChatMode\n}\n\n/**\n * 聊天状态管理 Hook\n */\nexport function useChat(options: UseChatOptions = {}) {\n const {\n adapter = createNullAdapter(),\n defaultModel = 'anthropic/claude-opus-4.5',\n defaultMode = 'agent',\n } = options\n\n // 会话状态\n const [sessions, setSessions] = useState<SessionRecord[]>([])\n const [currentSessionId, setCurrentSessionId] = useState<string | null>(null)\n const [messages, setMessages] = useState<ChatMessage[]>([])\n\n // 配置状态\n const [mode, setModeState] = useState<ChatMode>(defaultMode)\n const [model, setModelState] = useState(defaultModel)\n const [webSearch, setWebSearchState] = useState(true)\n const [thinking, setThinkingState] = useState(true)\n\n // 加载状态\n const [isLoading, setIsLoading] = useState(false)\n\n // 取消控制器\n const abortControllerRef = useRef<AbortController | null>(null)\n\n // 用于在回调中访问最新状态\n const sessionsRef = useRef(sessions)\n const messagesRef = useRef(messages)\n const currentSessionIdRef = useRef(currentSessionId)\n const modeRef = useRef(mode)\n const modelRef = useRef(model)\n const webSearchRef = useRef(webSearch)\n const thinkingRef = useRef(thinking)\n\n // 同步 ref\n sessionsRef.current = sessions\n messagesRef.current = messages\n currentSessionIdRef.current = currentSessionId\n modeRef.current = mode\n modelRef.current = model\n webSearchRef.current = webSearch\n thinkingRef.current = thinking\n\n /** 加载会话列表 */\n const loadSessions = useCallback(async () => {\n try {\n const list = await adapter.getSessions()\n setSessions(list)\n // 如果有会话且没有当前会话,选择最新的\n if (list.length > 0 && !currentSessionIdRef.current) {\n const firstSession = list[0]\n setCurrentSessionId(firstSession.id)\n const savedMessages = await adapter.getMessages(firstSession.id)\n setMessages(savedMessages.map(convertToMessage))\n setModeState(firstSession.mode)\n setModelState(firstSession.model)\n }\n } catch (error) {\n console.error('加载会话失败:', error)\n }\n }, [adapter])\n\n /** 切换会话 */\n const switchSession = useCallback(async (sessionId: string) => {\n if (currentSessionIdRef.current === sessionId) return\n\n setCurrentSessionId(sessionId)\n\n try {\n const savedMessages = await adapter.getMessages(sessionId)\n setMessages(savedMessages.map(convertToMessage))\n\n // 更新配置\n const session = sessionsRef.current.find((s) => s.id === sessionId)\n if (session) {\n setModeState(session.mode)\n setModelState(session.model)\n }\n } catch (error) {\n console.error('加载消息失败:', error)\n setMessages([])\n }\n }, [adapter])\n\n /** 创建新会话 */\n const createNewSession = useCallback(async () => {\n try {\n const session = await adapter.createSession({\n title: '新对话',\n model: modelRef.current,\n mode: modeRef.current,\n })\n setSessions(prev => [session, ...prev])\n setCurrentSessionId(session.id)\n setMessages([])\n } catch (error) {\n console.error('创建会话失败:', error)\n }\n }, [adapter])\n\n /** 删除会话 */\n const deleteSession = useCallback(async (sessionId: string) => {\n try {\n await adapter.deleteSession(sessionId)\n setSessions(prev => prev.filter((s) => s.id !== sessionId))\n\n // 如果删除的是当前会话,切换到下一个\n if (currentSessionIdRef.current === sessionId) {\n const remaining = sessionsRef.current.filter((s) => s.id !== sessionId)\n if (remaining.length > 0) {\n await switchSession(remaining[0].id)\n } else {\n setCurrentSessionId(null)\n setMessages([])\n }\n }\n } catch (error) {\n console.error('删除会话失败:', error)\n }\n }, [adapter, switchSession])\n\n /** 删除当前会话 */\n const deleteCurrentSession = useCallback(async () => {\n if (currentSessionIdRef.current) {\n await deleteSession(currentSessionIdRef.current)\n }\n }, [deleteSession])\n\n /** 更新消息 */\n const updateMessage = useCallback((index: number, progress: ChatProgress) => {\n setMessages(prev => {\n const newMessages = [...prev]\n const msg = { ...newMessages[index] }\n if (!msg) return prev\n\n switch (progress.type) {\n case 'thinking': {\n const thinkingData = progress.data as { content: string; isComplete: boolean }\n if (thinkingData.content) {\n msg.thinking = (msg.thinking || '') + thinkingData.content\n }\n msg.thinkingComplete = thinkingData.isComplete\n break\n }\n\n case 'search_start':\n msg.searching = true\n break\n\n case 'search_result': {\n msg.searching = false\n const searchData = progress.data as { results: SearchResult[] }\n msg.searchResults = searchData.results || []\n break\n }\n\n case 'tool_call': {\n const toolData = progress.data as { name: string; args: Record<string, unknown> }\n if (!msg.toolCalls) msg.toolCalls = []\n msg.toolCalls = [...msg.toolCalls, {\n name: toolData.name,\n args: toolData.args,\n status: 'running' as const,\n }]\n break\n }\n\n case 'tool_result': {\n const resultData = progress.data as { name: string; result: string }\n if (msg.toolCalls) {\n msg.toolCalls = msg.toolCalls.map((t: ToolCall) => {\n if (t.name === resultData.name && t.status === 'running') {\n return { ...t, result: resultData.result, status: 'success' as const }\n }\n return t\n })\n }\n break\n }\n\n case 'text_delta':\n msg.content = (msg.content || '') + (progress.data as string)\n break\n\n case 'text':\n if (!msg.content) {\n msg.content = progress.data as string\n }\n break\n\n case 'error':\n msg.content = (msg.content || '') + `\\n\\n❌ 错误: ${progress.data}`\n break\n }\n\n newMessages[index] = msg\n return newMessages\n })\n }, [])\n\n /** 发送消息 */\n const sendMessage = useCallback(async (text: string, images?: string[]) => {\n if (!text.trim() || isLoading) return\n\n // 如果没有当前会话,先创建\n let sessionId = currentSessionIdRef.current\n if (!sessionId) {\n try {\n const session = await adapter.createSession({\n title: '新对话',\n model: modelRef.current,\n mode: modeRef.current,\n })\n setSessions(prev => [session, ...prev])\n setCurrentSessionId(session.id)\n sessionId = session.id\n } catch (error) {\n console.error('创建会话失败:', error)\n return\n }\n }\n\n // 添加用户消息\n const userMsg: ChatMessage = {\n id: generateId(),\n role: 'user',\n content: text,\n images,\n timestamp: new Date(),\n }\n \n const currentMessages = messagesRef.current\n setMessages([...currentMessages, userMsg])\n\n // 保存用户消息\n try {\n await adapter.saveMessage({\n sessionId,\n role: 'user',\n content: text,\n })\n\n // 更新会话标题(如果是第一条消息)\n if (currentMessages.length === 0) {\n const title = text.slice(0, 20) + (text.length > 20 ? '...' : '')\n await adapter.updateSession(sessionId, { title })\n setSessions(prev => prev.map((s) =>\n s.id === sessionId ? { ...s, title } : s\n ))\n }\n } catch (error) {\n console.error('保存消息失败:', error)\n }\n\n // 创建助手消息\n const assistantMsgIndex = currentMessages.length + 1\n const assistantMsg: ChatMessage = {\n id: generateId(),\n role: 'assistant',\n content: '',\n toolCalls: [],\n thinkingComplete: false,\n searching: false,\n loading: true,\n timestamp: new Date(),\n }\n setMessages(prev => [...prev, assistantMsg])\n\n setIsLoading(true)\n abortControllerRef.current = new AbortController()\n\n try {\n // 使用异步迭代器接收消息流\n for await (const progress of adapter.sendMessage(\n text,\n {\n mode: modeRef.current,\n model: modelRef.current,\n enableWebSearch: webSearchRef.current,\n thinkingMode: thinkingRef.current ? 'enabled' : 'disabled',\n },\n images\n )) {\n // 检查是否被取消\n if (abortControllerRef.current?.signal.aborted) break\n\n updateMessage(assistantMsgIndex, progress)\n\n if (progress.type === 'done' || progress.type === 'error') {\n break\n }\n }\n } catch (error) {\n console.error('发送消息失败:', error)\n updateMessage(assistantMsgIndex, {\n type: 'error',\n data: error instanceof Error ? error.message : String(error),\n })\n } finally {\n setIsLoading(false)\n\n // 标记加载完成并保存\n setMessages(prev => {\n const newMessages = [...prev]\n const finalMsg = newMessages[assistantMsgIndex]\n if (finalMsg) {\n newMessages[assistantMsgIndex] = { ...finalMsg, loading: false }\n\n // 保存助手消息\n if (sessionId) {\n adapter.saveMessage({\n sessionId,\n role: 'assistant',\n content: finalMsg.content,\n thinking: finalMsg.thinking,\n toolCalls: finalMsg.toolCalls ? JSON.stringify(finalMsg.toolCalls) : undefined,\n searchResults: finalMsg.searchResults\n ? JSON.stringify(finalMsg.searchResults)\n : undefined,\n }).catch((e: Error) => console.error('保存助手消息失败:', e))\n }\n }\n return newMessages\n })\n\n abortControllerRef.current = null\n }\n }, [adapter, isLoading, updateMessage])\n\n /** 取消请求 */\n const cancelRequest = useCallback(() => {\n adapter.cancel()\n abortControllerRef.current?.abort()\n setIsLoading(false)\n }, [adapter])\n\n /** 复制消息 */\n const copyMessage = useCallback(async (messageId: string) => {\n const msg = messagesRef.current.find((m) => m.id === messageId)\n if (!msg) return\n\n try {\n await navigator.clipboard.writeText(msg.content)\n setMessages(prev => prev.map((m) =>\n m.id === messageId ? { ...m, copied: true } : m\n ))\n setTimeout(() => {\n setMessages(prev => prev.map((m) =>\n m.id === messageId ? { ...m, copied: false } : m\n ))\n }, 2000)\n } catch (err) {\n console.error('复制失败:', err)\n }\n }, [])\n\n /** 重新生成消息 */\n const regenerateMessage = useCallback((messageIndex: number) => {\n const currentMsgs = messagesRef.current\n if (messageIndex > 0 && currentMsgs[messageIndex - 1]?.role === 'user') {\n const userMsg = currentMsgs[messageIndex - 1]\n setMessages(prev => prev.slice(0, messageIndex - 1))\n sendMessage(userMsg.content, userMsg.images)\n }\n }, [sendMessage])\n\n /** 设置工作目录 */\n const setWorkingDirectory = useCallback((dir: string) => {\n if (adapter.setWorkingDir) {\n adapter.setWorkingDir(dir)\n }\n }, [adapter])\n\n // 配置方法\n const setMode = useCallback((value: ChatMode) => setModeState(value), [])\n const setModel = useCallback((value: string) => setModelState(value), [])\n const setWebSearch = useCallback((value: boolean) => setWebSearchState(value), [])\n const setThinking = useCallback((value: boolean) => setThinkingState(value), [])\n\n return {\n // 状态\n sessions,\n currentSessionId,\n messages,\n isLoading,\n mode,\n model,\n webSearch,\n thinking,\n\n // 会话方法\n loadSessions,\n switchSession,\n createNewSession,\n deleteSession,\n deleteCurrentSession,\n\n // 消息方法\n sendMessage,\n cancelRequest,\n copyMessage,\n regenerateMessage,\n\n // 配置方法\n setMode,\n setModel,\n setWebSearch,\n setThinking,\n\n // 工具方法\n setWorkingDirectory,\n }\n}\n","/**\n * ChatPanel Component\n * 与 Vue 版本 ChatPanel.vue 保持一致\n */\n\nimport { useEffect, useRef, useCallback, type FC, type ReactNode } from 'react'\nimport { useChat } from '../hooks/useChat'\nimport type { ChatAdapter, ModelConfig, ChatMode } from '@huyooo/ai-chat-bridge-electron/renderer'\nimport { DEFAULT_MODELS } from '../types'\nimport { ChatHeader } from './chat/ui/ChatHeader'\nimport { WelcomeMessage } from './chat/ui/WelcomeMessage'\nimport { MessageBubble } from './chat/messages/MessageBubble'\nimport { ChatInput } from './ChatInput'\n\ninterface ChatPanelProps {\n /** Adapter 实例 */\n adapter?: ChatAdapter\n /** 工作目录 */\n workingDir?: string\n /** 默认模型 */\n defaultModel?: string\n /** 默认模式 */\n defaultMode?: ChatMode\n /** 可用模型列表 */\n models?: ModelConfig[]\n /** 隐藏标题栏 */\n hideHeader?: boolean\n /** 关闭回调(有此属性时显示关闭按钮) */\n onClose?: () => void\n /** 自定义类名 */\n className?: string\n /** 自定义 Markdown 渲染器 */\n renderMarkdown?: (content: string) => ReactNode\n}\n\nexport const ChatPanel: FC<ChatPanelProps> = ({\n adapter,\n workingDir,\n defaultModel = 'anthropic/claude-opus-4.5',\n defaultMode = 'agent',\n models = DEFAULT_MODELS,\n hideHeader = false,\n onClose,\n className = '',\n renderMarkdown,\n}) => {\n const messagesRef = useRef<HTMLDivElement>(null)\n\n const {\n sessions,\n currentSessionId,\n messages,\n isLoading,\n mode,\n model,\n webSearch,\n thinking,\n loadSessions,\n switchSession,\n createNewSession,\n deleteSession,\n sendMessage,\n cancelRequest,\n copyMessage,\n regenerateMessage,\n setMode,\n setModel,\n setWebSearch,\n setThinking,\n setWorkingDirectory,\n } = useChat({\n adapter,\n defaultModel,\n defaultMode,\n })\n\n // 选中的图片\n // const [selectedImages, setSelectedImages] = useState<string[]>([])\n\n // 初始化\n useEffect(() => {\n loadSessions()\n }, [loadSessions])\n\n // 工作目录变化时更新\n useEffect(() => {\n if (workingDir) {\n setWorkingDirectory(workingDir)\n }\n }, [workingDir, setWorkingDirectory])\n\n // 滚动到底部\n const scrollToBottom = useCallback(() => {\n if (messagesRef.current) {\n messagesRef.current.scrollTop = messagesRef.current.scrollHeight\n }\n }, [])\n\n // 消息变化时滚动\n useEffect(() => {\n scrollToBottom()\n }, [messages, scrollToBottom])\n\n // 发送消息\n const handleSend = useCallback(\n (text: string) => {\n sendMessage(text)\n },\n [sendMessage]\n )\n\n // 快捷操作\n const handleQuickAction = useCallback(\n (text: string) => {\n sendMessage(text)\n },\n [sendMessage]\n )\n\n // 重新发送(编辑后)\n const handleResend = useCallback(\n (index: number, text: string) => {\n // 删除当前消息及后续消息,然后重新发送\n // 这里简化处理,实际需要更完善的逻辑\n sendMessage(text)\n },\n [sendMessage]\n )\n\n // 关闭\n const handleClose = useCallback(() => {\n onClose?.()\n }, [onClose])\n\n // 清空所有对话\n const handleClearAll = useCallback(() => {\n console.log('清空所有对话')\n }, [])\n\n // 关闭其他对话\n const handleCloseOthers = useCallback(() => {\n console.log('关闭其他对话')\n }, [])\n\n // 导出对话\n const handleExport = useCallback(() => {\n console.log('导出对话')\n }, [])\n\n // 复制请求 ID\n const handleCopyId = useCallback(() => {\n if (currentSessionId) {\n navigator.clipboard.writeText(currentSessionId)\n console.log('已复制请求 ID:', currentSessionId)\n }\n }, [currentSessionId])\n\n // 反馈\n const handleFeedback = useCallback(() => {\n console.log('反馈')\n }, [])\n\n // 设置\n const handleSettings = useCallback(() => {\n console.log('Agent 设置')\n }, [])\n\n return (\n <div className={`chat-panel ${className}`.trim()}>\n {/* 顶部标题栏 */}\n {!hideHeader && (\n <ChatHeader\n sessions={sessions}\n currentSessionId={currentSessionId}\n showClose={!!onClose}\n onNewSession={createNewSession}\n onSwitchSession={switchSession}\n onDeleteSession={deleteSession}\n onClose={handleClose}\n onClearAll={handleClearAll}\n onCloseOthers={handleCloseOthers}\n onExport={handleExport}\n onCopyId={handleCopyId}\n onFeedback={handleFeedback}\n onSettings={handleSettings}\n />\n )}\n\n {/* 消息列表 */}\n <div ref={messagesRef} className=\"messages-container\">\n {messages.length === 0 ? (\n <WelcomeMessage onQuickAction={handleQuickAction} />\n ) : (\n messages.map((msg, index) => (\n <MessageBubble\n key={msg.id}\n role={msg.role}\n content={msg.content}\n images={msg.images}\n thinking={msg.thinking}\n thinkingComplete={msg.thinkingComplete}\n searchResults={msg.searchResults}\n searching={msg.searching}\n toolCalls={msg.toolCalls}\n copied={msg.copied}\n loading={msg.loading}\n onCopy={() => copyMessage(msg.id)}\n onRegenerate={() => regenerateMessage(index)}\n onSend={(text) => handleResend(index, text)}\n renderMarkdown={renderMarkdown}\n />\n ))\n )}\n </div>\n\n {/* 输入区域 */}\n <ChatInput\n selectedImages={[]}\n isLoading={isLoading}\n mode={mode}\n model={model}\n models={models}\n webSearchEnabled={webSearch}\n thinkingEnabled={thinking}\n onSend={handleSend}\n onCancel={cancelRequest}\n onModeChange={setMode}\n onModelChange={setModel}\n onWebSearchChange={setWebSearch}\n onThinkingChange={setThinking}\n />\n </div>\n )\n}\n","/**\n * AI Chat 前端类型定义\n * 核心类型(ChatAdapter, SessionRecord, MessageRecord 等)从 bridge-electron 导出\n * 这里只定义前端特有的类型\n */\n\n// 从 bridge-electron 导入类型(用于本文件)\nimport type {\n ChatMode as ChatModeType,\n ModelConfig as ModelConfigType,\n ModelProvider as ModelProviderType,\n ThinkingMode as ThinkingModeType,\n SessionRecord as SessionRecordType,\n MessageRecord as MessageRecordType,\n} from '@huyooo/ai-chat-bridge-electron/renderer'\n\n// 重新导出核心类型\nexport type ChatMode = ChatModeType\nexport type ModelConfig = ModelConfigType\nexport type ModelProvider = ModelProviderType\nexport type ThinkingMode = ThinkingModeType\nexport type SessionRecord = SessionRecordType\nexport type MessageRecord = MessageRecordType\n\n/** 搜索结果 */\nexport interface SearchResult {\n title: string\n url: string\n snippet: string\n}\n\n/** 工具调用 */\nexport interface ToolCall {\n name: string\n args?: Record<string, unknown>\n result?: string\n status: 'running' | 'success' | 'error'\n}\n\n/** 聊天消息(前端显示用) */\nexport interface ChatMessage {\n id: string\n role: 'user' | 'assistant'\n content: string\n images?: string[]\n thinking?: string\n thinkingComplete?: boolean\n searchResults?: SearchResult[]\n searching?: boolean\n toolCalls?: ToolCall[]\n copied?: boolean\n loading?: boolean\n timestamp?: Date\n}\n\n/** 默认模型列表 */\nexport const DEFAULT_MODELS: ModelConfigType[] = [\n {\n provider: 'openrouter',\n model: 'anthropic/claude-opus-4.5',\n displayName: 'Claude Opus 4.5',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'doubao',\n model: 'doubao-seed-1-6-251015',\n displayName: 'Doubao Seed',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'deepseek',\n model: 'deepseek-v3-1-terminus',\n displayName: 'DeepSeek V3',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'qwen',\n model: 'qwen3-max-preview',\n displayName: 'Qwen Max',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n {\n provider: 'gemini',\n model: 'gemini-3-pro-preview',\n displayName: 'Gemini 3 Pro',\n supportsTools: true,\n supportsWebSearch: true,\n supportedThinkingModes: ['enabled', 'disabled'],\n },\n]\n\n// ================ 以下为向后兼容的类型 ================\n\n/** @deprecated 使用 SessionRecord */\nexport interface ChatSession {\n id: string\n title: string\n messages: ChatMessage[]\n createdAt: Date\n updatedAt: Date\n}\n\n/** 音视频操作类型 */\nexport interface MediaOperation {\n id: string\n type: 'clip' | 'transcode' | 'merge' | 'extract_audio' | 'add_subtitle' | 'analyze'\n description: string\n sourceFiles: string[]\n targetFile?: string\n parameters?: Record<string, unknown>\n status?: 'pending' | 'applied' | 'rejected'\n preview?: string\n}\n\n/** @deprecated 使用字符串枚举 */\nexport type AiModel = 'gemini-3-pro-preview' | 'gemini-3-pro-image-preview'\n\nexport enum FileType {\n FOLDER = 'folder',\n IMAGE = 'image',\n VIDEO = 'video',\n AUDIO = 'audio',\n TEXT = 'text',\n PDF = 'pdf',\n CODE = 'code',\n ARCHIVE = 'archive',\n OTHER = 'other'\n}\n\nexport interface DiffStat {\n file: string\n additions: number\n deletions: number\n type: 'modified' | 'added' | 'deleted'\n}\n","/**\n * ChatHeader Component\n * 与 Vue 版本 ChatHeader.vue 保持一致\n */\n\nimport { useState, useRef, useEffect, useCallback, type FC } from 'react'\nimport { Plus, Clock, MoreHorizontal, X, MessageSquare, Pencil, Trash2 } from 'lucide-react'\nimport type { SessionRecord } from '../../../types'\n\ninterface ChatHeaderProps {\n /** 当前会话列表 */\n sessions: SessionRecord[]\n /** 当前会话 ID */\n currentSessionId?: string | null\n /** 是否显示关闭按钮 */\n showClose?: boolean\n /** 创建新会话 */\n onNewSession?: () => void\n /** 切换会话 */\n onSwitchSession?: (sessionId: string) => void\n /** 删除会话 */\n onDeleteSession?: (sessionId: string) => void\n /** 关闭面板 */\n onClose?: () => void\n /** 清空所有对话 */\n onClearAll?: () => void\n /** 关闭其他对话 */\n onCloseOthers?: () => void\n /** 导出对话 */\n onExport?: () => void\n /** 复制请求 ID */\n onCopyId?: () => void\n /** 反馈 */\n onFeedback?: () => void\n /** Agent 设置 */\n onSettings?: () => void\n}\n\n/** 格式化时间 */\nfunction formatTime(date: Date | string | undefined): string {\n if (!date) return ''\n const d = new Date(date)\n const now = new Date()\n const diff = now.getTime() - d.getTime()\n const days = Math.floor(diff / (1000 * 60 * 60 * 24))\n \n if (days === 0) {\n return d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })\n } else if (days === 1) {\n return '昨天'\n } else if (days < 7) {\n return `${days}天前`\n } else {\n return d.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })\n }\n}\n\nexport const ChatHeader: FC<ChatHeaderProps> = ({\n sessions,\n currentSessionId,\n showClose = false,\n onNewSession,\n onSwitchSession,\n onDeleteSession,\n onClose,\n onClearAll,\n onCloseOthers,\n onExport,\n onCopyId,\n onFeedback,\n onSettings,\n}) => {\n const [historyOpen, setHistoryOpen] = useState(false)\n const [moreMenuOpen, setMoreMenuOpen] = useState(false)\n const [hiddenTabs, setHiddenTabs] = useState<Set<string>>(new Set())\n\n const historyRef = useRef<HTMLDivElement>(null)\n const moreMenuRef = useRef<HTMLDivElement>(null)\n\n // 可见的会话\n const visibleSessions = sessions.filter((s) => !hiddenTabs.has(s.id))\n\n // 点击外部关闭菜单\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as HTMLElement\n if (historyRef.current && !historyRef.current.contains(target)) {\n setHistoryOpen(false)\n }\n if (moreMenuRef.current && !moreMenuRef.current.contains(target)) {\n setMoreMenuOpen(false)\n }\n }\n\n document.addEventListener('click', handleClickOutside)\n return () => document.removeEventListener('click', handleClickOutside)\n }, [])\n\n // 切换会话\n const handleSwitchSession = useCallback(\n (sessionId: string) => {\n onSwitchSession?.(sessionId)\n setHistoryOpen(false)\n },\n [onSwitchSession]\n )\n\n // 隐藏 tab\n const handleHideTab = useCallback(\n (sessionId: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setHiddenTabs((prev) => new Set([...prev, sessionId]))\n if (sessionId === currentSessionId) {\n const remaining = sessions.filter((s) => s.id !== sessionId && !hiddenTabs.has(s.id))\n if (remaining.length > 0) {\n onSwitchSession?.(remaining[0].id)\n }\n }\n },\n [currentSessionId, sessions, hiddenTabs, onSwitchSession]\n )\n\n // 删除会话\n const handleDeleteSession = (sessionId: string, e: React.MouseEvent) => {\n e.stopPropagation()\n if (window.confirm('确定要删除这个对话吗?')) {\n onDeleteSession?.(sessionId)\n }\n }\n\n // 菜单项点击\n const handleMenuClick = (callback?: () => void) => {\n callback?.()\n setMoreMenuOpen(false)\n }\n\n return (\n <div className=\"chat-header\">\n {/* 左侧:Tabs */}\n <div className=\"chat-tabs\">\n {visibleSessions.length === 0 ? (\n <span className=\"chat-tab active\">\n <span className=\"chat-tab-title\">New Chat</span>\n </span>\n ) : (\n visibleSessions.map((session) => {\n const title = session.title === '新对话' ? 'New Chat' : session.title\n const isActive = session.id === currentSessionId\n return (\n <div\n key={session.id}\n className={`chat-tab${isActive ? ' active' : ''}`}\n onClick={() => handleSwitchSession(session.id)}\n title={session.title}\n >\n <span className=\"chat-tab-title\">{title}</span>\n <span\n className=\"chat-tab-close\"\n onClick={(e) => handleHideTab(session.id, e)}\n title=\"关闭标签\"\n >\n <X size={12} />\n </span>\n </div>\n )\n })\n )}\n </div>\n\n {/* 右侧:操作按钮 */}\n <div className=\"chat-header-actions\">\n {/* 新建会话 */}\n <button className=\"header-btn\" onClick={onNewSession} title=\"新建对话\">\n <Plus size={14} />\n </button>\n\n {/* 历史记录 */}\n <div ref={historyRef} style={{ position: 'relative' }}>\n <button\n className={`header-btn${historyOpen ? ' active' : ''}`}\n onClick={(e) => {\n e.stopPropagation()\n setHistoryOpen(!historyOpen)\n setMoreMenuOpen(false)\n }}\n title=\"历史记录\"\n >\n <Clock size={14} />\n </button>\n\n {/* 历史记录面板 */}\n {historyOpen && (\n <div className=\"history-panel\">\n {sessions.length === 0 ? (\n <div className=\"history-empty\">暂无历史对话</div>\n ) : (\n sessions.map((session) => {\n const isCurrent = session.id === currentSessionId\n return (\n <div\n key={session.id}\n className={`history-item${isCurrent ? ' active' : ''}`}\n >\n <button\n className=\"history-item-content\"\n onClick={() => handleSwitchSession(session.id)}\n >\n <MessageSquare size={12} />\n <span className=\"history-item-title\">\n {session.title === '新对话' ? 'New Chat' : session.title}\n </span>\n <span className=\"history-item-time\">\n {isCurrent ? 'Current' : formatTime(session.updatedAt)}\n </span>\n </button>\n <div className=\"history-item-actions\">\n <button className=\"history-action-btn\" title=\"编辑\">\n <Pencil size={10} />\n </button>\n <button\n className=\"history-action-btn delete\"\n title=\"删除\"\n onClick={(e) => handleDeleteSession(session.id, e)}\n >\n <Trash2 size={10} />\n </button>\n </div>\n </div>\n )\n })\n )}\n </div>\n )}\n </div>\n\n {/* 更多选项 */}\n <div ref={moreMenuRef} style={{ position: 'relative' }}>\n <button\n className={`header-btn${moreMenuOpen ? ' active' : ''}`}\n onClick={(e) => {\n e.stopPropagation()\n setMoreMenuOpen(!moreMenuOpen)\n setHistoryOpen(false)\n }}\n title=\"更多选项\"\n >\n <MoreHorizontal size={14} />\n </button>\n\n {/* 更多选项菜单 */}\n {moreMenuOpen && (\n <div className=\"more-menu\">\n {showClose && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onClose)}>\n <span>关闭对话</span>\n <span className=\"menu-shortcut\">⌘ W</span>\n </button>\n )}\n {onClearAll && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onClearAll)}>\n 清空所有对话\n </button>\n )}\n {onCloseOthers && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onCloseOthers)}>\n 关闭其他对话\n </button>\n )}\n \n {(showClose || onClearAll || onCloseOthers) && <div className=\"menu-divider\" />}\n \n {onExport && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onExport)}>\n 导出对话\n </button>\n )}\n {onCopyId && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onCopyId)}>\n 复制请求 ID\n </button>\n )}\n {onFeedback && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onFeedback)}>\n 反馈\n </button>\n )}\n \n {(onExport || onCopyId || onFeedback) && onSettings && <div className=\"menu-divider\" />}\n \n {onSettings && (\n <button className=\"menu-item\" onClick={() => handleMenuClick(onSettings)}>\n Agent 设置\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n","/**\n * WelcomeMessage Component\n * 与 Vue 版本 WelcomeMessage.vue 保持一致\n */\n\nimport { type FC } from 'react'\nimport { Wand2, ImageIcon, Video, Terminal } from 'lucide-react'\n\ninterface WelcomeMessageProps {\n /** 快捷操作回调 */\n onQuickAction: (text: string) => void\n}\n\n/** 快捷操作配置 */\nconst QUICK_ACTIONS = [\n {\n id: 'txt2img',\n Icon: Wand2,\n label: '文生图',\n desc: 'AI 绘制创意图像',\n prompt: '帮我生成一张图片:',\n gradient: 'purple',\n iconColor: 'purple',\n featured: true,\n },\n {\n id: 'img2img',\n Icon: ImageIcon,\n label: '图生图',\n desc: '风格迁移',\n prompt: '基于这张图片进行风格转换',\n gradient: 'blue',\n iconColor: 'blue',\n featured: false,\n },\n {\n id: 'img2vid',\n Icon: Video,\n label: '图生视频',\n desc: '动态化',\n prompt: '将这张图片转换成视频',\n gradient: 'emerald',\n iconColor: 'emerald',\n featured: false,\n },\n {\n id: 'cmd',\n Icon: Terminal,\n label: '执行命令',\n desc: '系统管理',\n prompt: '执行命令:',\n gradient: 'orange',\n iconColor: 'orange',\n featured: true,\n },\n]\n\nexport const WelcomeMessage: FC<WelcomeMessageProps> = ({ onQuickAction }) => {\n return (\n <div className=\"welcome-message\">\n {/* 动态极光背景 */}\n <div className=\"welcome-glow purple\" />\n <div className=\"welcome-glow blue\" />\n\n {/* 标题区域 */}\n <div className=\"welcome-title-area\">\n <h1 className=\"welcome-title\">\n Create\n <br />\n <span className=\"welcome-title-accent\">Everything</span>\n </h1>\n <p className=\"welcome-subtitle\">释放 AI 的无限创造力</p>\n </div>\n\n {/* 快捷操作网格 */}\n <div className=\"quick-actions\">\n {QUICK_ACTIONS.map((action) => (\n <button\n key={action.id}\n className={`quick-action-btn${action.featured ? ' featured' : ''}`}\n onClick={() => onQuickAction(action.prompt)}\n >\n {/* 卡片背景渐变 */}\n <div className={`quick-action-gradient ${action.gradient}`} />\n\n {/* 图标 */}\n <action.Icon className={`quick-action-icon ${action.iconColor}`} />\n\n {/* 文字 */}\n <div className=\"quick-action-text\">\n <span className=\"quick-action-label\">{action.label}</span>\n <span className=\"quick-action-desc\">{action.desc}</span>\n </div>\n\n {/* 装饰性光斑 */}\n <div className=\"quick-action-glow\" />\n </button>\n ))}\n </div>\n\n {/* 底部装饰 */}\n <div className=\"welcome-footer\">\n <div className=\"welcome-footer-line\" />\n </div>\n </div>\n )\n}\n","/**\n * MessageBubble Component\n * 与 Vue 版本 MessageBubble.vue 保持一致\n */\n\nimport { type FC, type ReactNode } from 'react'\nimport { Copy, Check, RefreshCw } from 'lucide-react'\nimport { ExecutionSteps } from './ExecutionSteps'\nimport { ChatInput } from '../../ChatInput'\nimport type { SearchResult, ToolCall } from '../../../types'\n\ninterface MessageBubbleProps {\n role: 'user' | 'assistant'\n content: string\n images?: string[]\n thinking?: string\n thinkingComplete?: boolean\n thinkingDuration?: number\n searchResults?: SearchResult[]\n searching?: boolean\n toolCalls?: ToolCall[]\n copied?: boolean\n loading?: boolean\n onCopy?: () => void\n onRegenerate?: () => void\n /** 编辑用户消息后重新发送 */\n onSend?: (text: string) => void\n /** 自定义 Markdown 渲染器 */\n renderMarkdown?: (content: string) => ReactNode\n}\n\n/** 默认 Markdown 渲染(简单处理) */\nfunction defaultRenderMarkdown(content: string): ReactNode {\n // 简单的 Markdown 处理:代码块\n const parts = content.split(/(```[\\s\\S]*?```)/g)\n \n return parts.map((part, i) => {\n if (part.startsWith('```') && part.endsWith('```')) {\n const code = part.slice(3, -3)\n const firstLine = code.indexOf('\\n')\n const lang = firstLine > 0 ? code.slice(0, firstLine).trim() : ''\n const codeContent = firstLine > 0 ? code.slice(firstLine + 1) : code\n return (\n <pre key={i}>\n <code>{codeContent}</code>\n </pre>\n )\n }\n // 处理行内代码\n const inlineParts = part.split(/(`[^`]+`)/g)\n return (\n <span key={i}>\n {inlineParts.map((p, j) => {\n if (p.startsWith('`') && p.endsWith('`')) {\n return <code key={j}>{p.slice(1, -1)}</code>\n }\n return p\n })}\n </span>\n )\n })\n}\n\nexport const MessageBubble: FC<MessageBubbleProps> = ({\n role,\n content,\n images,\n thinking,\n thinkingComplete = true,\n thinkingDuration,\n searchResults,\n searching,\n toolCalls,\n copied,\n loading,\n onCopy,\n onRegenerate,\n onSend,\n renderMarkdown = defaultRenderMarkdown,\n}) => {\n const isUser = role === 'user'\n\n return (\n <div className=\"message-bubble\">\n {/* 用户消息 - 复用 ChatInput 组件 */}\n {isUser ? (\n <ChatInput\n variant=\"message\"\n value={content}\n selectedImages={images}\n onSend={onSend}\n />\n ) : (\n /* AI 消息 */\n <div className=\"assistant-message\">\n {/* 执行步骤列表 */}\n <ExecutionSteps\n loading={loading}\n hasContent={!!content}\n thinking={thinking}\n thinkingComplete={thinkingComplete}\n thinkingDuration={thinkingDuration}\n searching={searching}\n searchResults={searchResults}\n toolCalls={toolCalls}\n />\n\n {/* 消息内容 */}\n {content && (\n <div className=\"message-content\">\n {renderMarkdown(content)}\n </div>\n )}\n\n {/* 操作按钮 */}\n {content && !loading && (\n <div className=\"message-actions\">\n <button className={`action-btn${copied ? ' copied' : ''}`} onClick={onCopy} title=\"复制\">\n {copied ? <Check size={14} /> : <Copy size={14} />}\n </button>\n <button className=\"action-btn\" onClick={onRegenerate} title=\"重新生成\">\n <RefreshCw size={14} />\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n","/**\n * ExecutionSteps Component\n * 与 Vue 版本 ExecutionSteps.vue 保持一致\n */\n\nimport { useState, type FC, type ReactNode } from 'react'\nimport {\n ChevronDown,\n ChevronUp,\n Sparkles,\n Globe,\n FileText,\n FileEdit,\n Terminal,\n Search,\n Folder,\n FolderPlus,\n Trash2,\n Image,\n Video,\n Wrench,\n ExternalLink,\n} from 'lucide-react'\nimport type { SearchResult, ToolCall } from '../../../types'\n\ninterface ExecutionStepsProps {\n /** 是否正在加载 */\n loading?: boolean\n /** 是否有消息内容 */\n hasContent?: boolean\n /** 思考内容 */\n thinking?: string\n /** 思考是否完成 */\n thinkingComplete?: boolean\n /** 思考耗时 */\n thinkingDuration?: number\n /** 是否正在搜索 */\n searching?: boolean\n /** 搜索结果 */\n searchResults?: SearchResult[]\n /** 工具调用列表 */\n toolCalls?: ToolCall[]\n}\n\n/** 步骤项组件 */\ninterface StepItemProps {\n icon: ReactNode\n title: string\n status: 'running' | 'completed' | 'error'\n extra?: string\n detail?: ReactNode\n defaultExpanded?: boolean\n}\n\nconst StepItem: FC<StepItemProps> = ({\n icon,\n title,\n status,\n extra,\n detail,\n defaultExpanded = false,\n}) => {\n const [expanded, setExpanded] = useState(defaultExpanded)\n const isRunning = status === 'running'\n const hasDetail = !!detail\n\n return (\n <div className=\"step-item\">\n <button\n className={`step-header${isRunning ? ' running' : ''}`}\n onClick={() => hasDetail && setExpanded(!expanded)}\n disabled={!hasDetail}\n style={{ cursor: hasDetail ? 'pointer' : 'default' }}\n >\n <span className={`step-icon${isRunning ? ' pulse' : ''}`}>{icon}</span>\n <span className=\"step-title\">{title}</span>\n {hasDetail && (\n expanded ? <ChevronUp className=\"step-chevron\" size={12} /> : <ChevronDown className=\"step-chevron\" size={12} />\n )}\n {extra && <span className=\"step-extra\">{extra}</span>}\n </button>\n\n {expanded && detail && (\n <div className=\"step-detail\">\n {typeof detail === 'string' ? <pre>{detail}</pre> : detail}\n </div>\n )}\n </div>\n )\n}\n\n/** 获取工具调用的显示名称 */\nfunction getToolDisplayName(name: string): string {\n const nameMap: Record<string, string> = {\n read_file: '读取文件',\n write_file: '写入文件',\n execute_command: '执行命令',\n search_files: '搜索文件',\n list_directory: '列出目录',\n create_directory: '创建目录',\n delete_file: '删除文件',\n web_search: '网页搜索',\n generate_image: '生成图片',\n image_to_video: '图片转视频',\n }\n return nameMap[name] || name\n}\n\n/** 获取工具调用的图标 */\nfunction getToolIcon(name: string) {\n switch (name) {\n case 'read_file': return <FileText size={14} />\n case 'write_file': return <FileEdit size={14} />\n case 'execute_command': return <Terminal size={14} />\n case 'search_files': return <Search size={14} />\n case 'list_directory': return <Folder size={14} />\n case 'create_directory': return <FolderPlus size={14} />\n case 'delete_file': return <Trash2 size={14} />\n case 'web_search': return <Globe size={14} />\n case 'generate_image': return <Image size={14} />\n case 'image_to_video': return <Video size={14} />\n default: return <Wrench size={14} />\n }\n}\n\n/** 格式化工具调用参数 */\nfunction formatToolArgs(args?: Record<string, unknown>): string {\n if (!args) return ''\n return Object.entries(args)\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join('\\n')\n}\n\nexport const ExecutionSteps: FC<ExecutionStepsProps> = ({\n loading,\n hasContent,\n thinking,\n thinkingComplete = true,\n thinkingDuration,\n searching,\n searchResults,\n toolCalls,\n}) => {\n // 判断是否有任何执行步骤\n const hasSteps =\n thinking ||\n searching ||\n (searchResults && searchResults.length > 0) ||\n (toolCalls && toolCalls.length > 0) ||\n (loading && !hasContent)\n\n if (!hasSteps) return null\n\n return (\n <div className=\"execution-steps\">\n {/* 正在规划 */}\n {loading && !hasContent && !thinking && !searching && (!toolCalls || toolCalls.length === 0) && (\n <StepItem\n icon={<Sparkles size={14} />}\n title=\"正在规划下一步...\"\n status=\"running\"\n />\n )}\n\n {/* 思考过程 */}\n {thinking && (\n <StepItem\n icon={<Sparkles size={14} />}\n title={thinkingComplete ? '思考完成' : '思考中...'}\n status={thinkingComplete ? 'completed' : 'running'}\n extra={thinkingDuration ? `${thinkingDuration}s` : undefined}\n detail={thinking}\n />\n )}\n\n {/* 搜索 */}\n {(searching || (searchResults && searchResults.length > 0)) && (\n <StepItem\n icon={<Globe size={14} />}\n title={searching ? '搜索中...' : `搜索完成 ${searchResults?.length || 0} 条结果`}\n status={searching ? 'running' : 'completed'}\n detail={\n searchResults && searchResults.length > 0 ? (\n <div>\n {searchResults.map((result, i) => (\n <a\n key={i}\n href={result.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"search-result-item\"\n >\n <div className=\"search-result-title\">\n <span>{result.title}</span>\n <ExternalLink size={12} style={{ opacity: 0.5 }} />\n </div>\n <div className=\"search-result-snippet\">{result.snippet}</div>\n </a>\n ))}\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* 工具调用 */}\n {toolCalls &&\n toolCalls.map((call, index) => (\n <StepItem\n key={`tool-${index}`}\n icon={getToolIcon(call.name)}\n title={`${getToolDisplayName(call.name)}${call.status === 'running' ? '...' : ''}`}\n status={call.status === 'running' ? 'running' : call.status === 'error' ? 'error' : 'completed'}\n detail={\n <div>\n {call.args && (\n <div style={{ marginBottom: call.result ? 8 : 0 }}>\n <div style={{ fontSize: 10, color: 'var(--chat-text-muted)', marginBottom: 4 }}>参数</div>\n <pre style={{ margin: 0 }}>{formatToolArgs(call.args)}</pre>\n </div>\n )}\n {call.result && (\n <div>\n <div style={{ fontSize: 10, color: 'var(--chat-text-muted)', marginBottom: 4 }}>结果</div>\n <pre style={{ margin: 0, maxHeight: 160, overflow: 'auto' }}>{call.result}</pre>\n </div>\n )}\n </div>\n }\n />\n ))}\n </div>\n )\n}\n","/**\n * ChatInput Component\n * 与 Vue 版本 ChatInput.vue 保持一致\n */\n\nimport { useState, useRef, useCallback, useEffect, type FC } from 'react'\nimport {\n X,\n ChevronDown,\n Check,\n Globe,\n Sparkles,\n ImageIcon,\n Square,\n ArrowUp,\n Zap,\n MessageCircle,\n AtSign,\n Mic,\n} from 'lucide-react'\nimport type { ChatMode, ModelConfig } from '../types'\nimport { DEFAULT_MODELS } from '../types'\n\ninterface ChatInputProps {\n /** 变体模式:input-底部输入框,message-历史消息 */\n variant?: 'input' | 'message'\n /** 受控值(用于历史消息编辑) */\n value?: string\n selectedImages?: string[]\n isLoading?: boolean\n mode?: ChatMode\n model?: string\n models?: ModelConfig[]\n webSearchEnabled?: boolean\n thinkingEnabled?: boolean\n onSend?: (text: string) => void\n onRemoveImage?: (index: number) => void\n onCancel?: () => void\n onUploadImage?: () => void\n onAtContext?: () => void\n onModeChange?: (mode: ChatMode) => void\n onModelChange?: (model: string) => void\n onWebSearchChange?: (enabled: boolean) => void\n onThinkingChange?: (enabled: boolean) => void\n}\n\n/** 模式配置 */\nconst MODES = [\n { value: 'agent' as const, label: 'Agent', Icon: Zap },\n { value: 'ask' as const, label: 'Ask', Icon: MessageCircle },\n]\n\nexport const ChatInput: FC<ChatInputProps> = ({\n variant = 'input',\n value = '',\n selectedImages = [],\n isLoading = false,\n mode = 'agent',\n model = '',\n models = DEFAULT_MODELS,\n webSearchEnabled = false,\n thinkingEnabled = false,\n onSend,\n onRemoveImage,\n onCancel,\n onUploadImage,\n onAtContext,\n onModeChange,\n onModelChange,\n onWebSearchChange,\n onThinkingChange,\n}) => {\n const isMessageVariant = variant === 'message'\n\n const [inputText, setInputText] = useState(value)\n const [isFocused, setIsFocused] = useState(false)\n const [modeMenuOpen, setModeMenuOpen] = useState(false)\n const [modelMenuOpen, setModelMenuOpen] = useState(false)\n\n const inputRef = useRef<HTMLTextAreaElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n // 同步外部 value\n useEffect(() => {\n setInputText(value)\n }, [value])\n\n const currentMode = MODES.find((m) => m.value === mode) || MODES[0]\n const currentModel = models.find((m) => m.model === model)\n\n // 是否显示工具栏\n const showToolbar = !isMessageVariant || isFocused\n\n // 预览\n const selectedPreview = selectedImages.slice(0, 3)\n\n // 占位符\n const placeholder = selectedImages.length > 0\n ? '描述你想要的效果...'\n : mode === 'ask'\n ? '有什么问题想问我?'\n : '描述任务,@ 添加上下文'\n\n // 自动调整高度\n const adjustTextareaHeight = useCallback(() => {\n if (inputRef.current) {\n inputRef.current.style.height = 'auto'\n const scrollHeight = inputRef.current.scrollHeight\n inputRef.current.style.height = `${Math.min(scrollHeight, 150)}px`\n }\n }, [])\n\n // 发送或取消\n const handleSendOrCancel = useCallback(() => {\n if (isLoading) {\n onCancel?.()\n return\n }\n\n const text = inputText.trim()\n if (!text) return\n\n onSend?.(text)\n\n if (!isMessageVariant) {\n setInputText('')\n if (inputRef.current) {\n inputRef.current.style.height = 'auto'\n }\n inputRef.current?.focus()\n } else {\n setIsFocused(false)\n }\n }, [isLoading, inputText, onSend, onCancel, isMessageVariant])\n\n // 键盘事件\n const handleKeydown = useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault()\n handleSendOrCancel()\n } else {\n setTimeout(adjustTextareaHeight, 0)\n }\n },\n [handleSendOrCancel, adjustTextareaHeight]\n )\n\n // 选择模式\n const selectMode = useCallback(\n (value: ChatMode) => {\n onModeChange?.(value)\n setModeMenuOpen(false)\n },\n [onModeChange]\n )\n\n // 选择模型\n const selectModel = useCallback(\n (m: ModelConfig) => {\n onModelChange?.(m.model)\n setModelMenuOpen(false)\n },\n [onModelChange]\n )\n\n // 图片 URL 处理\n const getImageUrl = (path: string): string => {\n if (\n path.startsWith('app://') ||\n path.startsWith('file://') ||\n path.startsWith('data:') ||\n path.startsWith('http')\n ) {\n return path\n }\n if (path.match(/^[A-Z]:\\\\/i)) {\n return `app://file${encodeURIComponent(path.replace(/\\\\/g, '/'))}`\n }\n return `app://file${encodeURIComponent(path)}`\n }\n\n // 点击外部关闭菜单\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as HTMLElement\n if (!target.closest('.selector')) {\n setModeMenuOpen(false)\n setModelMenuOpen(false)\n }\n if (\n isMessageVariant &&\n containerRef.current &&\n !containerRef.current.contains(target)\n ) {\n setIsFocused(false)\n }\n }\n\n document.addEventListener('click', handleClickOutside)\n return () => document.removeEventListener('click', handleClickOutside)\n }, [isMessageVariant])\n\n const CurrentModeIcon = currentMode.Icon\n\n return (\n <div className={`chat-input${isMessageVariant ? ' message-variant' : ''}`}>\n <div\n ref={containerRef}\n className={`input-container${isFocused ? ' focused' : ''}`}\n >\n {/* 附件预览 */}\n {selectedImages.length > 0 && (\n <div className=\"attachment-preview\">\n <div className=\"preview-images\">\n {selectedPreview.map((img, index) => (\n <div key={`${img}-${index}`} className=\"preview-item\">\n <img\n src={getImageUrl(img)}\n className=\"preview-thumb\"\n alt={`附件 ${index + 1}`}\n onError={(e) => {\n (e.target as HTMLImageElement).style.display = 'none'\n }}\n />\n {!isMessageVariant && (\n <button\n className=\"remove-btn\"\n title={`移除图片 ${index + 1}`}\n onClick={() => onRemoveImage?.(index)}\n >\n <X size={10} />\n </button>\n )}\n </div>\n ))}\n {selectedImages.length > 3 && (\n <div className=\"preview-more\">+{selectedImages.length - 3}</div>\n )}\n </div>\n </div>\n )}\n\n {/* 输入框 */}\n <div className=\"input-field-wrapper\">\n <textarea\n ref={inputRef}\n value={inputText}\n onChange={(e) => setInputText(e.target.value)}\n onKeyDown={handleKeydown}\n onInput={adjustTextareaHeight}\n onFocus={() => setIsFocused(true)}\n placeholder={placeholder}\n rows={1}\n className=\"input-field\"\n />\n </div>\n\n {/* 底部控制栏 */}\n {showToolbar && (\n <div className=\"input-controls\">\n {/* 左侧:模式和模型选择 */}\n <div className=\"input-left\">\n {/* 模式选择 */}\n <div\n className=\"selector mode-selector\"\n onClick={(e) => {\n e.stopPropagation()\n setModeMenuOpen(!modeMenuOpen)\n setModelMenuOpen(false)\n }}\n >\n <CurrentModeIcon size={12} />\n <span>{currentMode.label}</span>\n <ChevronDown size={10} className=\"chevron\" />\n\n {modeMenuOpen && (\n <div className=\"dropdown-menu\" onClick={(e) => e.stopPropagation()}>\n {MODES.map((m) => (\n <button\n key={m.value}\n className={`dropdown-item${mode === m.value ? ' active' : ''}`}\n onClick={() => selectMode(m.value)}\n >\n <m.Icon size={14} />\n <span>{m.label}</span>\n {mode === m.value && <Check size={14} className=\"check-icon\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* 模型选择 */}\n <div\n className=\"selector model-selector\"\n onClick={(e) => {\n e.stopPropagation()\n setModelMenuOpen(!modelMenuOpen)\n setModeMenuOpen(false)\n }}\n >\n <span>{currentModel?.displayName || 'Auto'}</span>\n <ChevronDown size={10} className=\"chevron\" />\n\n {modelMenuOpen && (\n <div className=\"dropdown-menu\" onClick={(e) => e.stopPropagation()}>\n {models.map((m) => (\n <button\n key={m.model}\n className={`dropdown-item${model === m.model ? ' active' : ''}`}\n onClick={() => selectModel(m)}\n >\n <span>{m.displayName}</span>\n {model === m.model && <Check size={14} className=\"check-icon\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* 右侧:功能按钮 */}\n <div className=\"input-right\">\n <button className=\"icon-btn\" title=\"提及上下文 (@)\" onClick={onAtContext}>\n <AtSign size={14} />\n </button>\n\n <button\n className={`toggle-btn${thinkingEnabled ? ' active' : ''}`}\n title=\"深度思考\"\n onClick={() => onThinkingChange?.(!thinkingEnabled)}\n >\n <Sparkles size={14} />\n </button>\n\n <button\n className={`toggle-btn${webSearchEnabled ? ' active' : ''}`}\n title=\"联网搜索\"\n onClick={() => onWebSearchChange?.(!webSearchEnabled)}\n >\n <Globe size={14} />\n </button>\n\n <button className=\"icon-btn\" title=\"上传图片\" onClick={onUploadImage}>\n <ImageIcon size={14} />\n </button>\n\n {inputText.trim() || isLoading ? (\n <button\n className={`send-btn${isLoading ? ' loading' : ''}`}\n title={isLoading ? '停止' : isMessageVariant ? '重新发送' : '发送'}\n onClick={handleSendOrCancel}\n >\n {isLoading ? <Square size={14} /> : <ArrowUp size={14} />}\n </button>\n ) : (\n <button className=\"icon-btn\" title=\"语音输入\">\n <Mic size={14} />\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useRef","useCallback","FileType","useState","useRef","useCallback","jsx","jsxs","Check","useState","Terminal","Trash2","Video","jsx","jsxs","useState","useRef","useCallback","useEffect","X","ChevronDown","Globe","Sparkles","ImageIcon","jsx","jsxs","useState","useRef","useEffect","useCallback","value","X","ChevronDown","Sparkles","Globe","ImageIcon","jsx","jsxs","Check","jsx","jsxs","useRef","useEffect","useCallback"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huyooo/ai-chat-frontend-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "AI Chat Frontend - React components with adapter pattern",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"react-dom": ">=18.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"@huyooo/ai-chat-bridge-electron": "^0.1.3",
|
|
33
34
|
"lucide-react": "^0.460.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
package/src/adapter.ts
CHANGED
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Chat Adapter
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* 与 Vue 版本保持一致
|
|
2
|
+
* Chat Adapter 辅助类型和工具
|
|
3
|
+
* 核心 ChatAdapter 接口从 bridge-electron 导入
|
|
6
4
|
*/
|
|
7
5
|
|
|
8
|
-
import type {
|
|
9
|
-
|
|
10
|
-
/** 聊天进度类型 */
|
|
11
|
-
export type ChatProgressType =
|
|
12
|
-
| 'thinking'
|
|
13
|
-
| 'search_start'
|
|
14
|
-
| 'search_result'
|
|
15
|
-
| 'tool_call'
|
|
16
|
-
| 'tool_result'
|
|
17
|
-
| 'text_delta'
|
|
18
|
-
| 'text'
|
|
19
|
-
| 'done'
|
|
20
|
-
| 'error'
|
|
6
|
+
import type { ChatAdapter, ChatMode, ThinkingMode } from '@huyooo/ai-chat-bridge-electron/renderer'
|
|
7
|
+
import type { SearchResult } from './types'
|
|
21
8
|
|
|
22
9
|
/** 思考数据 */
|
|
23
10
|
export interface ThinkingData {
|
|
@@ -43,12 +30,6 @@ export interface ImageData {
|
|
|
43
30
|
mimeType: string
|
|
44
31
|
}
|
|
45
32
|
|
|
46
|
-
/** 聊天进度事件 */
|
|
47
|
-
export interface ChatProgress {
|
|
48
|
-
type: ChatProgressType
|
|
49
|
-
data: string | ThinkingData | ToolCallData | ToolResultData | { results: SearchResult[] }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
33
|
/** 发送消息选项 */
|
|
53
34
|
export interface SendMessageOptions {
|
|
54
35
|
mode: ChatMode
|
|
@@ -81,78 +62,63 @@ export interface SaveMessageOptions {
|
|
|
81
62
|
searchResults?: string
|
|
82
63
|
}
|
|
83
64
|
|
|
84
|
-
/**
|
|
85
|
-
* Chat Adapter 接口
|
|
86
|
-
* 所有后端通信实现都需要实现此接口
|
|
87
|
-
*/
|
|
88
|
-
export interface ChatAdapter {
|
|
89
|
-
/** 获取所有会话 */
|
|
90
|
-
getSessions(): Promise<SessionRecord[]>
|
|
91
|
-
|
|
92
|
-
/** 创建新会话 */
|
|
93
|
-
createSession(options: CreateSessionOptions): Promise<SessionRecord>
|
|
94
|
-
|
|
95
|
-
/** 更新会话 */
|
|
96
|
-
updateSession(sessionId: string, options: UpdateSessionOptions): Promise<void>
|
|
97
|
-
|
|
98
|
-
/** 删除会话 */
|
|
99
|
-
deleteSession(sessionId: string): Promise<void>
|
|
100
|
-
|
|
101
|
-
/** 获取会话消息 */
|
|
102
|
-
getMessages(sessionId: string): Promise<MessageRecord[]>
|
|
103
|
-
|
|
104
|
-
/** 保存消息 */
|
|
105
|
-
saveMessage(options: SaveMessageOptions): Promise<MessageRecord>
|
|
106
|
-
|
|
107
|
-
/** 发送消息并获取流式响应 */
|
|
108
|
-
sendMessage(
|
|
109
|
-
content: string,
|
|
110
|
-
options: SendMessageOptions,
|
|
111
|
-
images?: string[]
|
|
112
|
-
): AsyncGenerator<ChatProgress, void, unknown>
|
|
113
|
-
|
|
114
|
-
/** 取消当前请求 */
|
|
115
|
-
cancel(): void
|
|
116
|
-
|
|
117
|
-
/** 设置工作目录 */
|
|
118
|
-
setWorkingDir?(dir: string): void
|
|
119
|
-
}
|
|
120
|
-
|
|
121
65
|
/**
|
|
122
66
|
* 创建空 Adapter(用于测试或无后端场景)
|
|
67
|
+
* 返回一个最小实现的 ChatAdapter
|
|
123
68
|
*/
|
|
124
69
|
export function createNullAdapter(): ChatAdapter {
|
|
125
70
|
return {
|
|
71
|
+
async getModels() {
|
|
72
|
+
return []
|
|
73
|
+
},
|
|
126
74
|
async getSessions() {
|
|
127
75
|
return []
|
|
128
76
|
},
|
|
129
|
-
async
|
|
77
|
+
async getSession() {
|
|
78
|
+
return null
|
|
79
|
+
},
|
|
80
|
+
async createSession(params) {
|
|
130
81
|
return {
|
|
131
82
|
id: Date.now().toString(),
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
83
|
+
appId: null,
|
|
84
|
+
userId: null,
|
|
85
|
+
title: params?.title || '新对话',
|
|
86
|
+
model: params?.model || '',
|
|
87
|
+
mode: (params?.mode || 'agent') as ChatMode,
|
|
135
88
|
createdAt: new Date(),
|
|
136
89
|
updatedAt: new Date(),
|
|
137
90
|
}
|
|
138
91
|
},
|
|
139
|
-
async updateSession() {
|
|
92
|
+
async updateSession() {
|
|
93
|
+
return null
|
|
94
|
+
},
|
|
140
95
|
async deleteSession() {},
|
|
141
96
|
async getMessages() {
|
|
142
97
|
return []
|
|
143
98
|
},
|
|
144
|
-
async saveMessage(
|
|
99
|
+
async saveMessage(params) {
|
|
145
100
|
return {
|
|
146
101
|
id: Date.now().toString(),
|
|
147
|
-
sessionId:
|
|
148
|
-
role:
|
|
149
|
-
content:
|
|
150
|
-
thinking:
|
|
151
|
-
toolCalls:
|
|
152
|
-
searchResults:
|
|
102
|
+
sessionId: params.sessionId,
|
|
103
|
+
role: params.role,
|
|
104
|
+
content: params.content,
|
|
105
|
+
thinking: params.thinking || null,
|
|
106
|
+
toolCalls: params.toolCalls || null,
|
|
107
|
+
searchResults: params.searchResults || null,
|
|
108
|
+
operationIds: null,
|
|
153
109
|
timestamp: new Date(),
|
|
154
110
|
}
|
|
155
111
|
},
|
|
112
|
+
async deleteMessagesAfter() {},
|
|
113
|
+
async getOperations() {
|
|
114
|
+
return []
|
|
115
|
+
},
|
|
116
|
+
async getTrashItems() {
|
|
117
|
+
return []
|
|
118
|
+
},
|
|
119
|
+
async restoreFromTrash() {
|
|
120
|
+
return undefined
|
|
121
|
+
},
|
|
156
122
|
async *sendMessage() {
|
|
157
123
|
yield { type: 'text', data: '无可用的 Adapter' }
|
|
158
124
|
yield { type: 'done', data: '' }
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { useEffect, useRef, useCallback, type FC, type ReactNode } from 'react'
|
|
7
7
|
import { useChat } from '../hooks/useChat'
|
|
8
|
-
import type { ChatAdapter } from '
|
|
9
|
-
import type { ModelConfig, ChatMode } from '../types'
|
|
8
|
+
import type { ChatAdapter, ModelConfig, ChatMode } from '@huyooo/ai-chat-bridge-electron/renderer'
|
|
10
9
|
import { DEFAULT_MODELS } from '../types'
|
|
11
10
|
import { ChatHeader } from './chat/ui/ChatHeader'
|
|
12
11
|
import { WelcomeMessage } from './chat/ui/WelcomeMessage'
|
package/src/hooks/useChat.ts
CHANGED
|
@@ -7,16 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { useState, useCallback, useRef } from 'react'
|
|
10
|
-
import type { ChatAdapter, ChatProgress } from '../adapter'
|
|
11
|
-
import { createNullAdapter } from '../adapter'
|
|
12
10
|
import type {
|
|
13
|
-
|
|
11
|
+
ChatAdapter,
|
|
12
|
+
ChatProgress,
|
|
14
13
|
ChatMode,
|
|
15
14
|
SessionRecord,
|
|
16
15
|
MessageRecord,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from '../types'
|
|
16
|
+
} from '@huyooo/ai-chat-bridge-electron/renderer'
|
|
17
|
+
import { createNullAdapter } from '../adapter'
|
|
18
|
+
import type { ChatMessage, SearchResult, ToolCall } from '../types'
|
|
20
19
|
|
|
21
20
|
/** 生成唯一 ID */
|
|
22
21
|
function generateId(): string {
|
package/src/index.ts
CHANGED
|
@@ -7,11 +7,25 @@
|
|
|
7
7
|
* 与 Vue 版本 (@huyooo/ai-chat-frontend-vue) 保持一致的 API
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// 从 bridge-electron 重新导出核心类型,确保类型一致性
|
|
11
11
|
export type {
|
|
12
12
|
ChatAdapter,
|
|
13
13
|
ChatProgress,
|
|
14
14
|
ChatProgressType,
|
|
15
|
+
ChatOptions,
|
|
16
|
+
ChatMode,
|
|
17
|
+
ModelConfig,
|
|
18
|
+
ModelProvider,
|
|
19
|
+
ThinkingMode,
|
|
20
|
+
SessionRecord,
|
|
21
|
+
MessageRecord,
|
|
22
|
+
} from '@huyooo/ai-chat-bridge-electron/renderer'
|
|
23
|
+
|
|
24
|
+
// 导出 createElectronAdapter 便于使用
|
|
25
|
+
export { createElectronAdapter } from '@huyooo/ai-chat-bridge-electron/renderer'
|
|
26
|
+
|
|
27
|
+
// 导出本地定义的辅助类型
|
|
28
|
+
export type {
|
|
15
29
|
ThinkingData,
|
|
16
30
|
ToolCallData,
|
|
17
31
|
ToolResultData,
|
|
@@ -43,15 +57,9 @@ export { WelcomeMessage } from './components/chat/ui/WelcomeMessage'
|
|
|
43
57
|
export { MessageBubble } from './components/chat/messages/MessageBubble'
|
|
44
58
|
export { ExecutionSteps } from './components/chat/messages/ExecutionSteps'
|
|
45
59
|
|
|
46
|
-
//
|
|
60
|
+
// 导出前端特有类型(不与 bridge-electron 重复)
|
|
47
61
|
export type {
|
|
48
62
|
ChatMessage,
|
|
49
|
-
ChatMode,
|
|
50
|
-
ModelConfig,
|
|
51
|
-
ModelProvider,
|
|
52
|
-
ThinkingMode,
|
|
53
|
-
SessionRecord,
|
|
54
|
-
MessageRecord,
|
|
55
63
|
SearchResult,
|
|
56
64
|
ToolCall,
|
|
57
65
|
// 向后兼容
|
|
@@ -68,14 +76,13 @@ export { DEFAULT_MODELS, FileType } from './types'
|
|
|
68
76
|
* 1. 导入样式:
|
|
69
77
|
* import '@huyooo/ai-chat-frontend-react/style.css'
|
|
70
78
|
*
|
|
71
|
-
* 2. 创建 adapter
|
|
72
|
-
* import { createElectronAdapter } from '@huyooo/ai-chat-
|
|
79
|
+
* 2. 创建 adapter 和使用组件(一行导入):
|
|
80
|
+
* import { ChatPanel, createElectronAdapter } from '@huyooo/ai-chat-frontend-react'
|
|
73
81
|
* const adapter = createElectronAdapter()
|
|
74
|
-
*
|
|
75
|
-
* 3. 在 React 组件中使用:
|
|
76
82
|
* <ChatPanel adapter={adapter} />
|
|
77
83
|
*
|
|
78
|
-
*
|
|
84
|
+
* 3. 或使用 useChat hook 自定义 UI:
|
|
85
|
+
* import { useChat, createElectronAdapter } from '@huyooo/ai-chat-frontend-react'
|
|
86
|
+
* const adapter = createElectronAdapter()
|
|
79
87
|
* const chat = useChat({ adapter })
|
|
80
|
-
* // 然后自定义 UI
|
|
81
88
|
*/
|
package/src/types/index.ts
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Chat 前端类型定义
|
|
3
|
-
*
|
|
3
|
+
* 核心类型(ChatAdapter, SessionRecord, MessageRecord 等)从 bridge-electron 导出
|
|
4
|
+
* 这里只定义前端特有的类型
|
|
4
5
|
*/
|
|
5
6
|
|
|
7
|
+
// 从 bridge-electron 导入类型(用于本文件)
|
|
8
|
+
import type {
|
|
9
|
+
ChatMode as ChatModeType,
|
|
10
|
+
ModelConfig as ModelConfigType,
|
|
11
|
+
ModelProvider as ModelProviderType,
|
|
12
|
+
ThinkingMode as ThinkingModeType,
|
|
13
|
+
SessionRecord as SessionRecordType,
|
|
14
|
+
MessageRecord as MessageRecordType,
|
|
15
|
+
} from '@huyooo/ai-chat-bridge-electron/renderer'
|
|
16
|
+
|
|
17
|
+
// 重新导出核心类型
|
|
18
|
+
export type ChatMode = ChatModeType
|
|
19
|
+
export type ModelConfig = ModelConfigType
|
|
20
|
+
export type ModelProvider = ModelProviderType
|
|
21
|
+
export type ThinkingMode = ThinkingModeType
|
|
22
|
+
export type SessionRecord = SessionRecordType
|
|
23
|
+
export type MessageRecord = MessageRecordType
|
|
24
|
+
|
|
6
25
|
/** 搜索结果 */
|
|
7
26
|
export interface SearchResult {
|
|
8
27
|
title: string
|
|
@@ -18,26 +37,7 @@ export interface ToolCall {
|
|
|
18
37
|
status: 'running' | 'success' | 'error'
|
|
19
38
|
}
|
|
20
39
|
|
|
21
|
-
/**
|
|
22
|
-
export type ModelProvider = 'openrouter' | 'doubao' | 'deepseek' | 'qwen' | 'gemini' | 'ark'
|
|
23
|
-
|
|
24
|
-
/** 模型配置 */
|
|
25
|
-
export interface ModelConfig {
|
|
26
|
-
provider: ModelProvider
|
|
27
|
-
model: string
|
|
28
|
-
displayName: string
|
|
29
|
-
supportsTools: boolean
|
|
30
|
-
supportsWebSearch: boolean
|
|
31
|
-
supportedThinkingModes: ThinkingMode[]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** 思考模式 */
|
|
35
|
-
export type ThinkingMode = 'enabled' | 'disabled'
|
|
36
|
-
|
|
37
|
-
/** 聊天模式 */
|
|
38
|
-
export type ChatMode = 'agent' | 'ask'
|
|
39
|
-
|
|
40
|
-
/** 聊天消息 */
|
|
40
|
+
/** 聊天消息(前端显示用) */
|
|
41
41
|
export interface ChatMessage {
|
|
42
42
|
id: string
|
|
43
43
|
role: 'user' | 'assistant'
|
|
@@ -53,30 +53,8 @@ export interface ChatMessage {
|
|
|
53
53
|
timestamp?: Date
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/** 会话记录 */
|
|
57
|
-
export interface SessionRecord {
|
|
58
|
-
id: string
|
|
59
|
-
title: string
|
|
60
|
-
model: string
|
|
61
|
-
mode: ChatMode
|
|
62
|
-
createdAt: Date
|
|
63
|
-
updatedAt: Date
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** 消息记录 */
|
|
67
|
-
export interface MessageRecord {
|
|
68
|
-
id: string
|
|
69
|
-
sessionId: string
|
|
70
|
-
role: 'user' | 'assistant'
|
|
71
|
-
content: string
|
|
72
|
-
thinking?: string
|
|
73
|
-
toolCalls?: string
|
|
74
|
-
searchResults?: string
|
|
75
|
-
timestamp: Date
|
|
76
|
-
}
|
|
77
|
-
|
|
78
56
|
/** 默认模型列表 */
|
|
79
|
-
export const DEFAULT_MODELS:
|
|
57
|
+
export const DEFAULT_MODELS: ModelConfigType[] = [
|
|
80
58
|
{
|
|
81
59
|
provider: 'openrouter',
|
|
82
60
|
model: 'anthropic/claude-opus-4.5',
|