@opensumi/ide-ai-native 3.9.1-next-1749007675.0 → 3.9.1-next-1749022016.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 (145) 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.view.d.ts.map +1 -1
  14. package/lib/browser/chat/chat.view.js +11 -7
  15. package/lib/browser/chat/chat.view.js.map +1 -1
  16. package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
  17. package/lib/browser/components/ChatMentionInput.js +141 -30
  18. package/lib/browser/components/ChatMentionInput.js.map +1 -1
  19. package/lib/browser/components/ChatReply.js +2 -2
  20. package/lib/browser/components/ChatReply.js.map +1 -1
  21. package/lib/browser/components/ChatToolRender.module.less +1 -0
  22. package/lib/browser/components/components.module.less +37 -8
  23. package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
  24. package/lib/browser/components/mention-input/mention-input.js +161 -14
  25. package/lib/browser/components/mention-input/mention-input.js.map +1 -1
  26. package/lib/browser/components/mention-input/mention-input.module.less +165 -1
  27. package/lib/browser/components/mention-input/mention-select.d.ts +28 -0
  28. package/lib/browser/components/mention-input/mention-select.d.ts.map +1 -0
  29. package/lib/browser/components/mention-input/mention-select.js +136 -0
  30. package/lib/browser/components/mention-input/mention-select.js.map +1 -0
  31. package/lib/browser/components/mention-input/mention-select.module.less +297 -0
  32. package/lib/browser/components/mention-input/types.d.ts +16 -1
  33. package/lib/browser/components/mention-input/types.d.ts.map +1 -1
  34. package/lib/browser/components/mention-input/types.js +1 -0
  35. package/lib/browser/components/mention-input/types.js.map +1 -1
  36. package/lib/browser/components/utils.d.ts +2 -2
  37. package/lib/browser/context/llm-context.service.d.ts +21 -2
  38. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  39. package/lib/browser/context/llm-context.service.js +162 -20
  40. package/lib/browser/context/llm-context.service.js.map +1 -1
  41. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.d.ts.map +1 -1
  42. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.js.map +1 -1
  43. package/lib/browser/contrib/intelligent-completions/diff-computer.js +1 -1
  44. package/lib/browser/contrib/intelligent-completions/diff-computer.js.map +1 -1
  45. package/lib/browser/contrib/terminal/terminal.feature.registry.js.map +1 -1
  46. package/lib/browser/index.d.ts.map +1 -1
  47. package/lib/browser/index.js +7 -0
  48. package/lib/browser/index.js.map +1 -1
  49. package/lib/browser/mcp/base-apply.service.d.ts +2 -2
  50. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  51. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  52. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  53. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
  54. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  55. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  56. package/lib/browser/model/msg-history-manager.d.ts +1 -39
  57. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  58. package/lib/browser/model/msg-history-manager.js +3 -170
  59. package/lib/browser/model/msg-history-manager.js.map +1 -1
  60. package/lib/browser/preferences/schema.d.ts.map +1 -1
  61. package/lib/browser/preferences/schema.js +5 -0
  62. package/lib/browser/preferences/schema.js.map +1 -1
  63. package/lib/browser/rules/rules.contribution.d.ts +29 -0
  64. package/lib/browser/rules/rules.contribution.d.ts.map +1 -0
  65. package/lib/browser/rules/rules.contribution.js +94 -0
  66. package/lib/browser/rules/rules.contribution.js.map +1 -0
  67. package/lib/browser/rules/rules.module.less +175 -0
  68. package/lib/browser/rules/rules.service.d.ts +25 -0
  69. package/lib/browser/rules/rules.service.d.ts.map +1 -0
  70. package/lib/browser/rules/rules.service.js +180 -0
  71. package/lib/browser/rules/rules.service.js.map +1 -0
  72. package/lib/browser/rules/rules.view.d.ts +3 -0
  73. package/lib/browser/rules/rules.view.d.ts.map +1 -0
  74. package/lib/browser/rules/rules.view.js +76 -0
  75. package/lib/browser/rules/rules.view.js.map +1 -0
  76. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  77. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  78. package/lib/common/MDC_PARSER_README.md +171 -0
  79. package/lib/common/index.d.ts +2 -0
  80. package/lib/common/index.d.ts.map +1 -1
  81. package/lib/common/index.js +2 -0
  82. package/lib/common/index.js.map +1 -1
  83. package/lib/common/llm-context.d.ts +19 -0
  84. package/lib/common/llm-context.d.ts.map +1 -1
  85. package/lib/common/llm-context.js.map +1 -1
  86. package/lib/common/mdc-parser.d.ts +60 -0
  87. package/lib/common/mdc-parser.d.ts.map +1 -0
  88. package/lib/common/mdc-parser.js +246 -0
  89. package/lib/common/mdc-parser.js.map +1 -0
  90. package/lib/common/model.d.ts +1 -0
  91. package/lib/common/model.d.ts.map +1 -1
  92. package/lib/common/model.js.map +1 -1
  93. package/lib/common/prompts/context-prompt-provider.d.ts +0 -2
  94. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  95. package/lib/common/prompts/context-prompt-provider.js +35 -29
  96. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  97. package/lib/common/prompts/system-prompt.d.ts +2 -0
  98. package/lib/common/prompts/system-prompt.d.ts.map +1 -0
  99. package/lib/common/prompts/system-prompt.js +5 -0
  100. package/lib/common/prompts/system-prompt.js.map +1 -0
  101. package/lib/common/types.d.ts +7 -0
  102. package/lib/common/types.d.ts.map +1 -1
  103. package/lib/node/base-language-model.d.ts.map +1 -1
  104. package/lib/node/base-language-model.js +2 -1
  105. package/lib/node/base-language-model.js.map +1 -1
  106. package/package.json +25 -24
  107. package/src/browser/chat/chat-manager.service.ts +6 -15
  108. package/src/browser/chat/chat-model.ts +19 -56
  109. package/src/browser/chat/chat-proxy.service.ts +68 -81
  110. package/src/browser/chat/chat.view.tsx +19 -7
  111. package/src/browser/components/ChatMentionInput.tsx +169 -35
  112. package/src/browser/components/ChatReply.tsx +4 -4
  113. package/src/browser/components/ChatToolRender.module.less +1 -0
  114. package/src/browser/components/components.module.less +37 -8
  115. package/src/browser/components/mention-input/mention-input.module.less +165 -1
  116. package/src/browser/components/mention-input/mention-input.tsx +257 -32
  117. package/src/browser/components/mention-input/mention-select.module.less +297 -0
  118. package/src/browser/components/mention-input/mention-select.tsx +256 -0
  119. package/src/browser/components/mention-input/types.ts +16 -0
  120. package/src/browser/context/llm-context.service.ts +182 -21
  121. package/src/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.ts +1 -1
  122. package/src/browser/contrib/intelligent-completions/diff-computer.ts +1 -1
  123. package/src/browser/contrib/terminal/terminal.feature.registry.ts +1 -1
  124. package/src/browser/index.ts +8 -0
  125. package/src/browser/mcp/base-apply.service.ts +0 -1
  126. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -1
  127. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -1
  128. package/src/browser/mcp/tools/handlers/ListDir.ts +1 -1
  129. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -1
  130. package/src/browser/model/msg-history-manager.ts +3 -230
  131. package/src/browser/preferences/schema.ts +5 -0
  132. package/src/browser/rules/rules.contribution.ts +105 -0
  133. package/src/browser/rules/rules.module.less +175 -0
  134. package/src/browser/rules/rules.service.ts +189 -0
  135. package/src/browser/rules/rules.view.tsx +127 -0
  136. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +0 -1
  137. package/src/common/MDC_PARSER_README.md +171 -0
  138. package/src/common/index.ts +3 -0
  139. package/src/common/llm-context.ts +23 -0
  140. package/src/common/mdc-parser.ts +295 -0
  141. package/src/common/model.ts +1 -0
  142. package/src/common/prompts/context-prompt-provider.ts +55 -40
  143. package/src/common/prompts/system-prompt.ts +2 -0
  144. package/src/common/types.ts +8 -0
  145. package/src/node/base-language-model.ts +1 -2
@@ -6,7 +6,6 @@
6
6
 
7
7
  .model_selector {
8
8
  margin-right: 5px;
9
- min-width: 150px;
10
9
  }
11
10
 
12
11
  .editor_area {
@@ -29,7 +28,9 @@
29
28
  word-break: break-word;
30
29
 
31
30
  .mention_tag {
31
+ height: 20px;
32
32
  margin: 0 2px;
33
+ margin-top: -3px;
33
34
  vertical-align: middle;
34
35
  }
35
36
  }
@@ -95,11 +96,13 @@
95
96
  .stop_logo {
96
97
  background-color: var(--badge-background);
97
98
  color: var(--badge-foreground);
99
+
98
100
  .stop_logo_icon {
99
101
  line-height: 16px;
100
102
  color: var(--kt-dangerButton-background);
101
103
  }
102
104
  }
105
+
103
106
  .send_logo {
104
107
  &:hover {
105
108
  background-color: var(--kt-primaryButton-background);
@@ -140,31 +143,38 @@
140
143
  display: flex;
141
144
  justify-content: center;
142
145
  }
146
+
143
147
  .context_container {
144
148
  display: flex;
145
149
  align-items: center;
146
150
  justify-content: center;
147
151
  cursor: pointer;
152
+
148
153
  .context_icon {
149
154
  flex-grow: 0;
150
155
  flex-shrink: 0;
156
+
151
157
  :global(.kt-icon) {
152
158
  font-size: 12px;
153
159
  }
160
+
154
161
  :global(.kticon-close) {
155
162
  display: none;
156
163
  }
157
164
  }
165
+
158
166
  &:hover {
159
167
  .context_icon {
160
168
  :global(.kticon-close) {
161
169
  display: block;
162
170
  }
171
+
163
172
  :global(.kticon-out-link) {
164
173
  display: none;
165
174
  }
166
175
  }
167
176
  }
177
+
168
178
  .context_description {
169
179
  flex: 1;
170
180
  margin-left: 3px;
@@ -254,6 +264,7 @@
254
264
  display: inline;
255
265
  flex: 1;
256
266
  direction: rtl;
267
+ unicode-bidi: plaintext;
257
268
  text-overflow: ellipsis;
258
269
  overflow: hidden;
259
270
  white-space: nowrap;
@@ -341,6 +352,10 @@
341
352
  justify-content: center;
342
353
  margin-right: 3px;
343
354
  font-size: 12px;
355
+ &::before {
356
+ font-size: 12px;
357
+ background-size: 12px !important;
358
+ }
344
359
  }
345
360
 
346
361
  .empty_state {
@@ -404,3 +419,152 @@
404
419
  color: #666;
405
420
  font-style: italic;
406
421
  }
422
+
423
+ .context_preview_container {
424
+ background-color: var(--kt-editorWidget-background);
425
+ border: 1px solid var(--kt-editorWidget-border);
426
+ margin: 0 16px;
427
+ animation: slideIn 0.3s ease-out;
428
+ display: flex;
429
+ flex-wrap: wrap;
430
+ gap: 4px;
431
+ align-items: flex-start;
432
+ margin-bottom: 3px;
433
+ width: calc(100% - 32px);
434
+ }
435
+
436
+ .context_preview_title {
437
+ cursor: pointer;
438
+ display: flex;
439
+ align-items: center;
440
+ justify-content: center;
441
+ padding: 2px 3px;
442
+ height: 18px;
443
+ width: auto;
444
+ box-sizing: border-box;
445
+ border-radius: 4px;
446
+ border: 1px solid color-mix(in srgb, var(--editor-foreground) 10%, transparent);
447
+ outline: none;
448
+ flex-shrink: 0;
449
+ color: var(--descriptionForeground);
450
+ font-size: 11px;
451
+
452
+ &::before {
453
+ content: '@';
454
+ margin-right: 4px;
455
+ }
456
+ &.has_context {
457
+ width: 18px;
458
+ &::before {
459
+ content: '@';
460
+ margin-right: 0;
461
+ }
462
+ }
463
+
464
+ &:hover {
465
+ color: var(--badge-foreground);
466
+ }
467
+ }
468
+
469
+ .context_preview_item {
470
+ cursor: pointer;
471
+ display: flex;
472
+ align-items: center;
473
+ justify-content: flex-start;
474
+ padding: 2px 4px;
475
+ height: 18px;
476
+ width: auto;
477
+ min-width: fit-content;
478
+ max-width: 300px;
479
+ box-sizing: border-box;
480
+ border-radius: 4px;
481
+ border: 1px solid color-mix(in srgb, var(--badge-background) 30%, transparent);
482
+ outline: none;
483
+ flex: 0 1 auto;
484
+ margin-left: 3px;
485
+
486
+ .icon {
487
+ width: 12px !important;
488
+ height: 12px !important;
489
+ line-height: 12px !important;
490
+ margin-right: 2px;
491
+ flex-shrink: 0;
492
+ }
493
+
494
+ .close_icon {
495
+ margin-right: 2px;
496
+ display: none !important;
497
+ width: 12px !important;
498
+ height: 12px !important;
499
+ line-height: 12px !important;
500
+ flex-shrink: 0;
501
+ opacity: 0.8;
502
+
503
+ &:hover {
504
+ opacity: 1;
505
+ }
506
+ }
507
+
508
+ &:hover {
509
+ opacity: 1;
510
+ .icon {
511
+ display: none;
512
+ }
513
+ .close_icon {
514
+ display: inline-block !important;
515
+ font-size: 12px !important;
516
+ }
517
+ }
518
+ }
519
+
520
+ .context_preview_item_icon {
521
+ display: flex;
522
+ align-items: center;
523
+ justify-content: center;
524
+ flex-shrink: 0;
525
+ opacity: 0.8;
526
+ &::before {
527
+ background-position: 2px !important;
528
+ font-size: 12px;
529
+ background-size: 11px !important;
530
+ }
531
+ }
532
+
533
+ .context_preview_item_text {
534
+ flex: 1;
535
+ overflow: hidden;
536
+ text-overflow: ellipsis;
537
+ color: var(--descriptionForeground);
538
+ white-space: nowrap;
539
+ font-weight: 400;
540
+ line-height: 1.2;
541
+ font-size: 11px;
542
+ max-width: 200px;
543
+ }
544
+
545
+ .context_preview_item_remove {
546
+ cursor: pointer;
547
+ opacity: 0.5;
548
+ font-size: 12px;
549
+ margin-left: 4px;
550
+ padding: 2px;
551
+ border-radius: 50%;
552
+ display: flex;
553
+ align-items: center;
554
+ justify-content: center;
555
+ flex-shrink: 0;
556
+ transition: all 0.2s ease;
557
+ width: 16px;
558
+ height: 16px;
559
+
560
+ &:hover {
561
+ opacity: 1;
562
+ background-color: var(--kt-dangerButton-background);
563
+ color: var(--kt-dangerButton-foreground);
564
+ transform: scale(1.1);
565
+ }
566
+
567
+ &:active {
568
+ transform: scale(0.9);
569
+ }
570
+ }
@@ -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'}