@opensumi/ide-ai-native 3.8.3-next-1745309832.0 → 3.8.3-next-1745568063.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 (32) hide show
  1. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  2. package/lib/browser/chat/chat.view.js +18 -7
  3. package/lib/browser/chat/chat.view.js.map +1 -1
  4. package/lib/browser/components/ChatMentionInput.d.ts +2 -0
  5. package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
  6. package/lib/browser/components/ChatMentionInput.js +69 -23
  7. package/lib/browser/components/ChatMentionInput.js.map +1 -1
  8. package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
  9. package/lib/browser/components/mention-input/mention-input.js +47 -10
  10. package/lib/browser/components/mention-input/mention-input.js.map +1 -1
  11. package/lib/browser/components/mention-input/mention-input.module.less +78 -6
  12. package/lib/browser/components/mention-input/types.d.ts +5 -0
  13. package/lib/browser/components/mention-input/types.d.ts.map +1 -1
  14. package/lib/browser/components/mention-input/types.js.map +1 -1
  15. package/lib/browser/context/llm-context.service.d.ts +1 -0
  16. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  17. package/lib/browser/context/llm-context.service.js.map +1 -1
  18. package/lib/browser/types.d.ts +2 -0
  19. package/lib/browser/types.d.ts.map +1 -1
  20. package/lib/browser/types.js.map +1 -1
  21. package/lib/common/llm-context.d.ts +1 -0
  22. package/lib/common/llm-context.d.ts.map +1 -1
  23. package/lib/common/llm-context.js.map +1 -1
  24. package/package.json +24 -23
  25. package/src/browser/chat/chat.view.tsx +20 -5
  26. package/src/browser/components/ChatMentionInput.tsx +85 -24
  27. package/src/browser/components/mention-input/mention-input.module.less +78 -6
  28. package/src/browser/components/mention-input/mention-input.tsx +86 -12
  29. package/src/browser/components/mention-input/types.ts +7 -0
  30. package/src/browser/context/llm-context.service.ts +1 -0
  31. package/src/browser/types.ts +2 -0
  32. package/src/common/llm-context.ts +6 -3
@@ -1,18 +1,21 @@
1
1
  import { DataContent } from 'ai';
2
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
 
4
4
  import { Image } from '@opensumi/ide-components/lib/image';
5
- import { LabelService, RecentFilesManager, useInjectable } from '@opensumi/ide-core-browser';
5
+ import { LabelService, RecentFilesManager, getSymbolIcon, useInjectable } from '@opensumi/ide-core-browser';
6
6
  import { Icon, getIcon } from '@opensumi/ide-core-browser/lib/components';
7
7
  import { ChatFeatureRegistryToken, URI, localize } from '@opensumi/ide-core-common';
8
8
  import { CommandService } from '@opensumi/ide-core-common/lib/command';
9
9
  import { defaultFilesWatcherExcludes } from '@opensumi/ide-core-common/lib/preferences/file-watch';
10
10
  import { WorkbenchEditorService } from '@opensumi/ide-editor';
11
11
  import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-search';
12
+ import { OutlineCompositeTreeNode, OutlineTreeNode } from '@opensumi/ide-outline/lib/browser/outline-node.define';
13
+ import { OutlineTreeService } from '@opensumi/ide-outline/lib/browser/services/outline-tree.service';
12
14
  import { IMessageService } from '@opensumi/ide-overlay';
13
15
  import { IWorkspaceService } from '@opensumi/ide-workspace';
14
16
 
15
17
  import { IChatInternalService } from '../../common';
18
+ import { LLMContextService } from '../../common/llm-context';
16
19
  import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
17
20
  import { ChatInternalService } from '../chat/chat.internal.service';
18
21
  import { OPEN_MCP_CONFIG_COMMAND } from '../mcp/config/mcp-config.commands';
@@ -48,11 +51,11 @@ export interface IChatMentionInputProps {
48
51
  setCommand: (command: string) => void;
49
52
  disableModelSelector?: boolean;
50
53
  sessionModelId?: string;
54
+ contextService?: LLMContextService;
51
55
  }
52
56
 
53
- // 指令命令激活组件
54
57
  export const ChatMentionInput = (props: IChatMentionInputProps) => {
55
- const { onSend, disabled = false } = props;
58
+ const { onSend, disabled = false, contextService } = props;
56
59
 
57
60
  const [value, setValue] = useState(props.value || '');
58
61
  const [images, setImages] = useState(props.images || []);
@@ -65,6 +68,8 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
65
68
  const labelService = useInjectable<LabelService>(LabelService);
66
69
  const messageService = useInjectable<IMessageService>(IMessageService);
67
70
  const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
71
+ const outlineTreeService = useInjectable<OutlineTreeService>(OutlineTreeService);
72
+ const prevOutlineItems = useRef<MentionItem[]>([]);
68
73
  const handleShowMCPConfig = React.useCallback(() => {
69
74
  commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
70
75
  }, [commandService]);
@@ -75,27 +80,76 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
75
80
  }
76
81
  }, [props.value]);
77
82
 
83
+ const resolveSymbols = useCallback(
84
+ async (parent?: OutlineCompositeTreeNode, symbols: (OutlineTreeNode | OutlineCompositeTreeNode)[] = []) => {
85
+ if (!parent) {
86
+ parent = (await outlineTreeService.resolveChildren())[0] as OutlineCompositeTreeNode;
87
+ }
88
+ const children = (await outlineTreeService.resolveChildren(parent)) as (
89
+ | OutlineTreeNode
90
+ | OutlineCompositeTreeNode
91
+ )[];
92
+ for (const child of children) {
93
+ symbols.push(child);
94
+ if (OutlineCompositeTreeNode.is(child)) {
95
+ await resolveSymbols(child, symbols);
96
+ }
97
+ }
98
+ return symbols;
99
+ },
100
+ [outlineTreeService],
101
+ );
102
+
78
103
  // 默认菜单项
79
104
  const defaultMenuItems: MentionItem[] = [
80
- // {
81
- // id: 'code',
82
- // type: 'code',
83
- // text: 'Code',
84
- // icon: getIcon('codebraces'),
85
- // getHighestLevelItems: () => [],
86
- // getItems: async (searchText: string) => {
87
- // const currentEditor = editorService.currentEditor;
88
- // if (!currentEditor) {
89
- // return [];
90
- // }
91
- // const currentDocumentModel = currentEditor.currentDocumentModel;
92
- // if (!currentDocumentModel) {
93
- // return [];
94
- // }
95
- // const symbols = await commandService.executeCommand('_executeFormatDocumentProvider', currentDocumentModel.uri.codeUri);
96
- // return [];
97
- // },
98
- // },
105
+ {
106
+ id: 'code',
107
+ type: 'code',
108
+ text: 'Code',
109
+ icon: getIcon('codebraces'),
110
+ getHighestLevelItems: () => [],
111
+ getItems: async (searchText: string) => {
112
+ if (!searchText || prevOutlineItems.current.length === 0) {
113
+ const uri = outlineTreeService.currentUri;
114
+ if (!uri) {
115
+ return [];
116
+ }
117
+ const treeNodes = await resolveSymbols();
118
+ prevOutlineItems.current = await Promise.all(
119
+ treeNodes.map(async (treeNode) => {
120
+ const relativePath = await workspaceService.asRelativePath(uri);
121
+ return {
122
+ id: treeNode.raw.id,
123
+ type: MentionType.CODE,
124
+ text: treeNode.raw.name,
125
+ symbol: treeNode.raw,
126
+ value: treeNode.raw.id,
127
+ description: `${relativePath?.root ? relativePath.path : ''}:L${treeNode.raw.range.startLineNumber}-${
128
+ treeNode.raw.range.endLineNumber
129
+ }`,
130
+ kind: treeNode.raw.kind,
131
+ contextId: `${outlineTreeService.currentUri?.codeUri.fsPath}:L${treeNode.raw.range.startLineNumber}-${treeNode.raw.range.endLineNumber}`,
132
+ icon: getSymbolIcon(treeNode.raw.kind) + ' outline-icon',
133
+ };
134
+ }),
135
+ );
136
+ return prevOutlineItems.current;
137
+ } else {
138
+ searchText = searchText.toLocaleLowerCase();
139
+ return prevOutlineItems.current.sort((a, b) => {
140
+ if (a.text.toLocaleLowerCase().includes(searchText) && b.text.toLocaleLowerCase().includes(searchText)) {
141
+ return 0;
142
+ }
143
+ if (a.text.toLocaleLowerCase().includes(searchText)) {
144
+ return -1;
145
+ } else if (b.text.toLocaleLowerCase().includes(searchText)) {
146
+ return 1;
147
+ }
148
+ return 0;
149
+ });
150
+ }
151
+ },
152
+ },
99
153
  {
100
154
  id: MentionType.FILE,
101
155
  type: MentionType.FILE,
@@ -194,7 +248,13 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
194
248
  let folders: MentionItem[] = [];
195
249
  if (!searchText) {
196
250
  const recentFile = await recentFilesManager.getMostRecentlyOpenedFiles();
197
- const recentFolder = Array.from(new Set(recentFile.map((file) => new URI(file).parent.codeUri.fsPath)));
251
+ const recentFolder = Array.from(
252
+ new Set(
253
+ recentFile
254
+ .map((file) => new URI(file).parent.codeUri.fsPath)
255
+ .filter((folder) => folder !== workspaceService.workspace?.uri.toString() && folder !== '/'),
256
+ ),
257
+ );
198
258
  folders = await Promise.all(
199
259
  recentFolder.map(async (folder) => {
200
260
  const uri = new URI(folder);
@@ -354,6 +414,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
354
414
  placeholder={localize('aiNative.chat.input.placeholder.default')}
355
415
  footerConfig={defaultMentionInputFooterOptions}
356
416
  onImageUpload={handleImageUpload}
417
+ contextService={contextService}
357
418
  />
358
419
  </div>
359
420
  );
@@ -12,8 +12,6 @@
12
12
  .editor_area {
13
13
  position: relative;
14
14
  padding: 0 15px;
15
- min-height: 42px;
16
- max-height: 105px;
17
15
  }
18
16
 
19
17
  .editor {
@@ -21,11 +19,11 @@
21
19
  background-color: transparent;
22
20
  border: none;
23
21
  font-size: 14px;
24
- line-height: 1.5;
22
+ line-height: 24px;
23
+ min-height: 72px;
24
+ max-height: 120px;
25
25
  outline: none;
26
26
  resize: none;
27
- min-height: 24px;
28
- max-height: 120px;
29
27
  overflow-y: auto;
30
28
  border-radius: 4px;
31
29
  word-break: break-word;
@@ -142,6 +140,43 @@
142
140
  display: flex;
143
141
  justify-content: center;
144
142
  }
143
+ .context_container {
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ cursor: pointer;
148
+ .context_icon {
149
+ flex-grow: 0;
150
+ flex-shrink: 0;
151
+ :global(.kt-icon) {
152
+ font-size: 12px;
153
+ }
154
+ :global(.kticon-close) {
155
+ display: none;
156
+ }
157
+ }
158
+ &:hover {
159
+ .context_icon {
160
+ :global(.kticon-close) {
161
+ display: block;
162
+ }
163
+ :global(.kticon-out-link) {
164
+ display: none;
165
+ }
166
+ }
167
+ }
168
+ .context_description {
169
+ flex: 1;
170
+ margin-left: 3px;
171
+ margin-right: 10px;
172
+ text-align: left;
173
+ font-size: 11px;
174
+ overflow: hidden;
175
+ text-overflow: ellipsis;
176
+ white-space: nowrap;
177
+ color: var(--descriptionForeground);
178
+ }
179
+ }
145
180
 
146
181
  .mention_panel {
147
182
  background-color: var(--editor-background);
@@ -191,6 +226,7 @@
191
226
 
192
227
  .mention_item_left {
193
228
  display: flex;
229
+ max-width: 100%;
194
230
  align-items: center;
195
231
  flex: 1;
196
232
  }
@@ -198,7 +234,8 @@
198
234
  .mention_item_icon {
199
235
  margin-right: 8px;
200
236
  width: 18px;
201
- height: 18px;
237
+ height: 22px;
238
+ line-height: 22px !important;
202
239
  display: flex;
203
240
  align-items: center;
204
241
  justify-content: center;
@@ -216,6 +253,7 @@
216
253
  font-size: 13px;
217
254
  display: inline;
218
255
  flex: 1;
256
+ direction: rtl;
219
257
  text-overflow: ellipsis;
220
258
  overflow: hidden;
221
259
  white-space: nowrap;
@@ -236,6 +274,40 @@
236
274
  align-items: center;
237
275
  }
238
276
 
277
+ .context_item {
278
+ display: flex;
279
+ align-items: center;
280
+ background-color: var(--badge-background);
281
+ color: var(--badge-foreground);
282
+ border-radius: 4px;
283
+ padding: 2px 6px;
284
+ font-size: 12px;
285
+ }
286
+
287
+ .context_item_icon {
288
+ margin-right: 4px;
289
+ }
290
+
291
+ .context_item_text {
292
+ margin-right: 4px;
293
+ max-width: 150px;
294
+ overflow: hidden;
295
+ text-overflow: ellipsis;
296
+ white-space: nowrap;
297
+ }
298
+
299
+ .context_item_remove {
300
+ cursor: pointer;
301
+ font-size: 12px;
302
+ display: flex;
303
+ align-items: center;
304
+ opacity: 0.7;
305
+
306
+ &:hover {
307
+ opacity: 1;
308
+ }
309
+ }
310
+
239
311
  .back_button {
240
312
  background: none;
241
313
  border: none;
@@ -1,13 +1,23 @@
1
1
  import cls from 'classnames';
2
2
  import * as React from 'react';
3
3
 
4
- import { Popover, PopoverPosition, Select, getIcon } from '@opensumi/ide-core-browser/lib/components';
4
+ import { formatLocalize, getSymbolIcon, localize } from '@opensumi/ide-core-browser';
5
+ import { Icon, Popover, PopoverPosition, Select, getIcon } from '@opensumi/ide-core-browser/lib/components';
5
6
  import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
6
7
  import { URI } from '@opensumi/ide-utils';
7
8
 
9
+ import { FileContext } from '../../../common/llm-context';
10
+
8
11
  import styles from './mention-input.module.less';
9
12
  import { MentionPanel } from './mention-panel';
10
- import { FooterButtonPosition, MENTION_KEYWORD, MentionInputProps, MentionItem, MentionState } from './types';
13
+ import {
14
+ FooterButtonPosition,
15
+ MENTION_KEYWORD,
16
+ MentionInputProps,
17
+ MentionItem,
18
+ MentionState,
19
+ MentionType,
20
+ } from './types';
11
21
 
12
22
  export const WHITE_SPACE_TEXT = '&nbsp;';
13
23
 
@@ -26,6 +36,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
26
36
  buttons: [],
27
37
  showModelSelector: false,
28
38
  },
39
+ contextService,
29
40
  }) => {
30
41
  const editorRef = React.useRef<HTMLDivElement>(null);
31
42
  const [mentionState, setMentionState] = React.useState<MentionState>({
@@ -53,8 +64,14 @@ export const MentionInput: React.FC<MentionInputProps> = ({
53
64
  const [historyIndex, setHistoryIndex] = React.useState<number>(-1);
54
65
  const [currentInput, setCurrentInput] = React.useState<string>('');
55
66
  const [isNavigatingHistory, setIsNavigatingHistory] = React.useState<boolean>(false);
67
+ const [attachedFiles, setAttachedFiles] = React.useState<{
68
+ files: FileContext[];
69
+ folders: FileContext[];
70
+ }>({
71
+ files: [],
72
+ folders: [],
73
+ });
56
74
 
57
- // 获取当前菜单项
58
75
  const getCurrentItems = (): MentionItem[] => {
59
76
  if (mentionState.level === 0) {
60
77
  return mentionItems;
@@ -70,7 +87,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
70
87
  return [];
71
88
  };
72
89
 
73
- // 添加防抖函数
74
90
  const useDebounce = <T,>(value: T, delay: number): T => {
75
91
  const [debouncedValue, setDebouncedValue] = React.useState<T>(value);
76
92
 
@@ -87,14 +103,12 @@ export const MentionInput: React.FC<MentionInputProps> = ({
87
103
  return debouncedValue;
88
104
  };
89
105
 
90
- // 使用防抖处理搜索文本
91
106
  const debouncedSecondLevelFilter = useDebounce(mentionState.secondLevelFilter, 300);
92
107
 
93
108
  React.useEffect(() => {
94
109
  setSelectedModel(footerConfig.defaultModel || '');
95
110
  }, [footerConfig.defaultModel]);
96
111
 
97
- // 监听搜索文本变化,实时更新二级菜单
98
112
  React.useEffect(() => {
99
113
  if (mentionState.level === 1 && mentionState.parentType && debouncedSecondLevelFilter !== undefined) {
100
114
  // 查找父级菜单项
@@ -162,6 +176,16 @@ export const MentionInput: React.FC<MentionInputProps> = ({
162
176
  }
163
177
  }, [debouncedSecondLevelFilter, mentionState.level, mentionState.parentType]);
164
178
 
179
+ React.useEffect(() => {
180
+ const disposable = contextService?.onDidContextFilesChangeEvent(({ attached, attachedFolders }) => {
181
+ setAttachedFiles({ files: attached, folders: attachedFolders });
182
+ });
183
+
184
+ return () => {
185
+ disposable?.dispose();
186
+ };
187
+ }, [contextService]);
188
+
165
189
  // 获取光标位置
166
190
  const getCursorPosition = (element: HTMLElement): number => {
167
191
  const selection = window.getSelection();
@@ -176,7 +200,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
176
200
  return preCaretRange.toString().length;
177
201
  };
178
202
 
179
- // 处理输入事件
180
203
  const handleInput = () => {
181
204
  // 如果用户开始输入,退出历史导航模式
182
205
  if (isNavigatingHistory) {
@@ -262,7 +285,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
262
285
  // 检查输入框高度,如果超过最大高度则添加滚动条
263
286
  if (editorRef.current) {
264
287
  const editorHeight = editorRef.current.scrollHeight;
265
- if (editorHeight > 120) {
288
+ if (editorHeight >= 120) {
266
289
  editorRef.current.style.overflowY = 'auto';
267
290
  } else {
268
291
  editorRef.current.style.overflowY = 'hidden';
@@ -305,6 +328,15 @@ export const MentionInput: React.FC<MentionInputProps> = ({
305
328
  return;
306
329
  }
307
330
 
331
+ // 当输入框为空时,处理删除键 (Backspace) 或 Delete 键来删除上下文内容
332
+ if (
333
+ (e.key === 'Backspace' || e.key === 'Delete') &&
334
+ editorRef.current &&
335
+ (!editorRef.current.textContent || editorRef.current.textContent.trim() === '')
336
+ ) {
337
+ contextService?.cleanFileContext();
338
+ }
339
+
308
340
  // 添加对 @ 键的监听,支持在任意位置触发菜单
309
341
  if (e.key === MENTION_KEYWORD && !mentionState.active && !mentionState.inlineSearchActive && editorRef.current) {
310
342
  const cursorPos = getCursorPosition(editorRef.current);
@@ -665,15 +697,30 @@ export const MentionInput: React.FC<MentionInputProps> = ({
665
697
  mentionTag.dataset.contextId = item.contextId || '';
666
698
  mentionTag.contentEditable = 'false';
667
699
 
668
- // file folder 类型添加图标
669
- if (item.type === 'file' || item.type === 'folder') {
700
+ if (item.type === MentionType.FILE || item.type === MentionType.FOLDER) {
670
701
  // 创建图标容器
671
702
  const iconSpan = document.createElement('span');
672
703
  iconSpan.className = cls(
673
704
  styles.mention_icon,
674
- item.type === 'file' ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'),
705
+ item.type === MentionType.FILE ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'),
675
706
  );
676
707
  mentionTag.appendChild(iconSpan);
708
+ if (item.type === MentionType.FOLDER) {
709
+ contextService?.addFolderToContext(new URI(item.contextId), true);
710
+ } else {
711
+ contextService?.addFileToContext(new URI(item.contextId), undefined, true);
712
+ }
713
+ } else if (item.type === MentionType.CODE) {
714
+ const iconSpan = document.createElement('span');
715
+ iconSpan.className = cls(styles.mention_icon, item.kind && getSymbolIcon(item.kind) + ' outline-icon');
716
+ mentionTag.appendChild(iconSpan);
717
+ if (item.symbol) {
718
+ contextService?.addFileToContext(
719
+ new URI(item.contextId),
720
+ [item.symbol.range.startLineNumber, item.symbol.range.endLineNumber],
721
+ true,
722
+ );
723
+ }
677
724
  }
678
725
  const workspace = workspaceService?.workspace;
679
726
  let relativePath = item.text;
@@ -913,7 +960,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
913
960
  setHistoryIndex(-1);
914
961
  setIsNavigatingHistory(false);
915
962
  }
916
-
917
963
  if (onSend) {
918
964
  // 传递当前选择的模型和其他配置信息
919
965
  onSend(processedContent, {
@@ -931,6 +977,10 @@ export const MentionInput: React.FC<MentionInputProps> = ({
931
977
  }
932
978
  };
933
979
 
980
+ const handleClearContext = React.useCallback(() => {
981
+ contextService?.cleanFileContext();
982
+ }, [contextService]);
983
+
934
984
  const handleStop = React.useCallback(() => {
935
985
  if (onStop) {
936
986
  onStop();
@@ -962,6 +1012,11 @@ export const MentionInput: React.FC<MentionInputProps> = ({
962
1012
  [footerConfig.buttons],
963
1013
  );
964
1014
 
1015
+ const hasContext = React.useMemo(
1016
+ () => attachedFiles.files.length > 0 || attachedFiles.folders.length > 0,
1017
+ [attachedFiles],
1018
+ );
1019
+
965
1020
  return (
966
1021
  <div className={styles.input_container}>
967
1022
  {mentionState.active && (
@@ -1005,6 +1060,25 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1005
1060
  </div>
1006
1061
  <div className={styles.right_control}>
1007
1062
  {renderButtons(FooterButtonPosition.RIGHT)}
1063
+ <Popover
1064
+ overlayClassName={styles.popover_icon}
1065
+ id={'ai-chat-clear-context'}
1066
+ position={PopoverPosition.top}
1067
+ content={localize('aiNative.chat.context.clear')}
1068
+ >
1069
+ <div className={styles.context_container} onClick={handleClearContext}>
1070
+ <div className={styles.context_icon}>
1071
+ <Icon icon='out-link' />
1072
+ <Icon icon='close' />
1073
+ </div>
1074
+ <div className={styles.context_description}>
1075
+ {formatLocalize(
1076
+ 'aiNative.chat.context.description',
1077
+ attachedFiles.files.length + attachedFiles.folders.length,
1078
+ )}
1079
+ </div>
1080
+ </div>
1081
+ </Popover>
1008
1082
  <Popover
1009
1083
  overlayClassName={styles.popover_icon}
1010
1084
  id={'ai-chat-send'}
@@ -1,3 +1,7 @@
1
+ import { DocumentSymbol, SymbolKind } from '@opensumi/ide-monaco';
2
+
3
+ import { LLMContextService } from '../../../common/llm-context';
4
+
1
5
  import type { LabelService } from '@opensumi/ide-core-browser';
2
6
  import type { IWorkspaceService } from '@opensumi/ide-workspace';
3
7
 
@@ -8,7 +12,9 @@ export interface MentionItem {
8
12
  value?: string;
9
13
  description?: string;
10
14
  contextId?: string;
15
+ symbol?: DocumentSymbol;
11
16
  icon?: string;
17
+ kind?: SymbolKind;
12
18
  getHighestLevelItems?: () => MentionItem[];
13
19
  getItems?: (searchText: string) => Promise<MentionItem[]>;
14
20
  }
@@ -82,6 +88,7 @@ export interface MentionInputProps {
82
88
  mentionKeyword?: string;
83
89
  labelService?: LabelService;
84
90
  workspaceService?: IWorkspaceService;
91
+ contextService?: LLMContextService;
85
92
  }
86
93
 
87
94
  export const MENTION_KEYWORD = '@';
@@ -45,6 +45,7 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
45
45
  private readonly onDidContextFilesChangeEmitter = new Emitter<{
46
46
  viewed: FileContext[];
47
47
  attached: FileContext[];
48
+ attachedFolders: FileContext[];
48
49
  version: number;
49
50
  }>();
50
51
  onDidContextFilesChangeEvent = this.onDidContextFilesChangeEmitter.event;
@@ -28,6 +28,7 @@ import { SumiReadableStream } from '@opensumi/ide-utils/lib/stream';
28
28
  import { IMarker } from '@opensumi/monaco-editor-core/esm/vs/platform/markers/common/markers';
29
29
 
30
30
  import { IChatWelcomeMessageContent, ISampleQuestions, ITerminalCommandSuggestionDesc } from '../common';
31
+ import { LLMContextService } from '../common/llm-context';
31
32
 
32
33
  import {
33
34
  ICodeEditsContextBean,
@@ -166,6 +167,7 @@ export type ChatInputRender = (props: {
166
167
  defaultAgentId?: string;
167
168
  command: string;
168
169
  setCommand: (theme: string) => void;
170
+ contextService?: LLMContextService;
169
171
  }) => React.ReactElement | React.JSX.Element;
170
172
  export type ChatViewHeaderRender = (props: {
171
173
  handleClear: () => any;
@@ -1,5 +1,3 @@
1
- import { DataContent } from 'ai';
2
-
3
1
  import { Event, URI } from '@opensumi/ide-core-common/lib/utils';
4
2
 
5
3
  export interface LLMContextService {
@@ -31,7 +29,12 @@ export interface LLMContextService {
31
29
  /**
32
30
  * 上下文文件变化事件
33
31
  */
34
- onDidContextFilesChangeEvent: Event<{ viewed: FileContext[]; attached: FileContext[]; version: number }>;
32
+ onDidContextFilesChangeEvent: Event<{
33
+ viewed: FileContext[];
34
+ attached: FileContext[];
35
+ attachedFolders: FileContext[];
36
+ version: number;
37
+ }>;
35
38
 
36
39
  /**
37
40
  * 从 context 中移除文件