@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
@@ -1,18 +1,16 @@
1
1
  import { Autowired, Injectable } from '@opensumi/di';
2
- import { AppConfig, PreferenceService } from '@opensumi/ide-core-browser';
2
+ import { PreferenceService } from '@opensumi/ide-core-browser';
3
3
  import {
4
4
  AIBackSerivcePath,
5
5
  CancellationToken,
6
6
  ChatAgentViewServiceToken,
7
7
  ChatFeatureRegistryToken,
8
- ChatServiceToken,
9
8
  Deferred,
10
9
  Disposable,
11
10
  IAIBackService,
12
11
  IAIReporter,
13
12
  IApplicationService,
14
13
  IChatProgress,
15
- getOperatingSystemName,
16
14
  } from '@opensumi/ide-core-common';
17
15
  import { AINativeSettingSectionsId } from '@opensumi/ide-core-common/lib/settings/ai-native';
18
16
  import { MonacoCommandRegistry } from '@opensumi/ide-editor/lib/browser/monaco-contrib/command/command.service';
@@ -27,10 +25,10 @@ import {
27
25
  IChatAgentService,
28
26
  IChatAgentWelcomeMessage,
29
27
  } from '../../common';
28
+ import { DEFAULT_SYSTEM_PROMPT } from '../../common/prompts/system-prompt';
30
29
  import { ChatToolRender } from '../components/ChatToolRender';
31
30
  import { IChatAgentViewService } from '../types';
32
31
 
33
- import { ChatService } from './chat.api.service';
34
32
  import { ChatFeatureRegistry } from './chat.feature.registry';
35
33
 
36
34
  /**
@@ -53,9 +51,6 @@ export class ChatProxyService extends Disposable {
53
51
  @Autowired(MonacoCommandRegistry)
54
52
  private readonly monacoCommandRegistry: MonacoCommandRegistry;
55
53
 
56
- @Autowired(ChatServiceToken)
57
- private aiChatService: ChatService;
58
-
59
54
  @Autowired(IAIReporter)
60
55
  private readonly aiReporter: IAIReporter;
61
56
 
@@ -71,9 +66,6 @@ export class ChatProxyService extends Disposable {
71
66
  @Autowired(IMessageService)
72
67
  private readonly messageService: IMessageService;
73
68
 
74
- @Autowired(AppConfig)
75
- private readonly appConfig: AppConfig;
76
-
77
69
  private chatDeferred: Deferred<void> = new Deferred<void>();
78
70
 
79
71
  public getRequestOptions() {
@@ -112,79 +104,74 @@ export class ChatProxyService extends Disposable {
112
104
  initialProps: {},
113
105
  });
114
106
 
115
- this.addDispose(
116
- this.chatAgentService.registerAgent({
117
- id: ChatProxyService.AGENT_ID,
118
- metadata: {
119
- systemPrompt:
120
- this.preferenceService.get<string>(
107
+ this.applicationService.getBackendOS().then(() => {
108
+ this.addDispose(
109
+ this.chatAgentService.registerAgent({
110
+ id: ChatProxyService.AGENT_ID,
111
+ metadata: {
112
+ systemPrompt: this.preferenceService.get<string>(
121
113
  AINativeSettingSectionsId.SystemPrompt,
122
- 'You are a powerful AI coding assistant working in OpenSumi, a top IDE framework. You collaborate with a USER to solve coding tasks, which may involve creating, modifying, or debugging code, or answering questions. When the USER sends a message, relevant context (e.g., open files, cursor position, edit history, linter errors) may be attached. Use this information as needed.\n\n<tool_calling>\nYou have access to tools to assist with tasks. Follow these rules:\n1. Always adhere to the tool call schema and provide all required parameters.\n2. Only use tools explicitly provided; ignore unavailable ones.\n3. Avoid mentioning tool names to the USER (e.g., say "I will edit your file" instead of "I need to use the edit_file tool").\n4. Only call tools when necessary; respond directly if the task is general or you already know the answer.\n5. Explain to the USER why you’re using a tool before calling it.\n</tool_calling>\n\n<making_code_changes>\nWhen modifying code:\n1. Use code edit tools instead of outputting code unless explicitly requested.\n2. Limit tool calls to one per turn.\n3. Ensure generated code is immediately executable by including necessary imports, dependencies, and endpoints.\n4. For new projects, create a dependency management file (e.g., requirements.txt) and a README.\n5. For web apps, design a modern, user-friendly UI.\n6. Avoid generating non-textual or excessively long code.\n7. Read file contents before editing, unless appending a small change or creating a new file.\n8. Fix introduced linter errors if possible, but stop after 3 attempts and ask the USER for guidance.\n9. Reapply reasonable code edits if they weren’t followed initially.\n</making_code_changes>\n\nUse the appropriate tools to fulfill the USER’s request, ensuring all required parameters are provided or inferred from context.',
123
- ) +
124
- `\n\n<user_info>\nThe user's OS is ${getOperatingSystemName()}. The absolute path of the user's workspace is ${
125
- this.appConfig.workspaceDir
126
- }.\n</user_info>`,
127
- },
128
- invoke: async (
129
- request: IChatAgentRequest,
130
- progress: (part: IChatProgress) => void,
131
- history: CoreMessage[],
132
- token: CancellationToken,
133
- ): Promise<IChatAgentResult> => {
134
- this.chatDeferred = new Deferred<void>();
135
-
136
- const { message, command } = request;
137
- let prompt: string = message;
138
-
139
- if (command) {
140
- const commandHandler = this.chatFeatureRegistry.getSlashCommandHandler(command);
141
- if (commandHandler && commandHandler.providerPrompt) {
142
- const editor = this.monacoCommandRegistry.getActiveCodeEditor();
143
- const slashCommandPrompt = await commandHandler.providerPrompt(message, editor);
144
- prompt = slashCommandPrompt;
114
+ DEFAULT_SYSTEM_PROMPT,
115
+ ),
116
+ },
117
+ invoke: async (
118
+ request: IChatAgentRequest,
119
+ progress: (part: IChatProgress) => void,
120
+ history: CoreMessage[],
121
+ token: CancellationToken,
122
+ ): Promise<IChatAgentResult> => {
123
+ this.chatDeferred = new Deferred<void>();
124
+ const { message, command } = request;
125
+ let prompt: string = message;
126
+ if (command) {
127
+ const commandHandler = this.chatFeatureRegistry.getSlashCommandHandler(command);
128
+ if (commandHandler && commandHandler.providerPrompt) {
129
+ const editor = this.monacoCommandRegistry.getActiveCodeEditor();
130
+ const slashCommandPrompt = await commandHandler.providerPrompt(message, editor);
131
+ prompt = slashCommandPrompt;
132
+ }
145
133
  }
146
- }
147
-
148
- const stream = await this.aiBackService.requestStream(
149
- prompt,
150
- {
151
- requestId: request.requestId,
152
- sessionId: request.sessionId,
153
- history,
154
- images: request.images,
155
- ...this.getRequestOptions(),
156
- },
157
- token,
158
- );
159
-
160
- listenReadable<IChatProgress>(stream, {
161
- onData: (data) => {
162
- progress(data);
163
- },
164
- onEnd: () => {
165
- this.chatDeferred.resolve();
166
- },
167
- onError: (error) => {
168
- this.messageService.error(error.message);
169
- this.aiReporter.end(request.sessionId + '_' + request.requestId, {
170
- message: error.message,
171
- success: false,
172
- command,
173
- });
174
- },
175
- });
176
-
177
- await this.chatDeferred.promise;
178
- return {};
179
- },
180
- provideSlashCommands: async (token: CancellationToken): Promise<IChatAgentCommand[]> =>
181
- this.chatFeatureRegistry
182
- .getAllSlashCommand()
183
- .map((s) => ({ ...s, name: s.name, description: s.description || '' })),
184
- provideChatWelcomeMessage: async (token: CancellationToken): Promise<IChatAgentWelcomeMessage | undefined> =>
185
- undefined,
186
- }),
187
- );
134
+
135
+ const stream = await this.aiBackService.requestStream(
136
+ prompt,
137
+ {
138
+ requestId: request.requestId,
139
+ sessionId: request.sessionId,
140
+ history,
141
+ images: request.images,
142
+ ...this.getRequestOptions(),
143
+ },
144
+ token,
145
+ );
146
+
147
+ listenReadable<IChatProgress>(stream, {
148
+ onData: (data) => {
149
+ progress(data);
150
+ },
151
+ onEnd: () => {
152
+ this.chatDeferred.resolve();
153
+ },
154
+ onError: (error) => {
155
+ this.messageService.error(error.message);
156
+ this.aiReporter.end(request.sessionId + '_' + request.requestId, {
157
+ message: error.message,
158
+ success: false,
159
+ command,
160
+ });
161
+ },
162
+ });
163
+
164
+ await this.chatDeferred.promise;
165
+ return {};
166
+ },
167
+ provideSlashCommands: async (): Promise<IChatAgentCommand[]> =>
168
+ this.chatFeatureRegistry
169
+ .getAllSlashCommand()
170
+ .map((s) => ({ ...s, name: s.name, description: s.description || '' })),
171
+ provideChatWelcomeMessage: async (): Promise<IChatAgentWelcomeMessage | undefined> => undefined,
172
+ }),
173
+ );
174
+ });
188
175
 
189
176
  queueMicrotask(() => {
190
177
  this.chatAgentService.updateAgent(ChatProxyService.AGENT_ID, {});
@@ -617,7 +617,12 @@ export const AIChatView = () => {
617
617
  const { message, images, agentId, command, reportExtra } = value;
618
618
  const { actionType, actionSource } = reportExtra || {};
619
619
 
620
- const request = aiChatService.createRequest(message, agentId!, images, command);
620
+ const request = aiChatService.createRequest(
621
+ message.replaceAll(LLM_CONTEXT_KEY_REGEX, ''),
622
+ agentId!,
623
+ images,
624
+ command,
625
+ );
621
626
  if (!request) {
622
627
  return;
623
628
  }
@@ -641,7 +646,7 @@ export const AIChatView = () => {
641
646
  600 * 1000,
642
647
  );
643
648
  msgHistoryManager.addUserMessage({
644
- content: message.replaceAll(LLM_CONTEXT_KEY_REGEX, ''),
649
+ content: message,
645
650
  images: images || [],
646
651
  agentId: agentId!,
647
652
  agentCommand: command!,
@@ -695,14 +700,9 @@ export const AIChatView = () => {
695
700
  let processedContent = message;
696
701
  const filePattern = /\{\{@file:(.*?)\}\}/g;
697
702
  const fileMatches = message.match(filePattern);
698
- let isCleanContext = false;
699
703
  if (fileMatches) {
700
704
  for (const match of fileMatches) {
701
705
  const filePath = match.replace(/\{\{@file:(.*?)\}\}/, '$1');
702
- if (filePath && !isCleanContext) {
703
- isCleanContext = true;
704
- llmContextService.cleanFileContext();
705
- }
706
706
  const fileUri = new URI(filePath);
707
707
  const relativePath = (await workspaceService.asRelativePath(fileUri))?.path || fileUri.displayName;
708
708
  processedContent = processedContent.replace(match, `\`${LLM_CONTEXT_KEY.AttachedFile}${relativePath}\``);
@@ -738,6 +738,18 @@ export const AIChatView = () => {
738
738
  );
739
739
  }
740
740
  }
741
+ const rulePattern = /\{\{@rule:(.*?)\}\}/g;
742
+ const ruleMatches = processedContent.match(rulePattern);
743
+ if (ruleMatches) {
744
+ for (const match of ruleMatches) {
745
+ const ruleName = match.replace(/\{\{@rule:(.*?)\}\}/, '$1');
746
+ const ruleUri = new URI(ruleName);
747
+ processedContent = processedContent.replace(
748
+ match,
749
+ `\`${LLM_CONTEXT_KEY.AttachedFile}${ruleUri.displayName}\``,
750
+ );
751
+ }
752
+ }
741
753
  return handleAgentReply({ message: processedContent, images, agentId, command, reportExtra });
742
754
  },
743
755
  [handleAgentReply],
@@ -10,7 +10,13 @@ import {
10
10
  useInjectable,
11
11
  } from '@opensumi/ide-core-browser';
12
12
  import { Icon, getIcon } from '@opensumi/ide-core-browser/lib/components';
13
- import { AINativeSettingSectionsId, ChatFeatureRegistryToken, URI, localize } from '@opensumi/ide-core-common';
13
+ import {
14
+ AINativeSettingSectionsId,
15
+ ChatFeatureRegistryToken,
16
+ RulesServiceToken,
17
+ URI,
18
+ localize,
19
+ } from '@opensumi/ide-core-common';
14
20
  import { CommandService } from '@opensumi/ide-core-common/lib/command';
15
21
  import { defaultFilesWatcherExcludes } from '@opensumi/ide-core-common/lib/preferences/file-watch';
16
22
  import { WorkbenchEditorService } from '@opensumi/ide-editor';
@@ -18,6 +24,8 @@ import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-se
18
24
  import { OutlineCompositeTreeNode, OutlineTreeNode } from '@opensumi/ide-outline/lib/browser/outline-node.define';
19
25
  import { OutlineTreeService } from '@opensumi/ide-outline/lib/browser/services/outline-tree.service';
20
26
  import { IMessageService } from '@opensumi/ide-overlay';
27
+ import { IconType } from '@opensumi/ide-theme';
28
+ import { IconService } from '@opensumi/ide-theme/lib/browser';
21
29
  import { IWorkspaceService } from '@opensumi/ide-workspace';
22
30
 
23
31
  import { IChatInternalService } from '../../common';
@@ -25,6 +33,8 @@ import { LLMContextService } from '../../common/llm-context';
25
33
  import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
26
34
  import { ChatInternalService } from '../chat/chat.internal.service';
27
35
  import { MCPConfigCommands } from '../mcp/config/mcp-config.commands';
36
+ import { RulesCommands } from '../rules/rules.contribution';
37
+ import { RulesService } from '../rules/rules.service';
28
38
 
29
39
  import styles from './components.module.less';
30
40
  import { MentionInput } from './mention-input/mention-input';
@@ -72,15 +82,21 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
72
82
  const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
73
83
  const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
74
84
  const labelService = useInjectable<LabelService>(LabelService);
85
+ const iconService = useInjectable<IconService>(IconService);
75
86
  const messageService = useInjectable<IMessageService>(IMessageService);
76
87
  const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
77
88
  const outlineTreeService = useInjectable<OutlineTreeService>(OutlineTreeService);
78
89
  const prevOutlineItems = useRef<MentionItem[]>([]);
79
90
  const preferenceService = useInjectable<PreferenceService>(PreferenceService);
91
+ const rulesService = useInjectable<RulesService>(RulesServiceToken);
80
92
  const handleShowMCPConfig = React.useCallback(() => {
81
93
  commandService.executeCommand(MCPConfigCommands.OPEN_MCP_CONFIG.id);
82
94
  }, [commandService]);
83
95
 
96
+ const handleShowRules = React.useCallback(() => {
97
+ commandService.executeCommand(RulesCommands.OPEN_RULES_FILE.id);
98
+ }, [commandService]);
99
+
84
100
  useEffect(() => {
85
101
  if (props.value !== value) {
86
102
  setValue(props.value || '');
@@ -107,6 +123,55 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
107
123
  [outlineTreeService],
108
124
  );
109
125
 
126
+ // 拆分目录路径为多个层级的辅助函数
127
+ const expandFolderPaths = async (folderPaths: string[], workspaceRootPath: string): Promise<MentionItem[]> => {
128
+ const expandedPaths = new Set<string>();
129
+ const workspaceUri = new URI(workspaceRootPath);
130
+
131
+ // 将所有路径展开为多层级
132
+ for (const folderPath of folderPaths) {
133
+ const uri = new URI(folderPath);
134
+ const relativePath = await workspaceService.asRelativePath(uri);
135
+
136
+ if (relativePath?.path) {
137
+ const pathSegments = relativePath.path.split('/').filter(Boolean);
138
+
139
+ // 为每个层级创建路径
140
+ for (let i = 0; i < pathSegments.length; i++) {
141
+ const segmentPath = pathSegments.slice(0, i + 1).join('/');
142
+ const fullPath = workspaceUri.resolve(segmentPath).codeUri.fsPath;
143
+
144
+ // 避免添加工作区本身或其上级目录
145
+ if (fullPath !== workspaceRootPath && !workspaceRootPath.startsWith(fullPath)) {
146
+ expandedPaths.add(fullPath);
147
+ }
148
+ }
149
+ } else {
150
+ // 如果无法获取相对路径,直接添加(但仍要过滤工作区路径)
151
+ if (folderPath !== workspaceRootPath && !workspaceRootPath.startsWith(folderPath)) {
152
+ expandedPaths.add(folderPath);
153
+ }
154
+ }
155
+ }
156
+
157
+ // 转换为 MentionItem 格式
158
+ return Promise.all(
159
+ Array.from(expandedPaths).map(async (folderPath) => {
160
+ const uri = new URI(folderPath);
161
+ const relativePath = await workspaceService.asRelativePath(uri);
162
+ return {
163
+ id: uri.codeUri.fsPath,
164
+ type: MentionType.FOLDER,
165
+ text: uri.displayName,
166
+ value: uri.codeUri.fsPath,
167
+ description: relativePath?.root ? relativePath.path : '',
168
+ contextId: uri.codeUri.fsPath,
169
+ icon: getIcon('folder'),
170
+ };
171
+ }),
172
+ );
173
+ };
174
+
110
175
  // 默认菜单项
111
176
  const defaultMenuItems: MentionItem[] = [
112
177
  {
@@ -214,21 +279,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
214
279
  .filter((folder) => folder !== workspaceService.workspace?.uri.toString() && folder !== '/'),
215
280
  ),
216
281
  );
217
- folders = await Promise.all(
218
- recentFolder.map(async (folder) => {
219
- const uri = new URI(folder);
220
- const relativePath = await workspaceService.asRelativePath(uri);
221
- return {
222
- id: uri.codeUri.fsPath,
223
- type: MentionType.FOLDER,
224
- text: uri.displayName,
225
- value: uri.codeUri.fsPath,
226
- description: relativePath?.root ? relativePath.path : '',
227
- contextId: uri.codeUri.fsPath,
228
- icon: getIcon('folder'),
229
- };
230
- }),
231
- );
282
+ folders = await expandFolderPaths(recentFolder, workspaceService.workspace?.uri.toString() || '');
232
283
  } else {
233
284
  const rootUris = (await workspaceService.roots).map((root) => new URI(root.uri).codeUri.fsPath.toString());
234
285
  const files = await searchService.find(searchText, {
@@ -246,22 +297,11 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
246
297
  .filter((folder) => folder !== workspaceService.workspace?.uri.toString()),
247
298
  ),
248
299
  );
249
- return Promise.all(
250
- folders.map(async (folder) => {
251
- const uri = new URI(folder);
252
- return {
253
- id: uri.codeUri.fsPath,
254
- type: MentionType.FOLDER,
255
- text: uri.displayName,
256
- value: uri.codeUri.fsPath,
257
- description: (await workspaceService.asRelativePath(uri.parent))?.path || '',
258
- contextId: uri.codeUri.fsPath,
259
- icon: getIcon('folder'),
260
- };
261
- }),
262
- );
300
+ return await expandFolderPaths(folders, workspaceService.workspace?.uri.toString() || '');
263
301
  }
264
- return folders.filter((folder) => folder.id !== new URI(workspaceService.workspace?.uri).codeUri.fsPath);
302
+ return folders
303
+ .filter(Boolean)
304
+ .filter((folder) => folder.id !== new URI(workspaceService.workspace?.uri).codeUri.fsPath);
265
305
  },
266
306
  },
267
307
  {
@@ -312,13 +352,100 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
312
352
  }
313
353
  },
314
354
  },
315
- ];
355
+ {
356
+ id: MentionType.RULE,
357
+ type: MentionType.RULE,
358
+ text: 'Rule',
359
+ icon: getIcon('rules'),
360
+ getHighestLevelItems: () => [],
361
+ getItems: async (searchText: string) => {
362
+ const rules = await rulesService.projectRules;
363
+ const mappedRules = rules.map((rule) => {
364
+ const uri = new URI(rule.path);
365
+ return {
366
+ id: uri.codeUri.fsPath,
367
+ type: MentionType.RULE,
368
+ text: uri.displayName,
369
+ value: uri.codeUri.fsPath,
370
+ contextId: uri.codeUri.fsPath,
371
+ description: rule.description,
372
+ icon: getIcon('rules'),
373
+ };
374
+ });
375
+
376
+ if (!searchText) {
377
+ return mappedRules.slice(0, 10);
378
+ }
379
+
380
+ const lowerSearchText = searchText.toLocaleLowerCase();
381
+ return mappedRules
382
+ .filter((rule) => rule.text.toLocaleLowerCase().includes(lowerSearchText))
383
+ .sort((a, b) => {
384
+ const aTextLower = a.text.toLocaleLowerCase();
385
+ const bTextLower = b.text.toLocaleLowerCase();
386
+ const aDescLower = a.description?.toLocaleLowerCase() || '';
387
+ const bDescLower = b.description?.toLocaleLowerCase() || '';
316
388
 
389
+ // 优先级:文件名包含搜索文本 > 描述包含搜索文本
390
+ const aTextMatch = aTextLower.includes(lowerSearchText);
391
+ const bTextMatch = bTextLower.includes(lowerSearchText);
392
+ const aDescMatch = aDescLower.includes(lowerSearchText);
393
+ const bDescMatch = bDescLower.includes(lowerSearchText);
394
+
395
+ if (aTextMatch && bTextMatch) {
396
+ // 如果都匹配文件名,按文件名字母序排序
397
+ return aTextLower.localeCompare(bTextLower);
398
+ }
399
+ if (aTextMatch && !bTextMatch) {
400
+ return -1;
401
+ }
402
+ if (!aTextMatch && bTextMatch) {
403
+ return 1;
404
+ }
405
+
406
+ // 如果文件名都不匹配,比较描述
407
+ if (aDescMatch && bDescMatch) {
408
+ return aTextLower.localeCompare(bTextLower);
409
+ }
410
+ if (aDescMatch && !bDescMatch) {
411
+ return -1;
412
+ }
413
+ if (!aDescMatch && bDescMatch) {
414
+ return 1;
415
+ }
416
+
417
+ // 如果都不匹配,按文件名字母序排序
418
+ return aTextLower.localeCompare(bTextLower);
419
+ })
420
+ .slice(0, 10);
421
+ },
422
+ },
423
+ ];
317
424
  const defaultMentionInputFooterOptions: FooterConfig = useMemo(
318
425
  () => ({
319
426
  modelOptions: [
320
- { label: 'Claude 4 Sonnet', value: 'claude_sonnet4' },
321
- { label: 'DeepSeek R1', value: 'deepseek-r1' },
427
+ {
428
+ label: 'Claude 4 Sonnet',
429
+ value: 'claude_sonnet4',
430
+ iconClass: iconService.fromIcon(
431
+ '',
432
+ 'https://img.alicdn.com/imgextra/i3/O1CN01p0mziz1Nsl40lp1HO_!!6000000001626-55-tps-92-65.svg',
433
+ IconType.Background,
434
+ ),
435
+ tags: ['多模态', '长上下文理解', '思考模式'],
436
+ description: '高性能模型,支持多模态输入',
437
+ },
438
+ {
439
+ label: 'DeepSeek R1',
440
+ value: 'DeepSeek-R1-0528',
441
+ iconClass: iconService.fromIcon(
442
+ '',
443
+ 'https://img.alicdn.com/imgextra/i3/O1CN01ClcK2w1JwdxcbAB3a_!!6000000001093-55-tps-30-30.svg',
444
+ IconType.Background,
445
+ ),
446
+ tags: ['思考模式', '长上下文理解'],
447
+ description: '专业创作,支持多模态输入',
448
+ },
322
449
  ],
323
450
  defaultModel:
324
451
  props.sessionModelId || preferenceService.get<string>(AINativeSettingSectionsId.ModelID) || 'deepseek-r1',
@@ -330,6 +457,13 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
330
457
  onClick: handleShowMCPConfig,
331
458
  position: FooterButtonPosition.LEFT,
332
459
  },
460
+ {
461
+ id: 'rules',
462
+ icon: 'rules',
463
+ title: 'Rules',
464
+ onClick: handleShowRules,
465
+ position: FooterButtonPosition.LEFT,
466
+ },
333
467
  {
334
468
  id: 'upload-image',
335
469
  icon: 'image',
@@ -352,7 +486,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
352
486
  showModelSelector: true,
353
487
  disableModelSelector: props.disableModelSelector,
354
488
  }),
355
- [handleShowMCPConfig, props.disableModelSelector, props.sessionModelId],
489
+ [iconService, handleShowMCPConfig, handleShowRules, props.disableModelSelector, props.sessionModelId],
356
490
  );
357
491
 
358
492
  const handleStop = useCallback(() => {
@@ -342,10 +342,10 @@ export const ChatReply = (props: IChatReplyProps) => {
342
342
  }}
343
343
  >
344
344
  <Icon iconClass='codicon codicon-sparkle' />
345
- {localize('aiNative.chat.thinking')}
346
- {isThinking ? (
347
- <Loading />
348
- ) : collapseThinkingIndexSet.has(index) ? (
345
+ <span style={{ marginLeft: 4 }} className={isThinking ? styles.shiny_text : ''}>
346
+ {localize('aiNative.chat.thinking')}
347
+ </span>
348
+ {isThinking ? null : collapseThinkingIndexSet.has(index) ? (
349
349
  <Icon iconClass='codicon codicon-chevron-right' />
350
350
  ) : (
351
351
  <Icon iconClass='codicon codicon-chevron-down' />
@@ -52,6 +52,7 @@
52
52
  flex: 1;
53
53
  text-overflow: ellipsis;
54
54
  direction: rtl;
55
+ unicode-bidi: plaintext;
55
56
  overflow: hidden;
56
57
  white-space: nowrap;
57
58
  display: inline-block;
@@ -524,7 +524,6 @@
524
524
  display: flex;
525
525
  font-size: 11px;
526
526
  align-items: center;
527
- min-width: 150px;
528
527
  }
529
528
 
530
529
  .mcp_desc {
@@ -585,7 +584,6 @@
585
584
  .reasoning_content {
586
585
  padding: 8px 12px;
587
586
  margin-top: 4px;
588
- border-left: 2px solid var(--descriptionForeground);
589
587
  color: var(--descriptionForeground);
590
588
  background: var(--design-block-background);
591
589
  border-radius: 4px;
@@ -597,8 +595,8 @@
597
595
  align-items: center;
598
596
  padding: 0 4px;
599
597
  margin: 0 2px;
600
- background: var(--badge-background);
601
- color: var(--badge-foreground);
598
+ background-color: var(--chat-slashCommandBackground);
599
+ color: var(--chat-slashCommandForeground);
602
600
  border-radius: 3px;
603
601
  vertical-align: middle;
604
602
  font-size: 12px;
@@ -609,10 +607,6 @@
609
607
  margin-right: 3px;
610
608
  }
611
609
  }
612
- &:hover {
613
- background-color: var(--chat-slashCommandBackground);
614
- color: var(--chat-slashCommandForeground);
615
- }
616
610
  }
617
611
 
618
612
  .attachment_text {
@@ -620,6 +614,13 @@
620
614
  vertical-align: middle;
621
615
  font-size: 12px;
622
616
  word-break: break-all;
617
+ max-width: 100px;
618
+ text-overflow: ellipsis;
619
+ direction: rtl;
620
+ unicode-bidi: plaintext;
621
+ overflow: hidden;
622
+ white-space: nowrap;
623
+ display: inline-block;
623
624
  }
624
625
 
625
626
  .thumbnail_container {
@@ -672,3 +673,31 @@
672
673
  border-radius: 3px;
673
674
  overflow: hidden;
674
675
  }
676
+
677
+ .shiny_text {
678
+ color: #b5b5b5a4; /* Adjust this color to change intensity/style */
679
+ background: linear-gradient(
680
+ 120deg,
681
+ rgba(255, 255, 255, 0) 40%,
682
+ rgba(255, 255, 255, 0.8) 50%,
683
+ rgba(255, 255, 255, 0) 60%
684
+ );
685
+ background-size: 200% 100%;
686
+ -webkit-background-clip: text;
687
+ background-clip: text;
688
+ display: inline-block;
689
+ animation: shine 5s linear infinite;
690
+ }
691
+
692
+ @keyframes shine {
693
+ 0% {
694
+ background-position: 100%;
695
+ }
696
+ 100% {
697
+ background-position: -100%;
698
+ }
699
+ }
700
+
701
+ .shiny_text.disabled {
702
+ animation: none;
703
+ }