@opensumi/ide-ai-native 3.9.1-next-1749540423.0 → 3.9.1-next-1749546307.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  2. package/lib/browser/ai-core.contribution.js +9 -4
  3. package/lib/browser/ai-core.contribution.js.map +1 -1
  4. package/lib/browser/chat/apply.service.d.ts +3 -0
  5. package/lib/browser/chat/apply.service.d.ts.map +1 -1
  6. package/lib/browser/chat/apply.service.js +47 -0
  7. package/lib/browser/chat/apply.service.js.map +1 -1
  8. package/lib/browser/chat/chat-manager.service.d.ts +1 -0
  9. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  10. package/lib/browser/chat/chat-manager.service.js +9 -3
  11. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  12. package/lib/browser/chat/chat-model.d.ts +8 -1
  13. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  14. package/lib/browser/chat/chat-model.js +113 -76
  15. package/lib/browser/chat/chat-model.js.map +1 -1
  16. package/lib/browser/chat/chat-proxy.service.d.ts +0 -2
  17. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  18. package/lib/browser/chat/chat-proxy.service.js +50 -57
  19. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  20. package/lib/browser/chat/chat.feature.registry.d.ts +4 -1
  21. package/lib/browser/chat/chat.feature.registry.d.ts.map +1 -1
  22. package/lib/browser/chat/chat.feature.registry.js +6 -0
  23. package/lib/browser/chat/chat.feature.registry.js.map +1 -1
  24. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  25. package/lib/browser/chat/chat.view.js +49 -10
  26. package/lib/browser/chat/chat.view.js.map +1 -1
  27. package/lib/browser/components/ChatEditor.js +2 -2
  28. package/lib/browser/components/ChatEditor.js.map +1 -1
  29. package/lib/browser/components/ChatHistory.d.ts.map +1 -1
  30. package/lib/browser/components/ChatHistory.js +2 -1
  31. package/lib/browser/components/ChatHistory.js.map +1 -1
  32. package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
  33. package/lib/browser/components/ChatMentionInput.js +148 -30
  34. package/lib/browser/components/ChatMentionInput.js.map +1 -1
  35. package/lib/browser/components/ChatReply.js +2 -2
  36. package/lib/browser/components/ChatReply.js.map +1 -1
  37. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  38. package/lib/browser/components/ChatToolRender.js +7 -2
  39. package/lib/browser/components/ChatToolRender.js.map +1 -1
  40. package/lib/browser/components/ChatToolRender.module.less +25 -0
  41. package/lib/browser/components/components.module.less +37 -8
  42. package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
  43. package/lib/browser/components/mention-input/mention-input.js +150 -14
  44. package/lib/browser/components/mention-input/mention-input.js.map +1 -1
  45. package/lib/browser/components/mention-input/mention-input.module.less +165 -1
  46. package/lib/browser/components/mention-input/mention-select.d.ts +28 -0
  47. package/lib/browser/components/mention-input/mention-select.d.ts.map +1 -0
  48. package/lib/browser/components/mention-input/mention-select.js +136 -0
  49. package/lib/browser/components/mention-input/mention-select.js.map +1 -0
  50. package/lib/browser/components/mention-input/mention-select.module.less +297 -0
  51. package/lib/browser/components/mention-input/types.d.ts +28 -1
  52. package/lib/browser/components/mention-input/types.d.ts.map +1 -1
  53. package/lib/browser/components/mention-input/types.js +1 -0
  54. package/lib/browser/components/mention-input/types.js.map +1 -1
  55. package/lib/browser/components/utils.d.ts +2 -2
  56. package/lib/browser/context/llm-context.service.d.ts +21 -2
  57. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  58. package/lib/browser/context/llm-context.service.js +162 -20
  59. package/lib/browser/context/llm-context.service.js.map +1 -1
  60. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.d.ts.map +1 -1
  61. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.js.map +1 -1
  62. package/lib/browser/contrib/intelligent-completions/diff-computer.js +1 -1
  63. package/lib/browser/contrib/intelligent-completions/diff-computer.js.map +1 -1
  64. package/lib/browser/contrib/terminal/terminal.feature.registry.js.map +1 -1
  65. package/lib/browser/index.d.ts.map +1 -1
  66. package/lib/browser/index.js +7 -0
  67. package/lib/browser/index.js.map +1 -1
  68. package/lib/browser/layout/ai-layout.d.ts.map +1 -1
  69. package/lib/browser/layout/ai-layout.js +6 -4
  70. package/lib/browser/layout/ai-layout.js.map +1 -1
  71. package/lib/browser/layout/tabbar.view.d.ts +1 -1
  72. package/lib/browser/layout/tabbar.view.d.ts.map +1 -1
  73. package/lib/browser/layout/tabbar.view.js +5 -12
  74. package/lib/browser/layout/tabbar.view.js.map +1 -1
  75. package/lib/browser/mcp/base-apply.service.d.ts +5 -4
  76. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  77. package/lib/browser/mcp/base-apply.service.js +23 -5
  78. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  79. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +3 -1
  80. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
  81. package/lib/browser/mcp/mcp-server-proxy.service.js +4 -0
  82. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
  83. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
  84. package/lib/browser/mcp/mcp-server.feature.registry.js +7 -1
  85. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
  86. package/lib/browser/mcp/tools/createNewFileWithText.d.ts +1 -3
  87. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  88. package/lib/browser/mcp/tools/createNewFileWithText.js +14 -40
  89. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  90. package/lib/browser/mcp/tools/fileSearch.d.ts.map +1 -1
  91. package/lib/browser/mcp/tools/fileSearch.js +9 -5
  92. package/lib/browser/mcp/tools/fileSearch.js.map +1 -1
  93. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  94. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
  95. package/lib/browser/mcp/tools/grepSearch.d.ts.map +1 -1
  96. package/lib/browser/mcp/tools/grepSearch.js +22 -10
  97. package/lib/browser/mcp/tools/grepSearch.js.map +1 -1
  98. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.d.ts +15 -0
  99. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.d.ts.map +1 -0
  100. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.js +53 -0
  101. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.js.map +1 -0
  102. package/lib/browser/mcp/tools/handlers/ListDir.js +1 -1
  103. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  104. package/lib/browser/mcp/tools/handlers/ReadFile.js +1 -1
  105. package/lib/browser/mcp/tools/handlers/ReadFile.js.map +1 -1
  106. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts +11 -1
  107. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
  108. package/lib/browser/mcp/tools/handlers/RunCommand.js +11 -4
  109. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
  110. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
  111. package/lib/browser/mcp/tools/listDir.js +19 -15
  112. package/lib/browser/mcp/tools/listDir.js.map +1 -1
  113. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  114. package/lib/browser/model/msg-history-manager.d.ts +47 -1
  115. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  116. package/lib/browser/model/msg-history-manager.js +127 -2
  117. package/lib/browser/model/msg-history-manager.js.map +1 -1
  118. package/lib/browser/preferences/schema.d.ts.map +1 -1
  119. package/lib/browser/preferences/schema.js +5 -0
  120. package/lib/browser/preferences/schema.js.map +1 -1
  121. package/lib/browser/rules/rules.contribution.d.ts +29 -0
  122. package/lib/browser/rules/rules.contribution.d.ts.map +1 -0
  123. package/lib/browser/rules/rules.contribution.js +94 -0
  124. package/lib/browser/rules/rules.contribution.js.map +1 -0
  125. package/lib/browser/rules/rules.module.less +175 -0
  126. package/lib/browser/rules/rules.service.d.ts +25 -0
  127. package/lib/browser/rules/rules.service.d.ts.map +1 -0
  128. package/lib/browser/rules/rules.service.js +180 -0
  129. package/lib/browser/rules/rules.service.js.map +1 -0
  130. package/lib/browser/rules/rules.view.d.ts +3 -0
  131. package/lib/browser/rules/rules.view.d.ts.map +1 -0
  132. package/lib/browser/rules/rules.view.js +76 -0
  133. package/lib/browser/rules/rules.view.js.map +1 -0
  134. package/lib/browser/types.d.ts +12 -1
  135. package/lib/browser/types.d.ts.map +1 -1
  136. package/lib/browser/types.js.map +1 -1
  137. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  138. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  139. package/lib/common/image-compression.d.ts +25 -0
  140. package/lib/common/image-compression.d.ts.map +1 -0
  141. package/lib/common/image-compression.js +153 -0
  142. package/lib/common/image-compression.js.map +1 -0
  143. package/lib/common/index.d.ts +5 -1
  144. package/lib/common/index.d.ts.map +1 -1
  145. package/lib/common/index.js +2 -0
  146. package/lib/common/index.js.map +1 -1
  147. package/lib/common/llm-context.d.ts +19 -0
  148. package/lib/common/llm-context.d.ts.map +1 -1
  149. package/lib/common/llm-context.js.map +1 -1
  150. package/lib/common/mdc-parser.d.ts +60 -0
  151. package/lib/common/mdc-parser.d.ts.map +1 -0
  152. package/lib/common/mdc-parser.js +246 -0
  153. package/lib/common/mdc-parser.js.map +1 -0
  154. package/lib/common/model.d.ts +1 -0
  155. package/lib/common/model.d.ts.map +1 -1
  156. package/lib/common/model.js.map +1 -1
  157. package/lib/common/prompts/context-prompt-provider.d.ts +0 -2
  158. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  159. package/lib/common/prompts/context-prompt-provider.js +35 -29
  160. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  161. package/lib/common/prompts/system-prompt.d.ts +2 -0
  162. package/lib/common/prompts/system-prompt.d.ts.map +1 -0
  163. package/lib/common/prompts/system-prompt.js +5 -0
  164. package/lib/common/prompts/system-prompt.js.map +1 -0
  165. package/lib/common/types.d.ts +21 -0
  166. package/lib/common/types.d.ts.map +1 -1
  167. package/lib/common/types.js.map +1 -1
  168. package/lib/common/utils.d.ts +1 -0
  169. package/lib/common/utils.d.ts.map +1 -1
  170. package/lib/common/utils.js +5 -2
  171. package/lib/common/utils.js.map +1 -1
  172. package/lib/node/anthropic/anthropic-language-model.d.ts +1 -1
  173. package/lib/node/base-language-model.d.ts +2 -1
  174. package/lib/node/base-language-model.d.ts.map +1 -1
  175. package/lib/node/base-language-model.js +12 -2
  176. package/lib/node/base-language-model.js.map +1 -1
  177. package/lib/node/deepseek/deepseek-language-model.d.ts +1 -1
  178. package/lib/node/mcp/sumi-mcp-server.d.ts +3 -1
  179. package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -1
  180. package/lib/node/mcp/sumi-mcp-server.js +7 -1
  181. package/lib/node/mcp/sumi-mcp-server.js.map +1 -1
  182. package/lib/node/mcp-server-manager-impl.d.ts +3 -1
  183. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
  184. package/lib/node/mcp-server-manager-impl.js +14 -2
  185. package/lib/node/mcp-server-manager-impl.js.map +1 -1
  186. package/lib/node/mcp-server.sse.d.ts +187 -1
  187. package/lib/node/mcp-server.sse.d.ts.map +1 -1
  188. package/lib/node/mcp-server.sse.js +2 -2
  189. package/lib/node/mcp-server.sse.js.map +1 -1
  190. package/lib/node/mcp-server.stdio.d.ts +187 -1
  191. package/lib/node/mcp-server.stdio.d.ts.map +1 -1
  192. package/package.json +27 -26
  193. package/src/browser/ai-core.contribution.ts +14 -4
  194. package/src/browser/chat/apply.service.ts +62 -1
  195. package/src/browser/chat/chat-manager.service.ts +16 -7
  196. package/src/browser/chat/chat-model.ts +130 -73
  197. package/src/browser/chat/chat-proxy.service.ts +68 -81
  198. package/src/browser/chat/chat.feature.registry.ts +17 -1
  199. package/src/browser/chat/chat.view.tsx +73 -12
  200. package/src/browser/components/ChatEditor.tsx +1 -1
  201. package/src/browser/components/ChatHistory.tsx +2 -1
  202. package/src/browser/components/ChatMentionInput.tsx +180 -35
  203. package/src/browser/components/ChatReply.tsx +4 -4
  204. package/src/browser/components/ChatToolRender.module.less +25 -0
  205. package/src/browser/components/ChatToolRender.tsx +10 -2
  206. package/src/browser/components/components.module.less +37 -8
  207. package/src/browser/components/mention-input/mention-input.module.less +165 -1
  208. package/src/browser/components/mention-input/mention-input.tsx +232 -31
  209. package/src/browser/components/mention-input/mention-select.module.less +297 -0
  210. package/src/browser/components/mention-input/mention-select.tsx +256 -0
  211. package/src/browser/components/mention-input/types.ts +29 -0
  212. package/src/browser/context/llm-context.service.ts +182 -21
  213. package/src/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.ts +1 -1
  214. package/src/browser/contrib/intelligent-completions/diff-computer.ts +1 -1
  215. package/src/browser/contrib/terminal/terminal.feature.registry.ts +1 -1
  216. package/src/browser/index.ts +8 -0
  217. package/src/browser/layout/ai-layout.tsx +12 -8
  218. package/src/browser/layout/tabbar.view.tsx +10 -23
  219. package/src/browser/mcp/base-apply.service.ts +30 -10
  220. package/src/browser/mcp/mcp-server-proxy.service.ts +6 -1
  221. package/src/browser/mcp/mcp-server.feature.registry.ts +6 -1
  222. package/src/browser/mcp/tools/createNewFileWithText.ts +17 -46
  223. package/src/browser/mcp/tools/fileSearch.ts +8 -5
  224. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -1
  225. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -1
  226. package/src/browser/mcp/tools/grepSearch.ts +32 -21
  227. package/src/browser/mcp/tools/handlers/CreateNewFileWithText.ts +49 -0
  228. package/src/browser/mcp/tools/handlers/ListDir.ts +2 -2
  229. package/src/browser/mcp/tools/handlers/ReadFile.ts +2 -2
  230. package/src/browser/mcp/tools/handlers/RunCommand.ts +21 -14
  231. package/src/browser/mcp/tools/listDir.ts +15 -12
  232. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -1
  233. package/src/browser/model/msg-history-manager.ts +181 -2
  234. package/src/browser/preferences/schema.ts +5 -0
  235. package/src/browser/rules/rules.contribution.ts +105 -0
  236. package/src/browser/rules/rules.module.less +175 -0
  237. package/src/browser/rules/rules.service.ts +189 -0
  238. package/src/browser/rules/rules.view.tsx +127 -0
  239. package/src/browser/types.ts +18 -0
  240. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +0 -1
  241. package/src/common/image-compression.ts +174 -0
  242. package/src/common/index.ts +6 -1
  243. package/src/common/llm-context.ts +23 -0
  244. package/src/common/mdc-parser.ts +295 -0
  245. package/src/common/model.ts +1 -0
  246. package/src/common/prompts/context-prompt-provider.ts +55 -40
  247. package/src/common/prompts/system-prompt.ts +2 -0
  248. package/src/common/types.ts +18 -0
  249. package/src/common/utils.ts +4 -1
  250. package/src/node/base-language-model.ts +11 -14
  251. package/src/node/mcp/sumi-mcp-server.ts +10 -2
  252. package/src/node/mcp-server-manager-impl.ts +17 -2
  253. package/src/node/mcp-server.sse.ts +1 -2
@@ -1,15 +1,17 @@
1
1
  import cls from 'classnames';
2
2
  import * as React from 'react';
3
3
 
4
- import { formatLocalize, getSymbolIcon, localize } from '@opensumi/ide-core-browser';
4
+ import { getSymbolIcon, localize } from '@opensumi/ide-core-browser';
5
5
  import { Icon, Popover, PopoverPosition, Select, getIcon } from '@opensumi/ide-core-browser/lib/components';
6
6
  import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
7
7
  import { URI } from '@opensumi/ide-utils';
8
8
 
9
9
  import { FileContext } from '../../../common/llm-context';
10
+ import { ProjectRule } from '../../../common/types';
10
11
 
11
12
  import styles from './mention-input.module.less';
12
13
  import { MentionPanel } from './mention-panel';
14
+ import { ExtendedModelOption, MentionSelect } from './mention-select';
13
15
  import {
14
16
  FooterButtonPosition,
15
17
  MENTION_KEYWORD,
@@ -67,11 +69,22 @@ export const MentionInput: React.FC<MentionInputProps> = ({
67
69
  const [attachedFiles, setAttachedFiles] = React.useState<{
68
70
  files: FileContext[];
69
71
  folders: FileContext[];
72
+ rules: ProjectRule[];
70
73
  }>({
71
74
  files: [],
72
75
  folders: [],
76
+ rules: [],
73
77
  });
74
78
 
79
+ // 添加用于跟踪 mention_tag 的状态
80
+ const prevMentionTagsRef = React.useRef<
81
+ Array<{
82
+ id: string;
83
+ type: string;
84
+ contextId: string;
85
+ }>
86
+ >([]);
87
+
75
88
  const getCurrentItems = (): MentionItem[] => {
76
89
  if (mentionState.level === 0) {
77
90
  return mentionItems;
@@ -177,14 +190,14 @@ export const MentionInput: React.FC<MentionInputProps> = ({
177
190
  }, [debouncedSecondLevelFilter, mentionState.level, mentionState.parentType]);
178
191
 
179
192
  React.useEffect(() => {
180
- const disposable = contextService?.onDidContextFilesChangeEvent(({ attached, attachedFolders }) => {
181
- setAttachedFiles({ files: attached, folders: attachedFolders });
193
+ const disposable = contextService?.onDidContextFilesChangeEvent(({ attached, attachedFolders, attachedRules }) => {
194
+ setAttachedFiles({ files: attached, folders: attachedFolders, rules: attachedRules });
182
195
  });
183
196
 
184
197
  return () => {
185
198
  disposable?.dispose();
186
199
  };
187
- }, [contextService]);
200
+ }, []);
188
201
 
189
202
  // 获取光标位置
190
203
  const getCursorPosition = (element: HTMLElement): number => {
@@ -207,6 +220,45 @@ export const MentionInput: React.FC<MentionInputProps> = ({
207
220
  setHistoryIndex(-1);
208
221
  }
209
222
 
223
+ // 检测 mention_tag 的删除
224
+ if (editorRef.current) {
225
+ const currentMentionTags = Array.from(editorRef.current.querySelectorAll(`.${styles.mention_tag}`)).map(
226
+ (tag) => ({
227
+ id: tag.getAttribute('data-id') || '',
228
+ type: tag.getAttribute('data-type') || '',
229
+ contextId: tag.getAttribute('data-context-id') || '',
230
+ }),
231
+ );
232
+
233
+ // 找出被删除的 mention_tag
234
+ const deletedTags = prevMentionTagsRef.current.filter(
235
+ (prevTag) =>
236
+ !currentMentionTags.some(
237
+ (currentTag) =>
238
+ currentTag.id === prevTag.id &&
239
+ currentTag.type === prevTag.type &&
240
+ currentTag.contextId === prevTag.contextId,
241
+ ),
242
+ );
243
+
244
+ // 清理被删除的 mention_tag 对应的 context
245
+ deletedTags.forEach((deletedTag) => {
246
+ if (deletedTag.contextId) {
247
+ const uri = new URI(deletedTag.contextId);
248
+ if (deletedTag.type === MentionType.FILE) {
249
+ removeContext(MentionType.FILE, uri);
250
+ } else if (deletedTag.type === MentionType.FOLDER) {
251
+ removeContext(MentionType.FOLDER, uri);
252
+ } else if (deletedTag.type === MentionType.RULE) {
253
+ removeContext(MentionType.RULE, uri);
254
+ }
255
+ }
256
+ });
257
+
258
+ // 更新 mention_tag 状态
259
+ prevMentionTagsRef.current = currentMentionTags;
260
+ }
261
+
210
262
  const selection = window.getSelection();
211
263
  if (!selection || !selection.rangeCount || !editorRef.current) {
212
264
  return;
@@ -490,7 +542,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
490
542
  const imageFiles: File[] = [];
491
543
  // eslint-disable-next-line @typescript-eslint/prefer-for-of
492
544
  for (let i = 0; i < items.length; i++) {
493
- if (items[i].kind === 'file' && items[i].type.startsWith('image/')) {
545
+ if (items[i].kind === MentionType.FILE && items[i].type.startsWith('image/')) {
494
546
  const file = items[i].getAsFile();
495
547
  if (file) {
496
548
  imageFiles.push(file);
@@ -572,6 +624,16 @@ export const MentionInput: React.FC<MentionInputProps> = ({
572
624
  if (placeholder && !editorRef.current.textContent) {
573
625
  editorRef.current.setAttribute('data-placeholder', placeholder);
574
626
  }
627
+
628
+ // 初始化 mention_tag 状态
629
+ const initialMentionTags = Array.from(editorRef.current.querySelectorAll(`.${styles.mention_tag}`)).map(
630
+ (tag) => ({
631
+ id: tag.getAttribute('data-id') || '',
632
+ type: tag.getAttribute('data-type') || '',
633
+ contextId: tag.getAttribute('data-context-id') || '',
634
+ }),
635
+ );
636
+ prevMentionTagsRef.current = initialMentionTags;
575
637
  }
576
638
  }, [placeholder]);
577
639
 
@@ -723,6 +785,11 @@ export const MentionInput: React.FC<MentionInputProps> = ({
723
785
  true,
724
786
  );
725
787
  }
788
+ } else if (item.type === MentionType.RULE) {
789
+ const iconSpan = document.createElement('span');
790
+ iconSpan.className = cls(styles.mention_icon, getIcon('rules'));
791
+ mentionTag.appendChild(iconSpan);
792
+ contextService?.addRuleToContext(new URI(item.contextId), true);
726
793
  }
727
794
  const workspace = workspaceService?.workspace;
728
795
  let relativePath = item.text;
@@ -822,12 +889,12 @@ export const MentionInput: React.FC<MentionInputProps> = ({
822
889
  mentionTag.contentEditable = 'false';
823
890
 
824
891
  // 为 file 和 folder 类型添加图标
825
- if (item.type === 'file' || item.type === 'folder') {
892
+ if (item.type === MentionType.FILE || item.type === 'folder') {
826
893
  // 创建图标容器
827
894
  const iconSpan = document.createElement('span');
828
895
  iconSpan.className = cls(
829
896
  styles.mention_icon,
830
- item.type === 'file' ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'),
897
+ item.type === MentionType.FILE ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'),
831
898
  );
832
899
  mentionTag.appendChild(iconSpan);
833
900
  }
@@ -983,6 +1050,65 @@ export const MentionInput: React.FC<MentionInputProps> = ({
983
1050
  contextService?.cleanFileContext();
984
1051
  }, [contextService]);
985
1052
 
1053
+ const handleTitleClick = React.useCallback(() => {
1054
+ if (!editorRef.current) {
1055
+ return;
1056
+ }
1057
+
1058
+ // 聚焦输入框
1059
+ editorRef.current.focus();
1060
+
1061
+ // 获取当前光标位置
1062
+ const selection = window.getSelection();
1063
+ if (!selection) {
1064
+ return;
1065
+ }
1066
+
1067
+ // 在当前位置插入 @ 符号
1068
+ const range = document.createRange();
1069
+
1070
+ // 如果编辑器为空,直接插入
1071
+ if (!editorRef.current.textContent || editorRef.current.textContent.trim() === '') {
1072
+ editorRef.current.innerHTML = '@';
1073
+ range.setStart(editorRef.current.firstChild || editorRef.current, 1);
1074
+ range.setEnd(editorRef.current.firstChild || editorRef.current, 1);
1075
+ } else {
1076
+ // 当输入框有内容时,总是在末尾插入 @ 符号
1077
+ const textNode = document.createTextNode(' @');
1078
+
1079
+ // 移动到编辑器末尾
1080
+ range.selectNodeContents(editorRef.current);
1081
+ range.collapse(false); // 移动到末尾
1082
+
1083
+ // 在末尾插入空格和 @ 符号
1084
+ range.insertNode(textNode);
1085
+ range.setStartAfter(textNode);
1086
+ range.setEndAfter(textNode);
1087
+ }
1088
+
1089
+ // 设置新的光标位置
1090
+ selection.removeAllRanges();
1091
+ selection.addRange(range);
1092
+
1093
+ // 获取插入后的光标位置
1094
+ const newCursorPos = getCursorPosition(editorRef.current);
1095
+
1096
+ // 激活菜单状态
1097
+ setMentionState({
1098
+ active: true,
1099
+ startPos: newCursorPos,
1100
+ filter: '@',
1101
+ position: { top: 0, left: 0 },
1102
+ activeIndex: 0,
1103
+ level: 0,
1104
+ parentType: null,
1105
+ secondLevelFilter: '',
1106
+ inlineSearchActive: false,
1107
+ inlineSearchStartPos: null,
1108
+ loading: false,
1109
+ });
1110
+ }, []);
1111
+
986
1112
  const handleStop = React.useCallback(() => {
987
1113
  if (onStop) {
988
1114
  onStop();
@@ -1015,7 +1141,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1015
1141
  );
1016
1142
 
1017
1143
  const hasContext = React.useMemo(
1018
- () => attachedFiles.files.length > 0 || attachedFiles.folders.length > 0,
1144
+ () => attachedFiles.files.length > 0 || attachedFiles.folders.length > 0 || attachedFiles.rules.length > 0,
1019
1145
  [attachedFiles],
1020
1146
  );
1021
1147
 
@@ -1037,8 +1163,101 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1037
1163
  [footerConfig.disableModelSelector],
1038
1164
  );
1039
1165
 
1166
+ // 转换模型选项为扩展格式
1167
+ const getExtendedModelOptions = React.useMemo((): ExtendedModelOption[] => {
1168
+ // 如果有扩展模型选项,直接使用
1169
+ if (footerConfig.extendedModelOptions) {
1170
+ return footerConfig.extendedModelOptions.map((option) => ({
1171
+ ...option,
1172
+ selected: option.value === selectedModel,
1173
+ }));
1174
+ }
1175
+
1176
+ // 否则从基础模型选项转换
1177
+ return (footerConfig.modelOptions || []).map((option): ExtendedModelOption => {
1178
+ const extendedOption: ExtendedModelOption = {
1179
+ ...option,
1180
+ };
1181
+
1182
+ // 设置选中状态:如果当前模型匹配选中的模型,则标记为选中
1183
+ extendedOption.selected = option.value === selectedModel;
1184
+
1185
+ return extendedOption;
1186
+ });
1187
+ }, [footerConfig.modelOptions, footerConfig.extendedModelOptions, selectedModel]);
1188
+
1189
+ const removeContext = React.useCallback(
1190
+ (type: MentionType, uri: URI) => {
1191
+ if (type === MentionType.FILE) {
1192
+ contextService?.removeFileFromContext(uri, true);
1193
+ } else if (type === MentionType.FOLDER) {
1194
+ contextService?.removeFolderFromContext(uri);
1195
+ } else if (type === MentionType.RULE) {
1196
+ contextService?.removeRuleFromContext(uri);
1197
+ }
1198
+ },
1199
+ [contextService],
1200
+ );
1201
+
1202
+ const getFileNameFromPath = (path: string) => decodeURIComponent(path.split('/').pop() || 'Unknown Rule');
1203
+
1204
+ const renderContextPreview = React.useCallback(
1205
+ () => (
1206
+ <div className={styles.context_preview_container}>
1207
+ <span
1208
+ className={cls(styles.context_preview_title, hasContext && styles.has_context)}
1209
+ onClick={handleTitleClick}
1210
+ >
1211
+ {!hasContext ? localize('aiNative.chat.context.title') : ''}
1212
+ </span>
1213
+ {attachedFiles.files.map((file, index) => (
1214
+ <div key={`file-${index}`} className={styles.context_preview_item} data-type={MentionType.FILE}>
1215
+ <Icon
1216
+ iconClass={cls(
1217
+ labelService?.getIcon(file.uri) || MentionType.FILE,
1218
+ styles.context_preview_item_icon,
1219
+ styles.icon,
1220
+ )}
1221
+ />
1222
+ <Icon
1223
+ iconClass={cls(styles.close_icon, getIcon('close'))}
1224
+ onClick={() => removeContext(MentionType.FILE, file.uri)}
1225
+ />
1226
+ <span className={styles.context_preview_item_text}>{new URI(file.uri.toString()).displayName}</span>
1227
+ </div>
1228
+ ))}
1229
+
1230
+ {attachedFiles.folders.map((folder, index) => (
1231
+ <div key={`folder-${index}`} className={styles.context_preview_item} data-type='folder'>
1232
+ <Icon iconClass={cls(getIcon('folder'), styles.context_preview_item_icon, styles.icon)} />
1233
+ <Icon
1234
+ iconClass={cls(styles.close_icon, getIcon('close'))}
1235
+ onClick={() => removeContext(MentionType.FOLDER, folder.uri)}
1236
+ />
1237
+ <span className={styles.context_preview_item_text}>{new URI(folder.uri.toString()).displayName}</span>
1238
+ </div>
1239
+ ))}
1240
+
1241
+ {attachedFiles.rules.map((rule, index) => (
1242
+ <div key={`rule-${index}`} className={styles.context_preview_item} data-type='rule'>
1243
+ <Icon iconClass={cls(getIcon('rules'), styles.context_preview_item_icon, styles.icon)} />
1244
+ <Icon
1245
+ iconClass={cls(styles.close_icon, getIcon('close'))}
1246
+ onClick={() => removeContext(MentionType.RULE, new URI(rule.path))}
1247
+ />
1248
+ <span className={styles.context_preview_item_text}>
1249
+ {getFileNameFromPath(rule.path).replace('.mdc', '')}
1250
+ </span>
1251
+ </div>
1252
+ ))}
1253
+ </div>
1254
+ ),
1255
+ [handleClearContext, hasContext, attachedFiles, labelService, contextService, handleTitleClick, removeContext],
1256
+ );
1257
+
1040
1258
  return (
1041
1259
  <div className={styles.input_container}>
1260
+ {renderContextPreview()}
1042
1261
  {mentionState.active && (
1043
1262
  <div className={styles.mention_panel_container}>
1044
1263
  <MentionPanel
@@ -1068,40 +1287,22 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1068
1287
  <div className={styles.left_control}>
1069
1288
  {footerConfig.showModelSelector &&
1070
1289
  renderModelSelectorTip(
1071
- <Select
1072
- options={footerConfig.modelOptions || []}
1290
+ <MentionSelect
1291
+ options={getExtendedModelOptions}
1073
1292
  value={selectedModel}
1074
1293
  onChange={handleModelChange}
1075
1294
  className={styles.model_selector}
1076
1295
  size='small'
1077
1296
  disabled={footerConfig.disableModelSelector}
1297
+ showThinking={footerConfig.showThinking}
1298
+ thinkingEnabled={footerConfig.thinkingEnabled}
1299
+ onThinkingChange={footerConfig.onThinkingChange}
1078
1300
  />,
1079
1301
  )}
1080
1302
  {renderButtons(FooterButtonPosition.LEFT)}
1081
1303
  </div>
1082
1304
  <div className={styles.right_control}>
1083
1305
  {renderButtons(FooterButtonPosition.RIGHT)}
1084
- {hasContext && (
1085
- <Popover
1086
- overlayClassName={styles.popover_icon}
1087
- id={'ai-chat-clear-context'}
1088
- position={PopoverPosition.top}
1089
- content={localize('aiNative.chat.context.clear')}
1090
- >
1091
- <div className={styles.context_container} onClick={handleClearContext}>
1092
- <div className={styles.context_icon}>
1093
- <Icon icon='out-link' />
1094
- <Icon icon='close' />
1095
- </div>
1096
- <div className={styles.context_description}>
1097
- {formatLocalize(
1098
- 'aiNative.chat.context.description',
1099
- attachedFiles.files.length + attachedFiles.folders.length,
1100
- )}
1101
- </div>
1102
- </div>
1103
- </Popover>
1104
- )}
1105
1306
  <Popover
1106
1307
  overlayClassName={styles.popover_icon}
1107
1308
  id={'ai-chat-send'}
@@ -0,0 +1,297 @@
1
+ .mention_select {
2
+ position: relative;
3
+ display: inline-block;
4
+
5
+ &.size_small {
6
+ .select_trigger {
7
+ height: 20px;
8
+ padding: 0 6px;
9
+ font-size: 12px;
10
+ }
11
+ }
12
+
13
+ &.size_medium {
14
+ .select_trigger {
15
+ height: 32px;
16
+ padding: 0 12px;
17
+ font-size: 14px;
18
+ }
19
+ }
20
+
21
+ &.size_large {
22
+ .select_trigger {
23
+ height: 40px;
24
+ padding: 0 16px;
25
+ font-size: 16px;
26
+ }
27
+ }
28
+
29
+ &.disabled {
30
+ opacity: 0.6;
31
+ cursor: not-allowed;
32
+
33
+ .select_trigger {
34
+ cursor: not-allowed;
35
+ background-color: var(--input-background-disabled);
36
+ }
37
+ }
38
+
39
+ .select_trigger {
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: space-between;
43
+ background-color: var(--input-background);
44
+ border: 1px solid var(--input-border);
45
+ border-radius: 4px;
46
+ cursor: pointer;
47
+ transition: all 0.2s;
48
+ opacity: 0.7;
49
+
50
+ &:hover:not(.disabled) {
51
+ opacity: 1;
52
+ }
53
+ }
54
+
55
+ .select_content {
56
+ flex: 1;
57
+ overflow: hidden;
58
+ }
59
+
60
+ .selected_option {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 6px;
64
+ }
65
+
66
+ .option_icon {
67
+ flex-shrink: 0;
68
+ font-size: 14px;
69
+ }
70
+
71
+ .option_label {
72
+ flex: 1;
73
+ white-space: nowrap;
74
+ overflow: hidden;
75
+ text-overflow: ellipsis;
76
+ }
77
+
78
+ .option_badge {
79
+ display: inline-block;
80
+ padding: 2px 6px;
81
+ border-radius: 10px;
82
+ font-size: 10px;
83
+ color: white;
84
+ background-color: var(--badge-background);
85
+ white-space: nowrap;
86
+ }
87
+
88
+ .placeholder {
89
+ color: var(--input-placeholder-foreground);
90
+ }
91
+
92
+ .dropdown_arrow {
93
+ margin-left: 8px;
94
+ transition: transform 0.2s;
95
+ color: var(--icon-foreground);
96
+
97
+ &.open {
98
+ transform: rotate(180deg);
99
+ }
100
+ }
101
+
102
+ .dropdown {
103
+ position: absolute;
104
+ left: 0;
105
+ right: 0;
106
+ z-index: 1000;
107
+ background-color: var(--editor-background);
108
+ border: 1px solid var(--dropdown-border);
109
+ border-radius: 6px;
110
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.05);
111
+ max-height: 400px;
112
+ min-width: 300px;
113
+ overflow-y: auto;
114
+ padding: 4px;
115
+ animation: dropdownFadeIn 0.15s ease-out;
116
+
117
+ // 滚动条样式
118
+ &::-webkit-scrollbar {
119
+ width: 6px;
120
+ }
121
+
122
+ &::-webkit-scrollbar-track {
123
+ background: transparent;
124
+ }
125
+
126
+ &::-webkit-scrollbar-thumb {
127
+ background: var(--scrollbar-thumb);
128
+ border-radius: 3px;
129
+ transition: background-color 0.2s;
130
+ }
131
+
132
+ &::-webkit-scrollbar-thumb:hover {
133
+ background: var(--scrollbar-thumb-hover);
134
+ }
135
+ }
136
+
137
+ @keyframes dropdownFadeIn {
138
+ from {
139
+ opacity: 0;
140
+ transform: translateY(-4px);
141
+ }
142
+
143
+ to {
144
+ opacity: 1;
145
+ transform: translateY(0);
146
+ }
147
+ }
148
+
149
+ .thinking_section {
150
+ padding: 8px 12px 4px;
151
+ }
152
+
153
+ .thinking_toggle {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 8px;
157
+ padding: 6px 0;
158
+ cursor: pointer;
159
+ border-radius: 4px;
160
+
161
+ &:hover {
162
+ background-color: var(--list-hover-background);
163
+ }
164
+ }
165
+
166
+ .thinking_icon {
167
+ flex-shrink: 0;
168
+ color: var(--icon-foreground);
169
+
170
+ &.enabled {
171
+ color: var(--list-active-selection-foreground);
172
+ }
173
+ }
174
+
175
+ .thinking_label {
176
+ font-size: 13px;
177
+ color: var(--foreground);
178
+ font-weight: 500;
179
+ }
180
+
181
+ .divider {
182
+ height: 1px;
183
+ background-color: var(--separator-border);
184
+ margin: 8px 0 4px;
185
+ }
186
+
187
+ // 向上展开(默认)
188
+ &.dropdown_up .dropdown {
189
+ bottom: 100%;
190
+ margin-bottom: 4px;
191
+ }
192
+
193
+ // 向下展开
194
+ &.dropdown_down .dropdown {
195
+ top: 100%;
196
+ margin-top: 4px;
197
+ }
198
+
199
+ .option {
200
+ padding: 8px 12px;
201
+ border-radius: 4px;
202
+ cursor: pointer;
203
+ transition: all 0.2s;
204
+ position: relative;
205
+ border: 1px solid transparent;
206
+ box-sizing: content-box;
207
+ margin-bottom: 4px;
208
+ &:last-child {
209
+ margin-bottom: 0;
210
+ }
211
+ &:hover:not(.disabled) {
212
+ border-color: var(--dropdown-border);
213
+ }
214
+
215
+ &.active {
216
+ border-color: var(--dropdown-border);
217
+ }
218
+
219
+ &.selected {
220
+ background-color: var(--dropdown-background);
221
+
222
+ &::after {
223
+ content: '✓';
224
+ position: absolute;
225
+ top: 50%;
226
+ right: 12px;
227
+ transform: translateY(-50%);
228
+ color: var(--list-active-selection-foreground);
229
+ font-weight: bold;
230
+ font-size: 14px;
231
+ z-index: 1;
232
+ }
233
+ }
234
+
235
+ &.disabled {
236
+ opacity: 0.5;
237
+ cursor: not-allowed;
238
+
239
+ &:hover {
240
+ transform: none;
241
+ box-shadow: none;
242
+ border-color: transparent;
243
+ }
244
+ }
245
+ }
246
+
247
+ .option_main {
248
+ display: flex;
249
+ flex-direction: column;
250
+ gap: 6px;
251
+ padding-right: 24px;
252
+ }
253
+
254
+ .option_header {
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: space-between;
258
+ }
259
+
260
+ .option_title {
261
+ display: flex;
262
+ align-items: center;
263
+ gap: 8px;
264
+ flex: 1;
265
+ }
266
+
267
+ .option_description {
268
+ font-size: 12px;
269
+ color: var(--descriptionForeground);
270
+ line-height: 1.4;
271
+ margin-top: 4px;
272
+ }
273
+
274
+ .option_tags {
275
+ display: flex;
276
+ flex-wrap: wrap;
277
+ gap: 4px;
278
+ margin-top: 6px;
279
+ }
280
+
281
+ .tag {
282
+ display: inline-block;
283
+ padding: 2px 6px;
284
+ border-radius: 10px;
285
+ font-size: 10px;
286
+ background-color: var(--badge-background);
287
+ color: var(--badge-foreground);
288
+ white-space: nowrap;
289
+ }
290
+
291
+ .status_indicator {
292
+ width: 8px;
293
+ height: 8px;
294
+ border-radius: 50%;
295
+ flex-shrink: 0;
296
+ }
297
+ }