@opensumi/ide-ai-native 3.9.1-next-1749007675.0 → 3.9.1-next-1749008258.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 (146) hide show
  1. package/lib/browser/chat/chat-manager.service.d.ts +0 -1
  2. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  3. package/lib/browser/chat/chat-manager.service.js +3 -8
  4. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  5. package/lib/browser/chat/chat-model.d.ts +1 -3
  6. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-model.js +17 -48
  8. package/lib/browser/chat/chat-model.js.map +1 -1
  9. package/lib/browser/chat/chat-proxy.service.d.ts +0 -2
  10. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  11. package/lib/browser/chat/chat-proxy.service.js +50 -57
  12. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  13. package/lib/browser/chat/chat.feature.registry.d.ts +1 -4
  14. package/lib/browser/chat/chat.feature.registry.d.ts.map +1 -1
  15. package/lib/browser/chat/chat.feature.registry.js +0 -6
  16. package/lib/browser/chat/chat.feature.registry.js.map +1 -1
  17. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  18. package/lib/browser/chat/chat.view.js +12 -29
  19. package/lib/browser/chat/chat.view.js.map +1 -1
  20. package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
  21. package/lib/browser/components/ChatMentionInput.js +141 -30
  22. package/lib/browser/components/ChatMentionInput.js.map +1 -1
  23. package/lib/browser/components/ChatToolRender.module.less +1 -0
  24. package/lib/browser/components/components.module.less +9 -8
  25. package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
  26. package/lib/browser/components/mention-input/mention-input.js +161 -14
  27. package/lib/browser/components/mention-input/mention-input.js.map +1 -1
  28. package/lib/browser/components/mention-input/mention-input.module.less +165 -1
  29. package/lib/browser/components/mention-input/mention-select.d.ts +28 -0
  30. package/lib/browser/components/mention-input/mention-select.d.ts.map +1 -0
  31. package/lib/browser/components/mention-input/mention-select.js +136 -0
  32. package/lib/browser/components/mention-input/mention-select.js.map +1 -0
  33. package/lib/browser/components/mention-input/mention-select.module.less +297 -0
  34. package/lib/browser/components/mention-input/types.d.ts +16 -1
  35. package/lib/browser/components/mention-input/types.d.ts.map +1 -1
  36. package/lib/browser/components/mention-input/types.js +1 -0
  37. package/lib/browser/components/mention-input/types.js.map +1 -1
  38. package/lib/browser/components/utils.d.ts +2 -2
  39. package/lib/browser/context/llm-context.service.d.ts +21 -2
  40. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  41. package/lib/browser/context/llm-context.service.js +162 -20
  42. package/lib/browser/context/llm-context.service.js.map +1 -1
  43. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.d.ts.map +1 -1
  44. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.js.map +1 -1
  45. package/lib/browser/contrib/intelligent-completions/diff-computer.js +1 -1
  46. package/lib/browser/contrib/intelligent-completions/diff-computer.js.map +1 -1
  47. package/lib/browser/contrib/terminal/terminal.feature.registry.js.map +1 -1
  48. package/lib/browser/index.d.ts.map +1 -1
  49. package/lib/browser/index.js +7 -0
  50. package/lib/browser/index.js.map +1 -1
  51. package/lib/browser/mcp/base-apply.service.d.ts +2 -2
  52. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  53. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  54. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  55. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
  56. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  57. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  58. package/lib/browser/model/msg-history-manager.d.ts +1 -39
  59. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  60. package/lib/browser/model/msg-history-manager.js +3 -170
  61. package/lib/browser/model/msg-history-manager.js.map +1 -1
  62. package/lib/browser/preferences/schema.d.ts.map +1 -1
  63. package/lib/browser/preferences/schema.js +5 -0
  64. package/lib/browser/preferences/schema.js.map +1 -1
  65. package/lib/browser/rules/rules.contribution.d.ts +29 -0
  66. package/lib/browser/rules/rules.contribution.d.ts.map +1 -0
  67. package/lib/browser/rules/rules.contribution.js +94 -0
  68. package/lib/browser/rules/rules.contribution.js.map +1 -0
  69. package/lib/browser/rules/rules.module.less +175 -0
  70. package/lib/browser/rules/rules.service.d.ts +25 -0
  71. package/lib/browser/rules/rules.service.d.ts.map +1 -0
  72. package/lib/browser/rules/rules.service.js +180 -0
  73. package/lib/browser/rules/rules.service.js.map +1 -0
  74. package/lib/browser/rules/rules.view.d.ts +3 -0
  75. package/lib/browser/rules/rules.view.d.ts.map +1 -0
  76. package/lib/browser/rules/rules.view.js +76 -0
  77. package/lib/browser/rules/rules.view.js.map +1 -0
  78. package/lib/browser/types.d.ts +1 -8
  79. package/lib/browser/types.d.ts.map +1 -1
  80. package/lib/browser/types.js.map +1 -1
  81. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  82. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  83. package/lib/common/MDC_PARSER_README.md +171 -0
  84. package/lib/common/index.d.ts +2 -0
  85. package/lib/common/index.d.ts.map +1 -1
  86. package/lib/common/index.js +2 -0
  87. package/lib/common/index.js.map +1 -1
  88. package/lib/common/llm-context.d.ts +19 -0
  89. package/lib/common/llm-context.d.ts.map +1 -1
  90. package/lib/common/llm-context.js.map +1 -1
  91. package/lib/common/mdc-parser.d.ts +60 -0
  92. package/lib/common/mdc-parser.d.ts.map +1 -0
  93. package/lib/common/mdc-parser.js +246 -0
  94. package/lib/common/mdc-parser.js.map +1 -0
  95. package/lib/common/prompts/context-prompt-provider.d.ts +0 -2
  96. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  97. package/lib/common/prompts/context-prompt-provider.js +35 -29
  98. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  99. package/lib/common/prompts/system-prompt.d.ts +2 -0
  100. package/lib/common/prompts/system-prompt.d.ts.map +1 -0
  101. package/lib/common/prompts/system-prompt.js +5 -0
  102. package/lib/common/prompts/system-prompt.js.map +1 -0
  103. package/lib/common/types.d.ts +7 -0
  104. package/lib/common/types.d.ts.map +1 -1
  105. package/lib/node/base-language-model.d.ts.map +1 -1
  106. package/lib/node/base-language-model.js.map +1 -1
  107. package/package.json +25 -24
  108. package/src/browser/chat/chat-manager.service.ts +6 -15
  109. package/src/browser/chat/chat-model.ts +19 -56
  110. package/src/browser/chat/chat-proxy.service.ts +68 -81
  111. package/src/browser/chat/chat.feature.registry.ts +1 -17
  112. package/src/browser/chat/chat.view.tsx +22 -28
  113. package/src/browser/components/ChatMentionInput.tsx +169 -35
  114. package/src/browser/components/ChatToolRender.module.less +1 -0
  115. package/src/browser/components/components.module.less +9 -8
  116. package/src/browser/components/mention-input/mention-input.module.less +165 -1
  117. package/src/browser/components/mention-input/mention-input.tsx +257 -32
  118. package/src/browser/components/mention-input/mention-select.module.less +297 -0
  119. package/src/browser/components/mention-input/mention-select.tsx +256 -0
  120. package/src/browser/components/mention-input/types.ts +16 -0
  121. package/src/browser/context/llm-context.service.ts +182 -21
  122. package/src/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.ts +1 -1
  123. package/src/browser/contrib/intelligent-completions/diff-computer.ts +1 -1
  124. package/src/browser/contrib/terminal/terminal.feature.registry.ts +1 -1
  125. package/src/browser/index.ts +8 -0
  126. package/src/browser/mcp/base-apply.service.ts +0 -1
  127. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -1
  128. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -1
  129. package/src/browser/mcp/tools/handlers/ListDir.ts +1 -1
  130. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -1
  131. package/src/browser/model/msg-history-manager.ts +3 -230
  132. package/src/browser/preferences/schema.ts +5 -0
  133. package/src/browser/rules/rules.contribution.ts +105 -0
  134. package/src/browser/rules/rules.module.less +175 -0
  135. package/src/browser/rules/rules.service.ts +189 -0
  136. package/src/browser/rules/rules.view.tsx +127 -0
  137. package/src/browser/types.ts +0 -12
  138. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +0 -1
  139. package/src/common/MDC_PARSER_README.md +171 -0
  140. package/src/common/index.ts +3 -0
  141. package/src/common/llm-context.ts +23 -0
  142. package/src/common/mdc-parser.ts +295 -0
  143. package/src/common/prompts/context-prompt-provider.ts +55 -40
  144. package/src/common/prompts/system-prompt.ts +2 -0
  145. package/src/common/types.ts +8 -0
  146. package/src/node/base-language-model.ts +0 -1
@@ -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';
5
- import { Icon, Popover, PopoverPosition, Select, getIcon } from '@opensumi/ide-core-browser/lib/components';
4
+ import { getSymbolIcon, localize } from '@opensumi/ide-core-browser';
5
+ import { Icon, Popover, PopoverPosition, 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,125 @@ 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
1215
+ key={`file-${index}`}
1216
+ className={styles.context_preview_item}
1217
+ data-type={MentionType.FILE}
1218
+ onClick={() => contextService?.removeFileFromContext(file.uri, true)}
1219
+ >
1220
+ <Icon
1221
+ iconClass={cls(
1222
+ labelService?.getIcon(file.uri) || MentionType.FILE,
1223
+ styles.context_preview_item_icon,
1224
+ styles.icon,
1225
+ )}
1226
+ />
1227
+ <Icon
1228
+ iconClass={cls(styles.close_icon, getIcon('close'))}
1229
+ onClick={() => removeContext(MentionType.FILE, file.uri)}
1230
+ />
1231
+ <span className={styles.context_preview_item_text}>{new URI(file.uri.toString()).displayName}</span>
1232
+ </div>
1233
+ ))}
1234
+
1235
+ {attachedFiles.folders.map((folder, index) => (
1236
+ <div
1237
+ key={`folder-${index}`}
1238
+ className={styles.context_preview_item}
1239
+ data-type='folder'
1240
+ onClick={() => contextService?.removeFileFromContext(folder.uri, true)}
1241
+ >
1242
+ <Icon iconClass={cls(getIcon('folder'), styles.context_preview_item_icon, styles.icon)} />
1243
+ <Icon
1244
+ iconClass={cls(styles.close_icon, getIcon('close'))}
1245
+ onClick={() => removeContext(MentionType.FOLDER, folder.uri)}
1246
+ />
1247
+ <span className={styles.context_preview_item_text}>{new URI(folder.uri.toString()).displayName}</span>
1248
+ </div>
1249
+ ))}
1250
+
1251
+ {attachedFiles.rules.map((rule, index) => (
1252
+ <div
1253
+ key={`rule-${index}`}
1254
+ className={styles.context_preview_item}
1255
+ data-type='rule'
1256
+ onClick={() => {
1257
+ // 由于没有专门的删除规则方法,我们重新构建规则列表
1258
+ contextService?.cleanFileContext();
1259
+ // 重新添加除了当前要删除的规则之外的所有上下文
1260
+ attachedFiles.files.forEach((file) => contextService?.addFileToContext(file.uri, file.selection, true));
1261
+ attachedFiles.folders.forEach((folder) => contextService?.addFolderToContext(folder.uri, true));
1262
+ attachedFiles.rules.forEach((r, i) => {
1263
+ if (i !== index) {
1264
+ contextService?.addRuleToContext(new URI(r.path), true);
1265
+ }
1266
+ });
1267
+ }}
1268
+ >
1269
+ <Icon iconClass={cls(getIcon('rules'), styles.context_preview_item_icon, styles.icon)} />
1270
+ <Icon
1271
+ iconClass={cls(styles.close_icon, getIcon('close'))}
1272
+ onClick={() => removeContext(MentionType.RULE, new URI(rule.path))}
1273
+ />
1274
+ <span className={styles.context_preview_item_text}>{getFileNameFromPath(rule.path)}</span>
1275
+ </div>
1276
+ ))}
1277
+ </div>
1278
+ ),
1279
+ [handleClearContext, hasContext, attachedFiles, labelService, contextService, handleTitleClick, removeContext],
1280
+ );
1281
+
1040
1282
  return (
1041
1283
  <div className={styles.input_container}>
1284
+ {renderContextPreview()}
1042
1285
  {mentionState.active && (
1043
1286
  <div className={styles.mention_panel_container}>
1044
1287
  <MentionPanel
@@ -1068,40 +1311,22 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1068
1311
  <div className={styles.left_control}>
1069
1312
  {footerConfig.showModelSelector &&
1070
1313
  renderModelSelectorTip(
1071
- <Select
1072
- options={footerConfig.modelOptions || []}
1314
+ <MentionSelect
1315
+ options={getExtendedModelOptions}
1073
1316
  value={selectedModel}
1074
1317
  onChange={handleModelChange}
1075
1318
  className={styles.model_selector}
1076
1319
  size='small'
1077
1320
  disabled={footerConfig.disableModelSelector}
1321
+ showThinking={footerConfig.showThinking}
1322
+ thinkingEnabled={footerConfig.thinkingEnabled}
1323
+ onThinkingChange={footerConfig.onThinkingChange}
1078
1324
  />,
1079
1325
  )}
1080
1326
  {renderButtons(FooterButtonPosition.LEFT)}
1081
1327
  </div>
1082
1328
  <div className={styles.right_control}>
1083
1329
  {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
1330
  <Popover
1106
1331
  overlayClassName={styles.popover_icon}
1107
1332
  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
+ }