Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support new chat mention input for MCP model #4466

Merged
merged 37 commits into from
Mar 14, 2025
Merged

Conversation

erha19
Copy link
Member

@erha19 erha19 commented Mar 11, 2025

Types

  • 🎉 New Features
  • 💄 Style Changes

Background or solution

仅在 MCP 配置开启时展示,支持纯键盘操作

image image
new.input.mp4

Changelog

support new chat mention input for MCP model

Summary by CodeRabbit

Summary by CodeRabbit

  • 新功能

    • 升级聊天界面,现支持使用 “@” 提及文件和文件夹,实现更智能的消息处理。
    • 增强了上下文管理,现可同时附加文件和文件夹,并引入自动收集功能。
    • 文件搜索功能优化,新增仅搜索文件夹选项,提升搜索精准度。
    • 提示文本更新,为消息输入提供更清晰指引(如显示“当前文件”与“当前文件夹”)。
    • 新增提及输入组件,支持更丰富的用户互动体验。
    • 新增聊天相关的颜色令牌,统一了界面视觉风格。
    • 新增聊天提及的图标,扩展了可用图标集。
    • 引入新的构建脚本,简化组件库的构建过程。
  • 样式更新

    • 图标库进行了扩充和调整,新增多款图标并优化部分图标命名。
    • 引入新的聊天主题色,统一了界面视觉风格,带来更清新现代的体验。
    • 聊天输入区域的样式进行了优化,确保更好的用户体验。
  • 修复

    • 修复了聊天组件中消息处理的异步逻辑,确保消息在发送前正确处理。

@opensumi opensumi bot added the 🎨 feature feature required label Mar 11, 2025
Copy link
Contributor

coderabbitai bot commented Mar 11, 2025

Walkthrough

此次提交涉及多个模块的更新和新功能引入。主要改动包括:新增构建组件库的脚本;修改 AI 聊天服务中异步请求上下文的逻辑;调整并扩展聊天视图的消息处理流程;新增专门处理文件和文件夹提及的输入组件及其样式与类型定义;扩展上下文服务以支持文件夹管理;更新图标库与文件搜索逻辑;并调整本地化文本和主题色彩 tokens。

Changes

文件 改动说明
package.json 新增脚本 "build:components:lib": "cd packages/components && yarn run build" 用于构建组件库。
packages/ai-native/src/browser/chat/chat-agent.service.ts
packages/ai-native/src/browser/chat/chat.view.tsx
packages/ai-native/src/browser/components/chat-context/index.tsx
修改 ChatAgentService 中 invokeAgentprovideContextMessage 的异步处理逻辑;更新 chat.view.tsxhandleSend 函数,添加对文件/文件夹占位符的处理;调整组件中上下文管理导入路径。
packages/ai-native/src/browser/components/ChatMentionInput.tsx
packages/ai-native/src/browser/components/mention-input/mention-input.module.less
packages/ai-native/src/browser/components/mention-input/mention-input.tsx
packages/ai-native/src/browser/components/mention-input/mention-item.tsx
packages/ai-native/src/browser/components/mention-input/mention-panel.tsx
packages/ai-native/src/browser/components/mention-input/types.ts
新增用于处理提及(文件、文件夹)的 React 组件及其配套样式和类型定义。
packages/ai-native/src/browser/context/llm-context.service.ts
packages/ai-native/src/common/llm-context.ts
扩展上下文管理服务,新增对文件夹的支持,包括添加文件夹、序列化文件夹数据及调整异步方法签名。
packages/components/src/icon/iconMap.ts
packages/components/src/icon/iconfont/iconfont.css
packages/components/src/icon/iconfont/iconfont.html
packages/core-browser/src/style/icon/ide-iconfont.ts
新增图标 codebracesfilemcp;重命名 send-hollowsend-outlined;更新图标文件 URL。
packages/file-search/src/common/file-search.ts
packages/file-search/src/node/file-search.service.ts
在文件搜索中新增 onlyFolders 属性,用于限制只搜索文件夹,同时更新搜索参数和排序逻辑。
packages/i18n/src/common/en-US.lang.ts
packages/i18n/src/common/zh-CN.lang.ts
修改 AI 聊天输入占位符文本;新增上下文默认文件和文件夹的本地化条目。
packages/theme/src/common/color-tokens/chatColors.ts
packages/theme/src/common/color-tokens/index.ts
新增一系列聊天相关的颜色 tokens,并将其导出供主题统一管理。

Suggested labels

💄 style change

Suggested reviewers

  • Aaaaash
  • Ricbet
  • life2015
  • hacke2

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

yarn install v1.22.22
[1/4] Resolving packages...
warning [email protected]: This version is no longer supported. Please see https://eslint.org/version-support for other options.
warning eslint > @humanwhocodes/[email protected]: Use @eslint/config-array instead
warning eslint > @humanwhocodes/config-array > @humanwhocodes/[email protected]: Use @eslint/object-schema instead
warning eslint > file-entry-cache > flat-cache > [email protected]: Rimraf versions prior to v4 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > [email protected]: Glob versions prior to v9 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > glob > [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
warning eslint-import-resolver-typescript > [email protected]: Glob versions prior to v9 are no longer supported
error Couldn't find any versions for "@opensumi/ide-dev-tool" that matches "workspace:*"
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (20)
packages/file-search/src/node/file-search.service.ts (2)

97-107: 实现文件夹过滤逻辑

这段代码实现了当onlyFolders为true时,使用fs.statSync检查结果是否为目录,非目录则跳过。代码逻辑正确,但需要注意潜在的性能影响,因为同步的文件系统操作可能在文件数量大时导致性能问题。

考虑使用异步版本fs.stat替代fs.statSync,以避免在大量文件时阻塞主线程:

-                try {
-                  const fs = require('fs');
-                  const stat = fs.statSync(fileUri);
-                  if (!stat.isDirectory()) {
-                    return;
-                  }
-                } catch (e) {
-                  return;
-                }
+                const fs = require('fs/promises');
+                try {
+                  const stat = await fs.stat(fileUri);
+                  if (!stat.isDirectory()) {
+                    return;
+                  }
+                } catch (e) {
+                  return;
+                }

注意:这需要调整doFind方法以支持异步操作。


157-160: 避免代码重复

doFind方法中添加了当onlyFolders为true时设置RipGrep参数的代码,但这段逻辑与下面getSearchArgs方法中的代码重复。

建议移除这段重复代码,依赖getSearchArgs方法中的实现:

-        if (options.onlyFolders) {
-          args.push('--type-list', 'd:dir');
-          args.push('--type', 'd');
-        }
packages/ai-native/src/browser/components/mention-input/mention-input.module.less (2)

31-35: 建议:避免重复声明 .mention_tag,以免引发样式冲突。

当前文件第 31 和 239 行同时定义了 .mention_tag,且都作用于类似的场景。虽然一个在 .editor 内,另一个在全局,但命名相同容易混淆或导致叠加效果。建议为不同场景的选择器设立更精准的命名,或者在样式层级/作用范围上加以区分。


147-158: 提示:.mention_item 的圆角设置存在相互冲突。

第 147 行使用了 border-radius: 8px;,而第 156 行再次声明了 border-radius: 4px;。这会造成样式不一致,建议统一为同一个值,或在不同选择器分开定义,以保持可维护性。

 .mention_item {
-  border-radius: 8px;
   ...
   border-radius: 4px;
 }
packages/ai-native/src/browser/components/mention-input/mention-input.tsx (1)

157-168: 建议:检查跨浏览器光标位置的兼容性。

此处使用 window.getSelection() 并基于文本节点长度计算光标位置。不同浏览器的实现和空格换行可能影响测算结果,易引发小概率的光标定位异常。可考虑通过更成熟的富文本编辑库或在此处增加更多兼容性逻辑,以便后期维护。

packages/ai-native/src/browser/components/mention-input/mention-panel.tsx (2)

66-69: 提醒:在一级菜单下 filteredItems 为空时立即返回 null 可能影响用户体验。

如果用户输入条件过苛,面板直接不渲染,可能让用户误以为功能异常。可在此处提示 “无匹配结果” 等文案,提供更清晰的交互反馈。

 if (level === 0 && filteredItems.length === 0) {
-  return null;
+  return (
+    <div className={styles.no_results}>
+      无匹配结果
+    </div>
+  );
 }

85-85: 细节:建议将“正在搜索...”文本提取为可本地化资源。

目前字符串直接写死在代码中,后续若需要多语言支持或统一管理提示文案,维护成本会更高。可改用国际化配置或集中管理的常量。

packages/ai-native/src/browser/components/ChatMentionInput.tsx (5)

64-82: 建议移除或完成注释掉的代码块

这段代码似乎是一个计划中但尚未实现的"code"类型提及功能。建议要么完成实现,要么移除这段注释掉的代码,以保持代码库的干净和可维护性。

-// {
-//   id: 'code',
-//   type: 'code',
-//   text: 'Code',
-//   icon: getIcon('codebraces'),
-//   getHighestLevelItems: () => [],
-//   getItems: async (searchText: string) => {
-//     const currentEditor = editorService.currentEditor;
-//     if (!currentEditor) {
-//       return [];
-//     }
-//     const currentDocumentModel = currentEditor.currentDocumentModel;
-//     if (!currentDocumentModel) {
-//       return [];
-//     }
-//     const symbols = await commandService.executeCommand('_executeFormatDocumentProvider', currentDocumentModel.uri.codeUri);
-//     return [];
-//   },
-// },

220-238: 考虑将模型配置项抽取到配置文件中

当前模型选项(如 "QWQ 32B" 和 "DeepSeek R1")是硬编码在组件中的。建议将这些选项移到配置文件或通过服务获取,以便未来添加或修改模型时无需修改组件代码。


245-253: 依赖数组包含未使用的 editorService

handleSend 函数的依赖数组中包含 editorService,但函数体中并未使用此服务。建议从依赖数组中移除未使用的依赖。

const handleSend = useCallback(
  async (content: string, option?: { model: string; [key: string]: any }) => {
    if (disabled) {
      return;
    }
    onSend(content, undefined, undefined, option);
  },
-  [onSend, editorService, disabled],
+  [onSend, disabled],
);

106-146: 建议增加文件搜索的错误处理

当前的文件搜索实现没有处理搜索可能失败的情况。如果 searchService.find 调用失败,应该有适当的错误处理逻辑。

getItems: async (searchText: string) => {
  if (!searchText) {
    // 现有代码...
  } else {
    const rootUris = (await workspaceService.roots).map((root) => new URI(root.uri).codeUri.fsPath.toString());
-   const results = await searchService.find(searchText, {
-     rootUris,
-     useGitIgnore: true,
-     noIgnoreParent: true,
-     fuzzyMatch: true,
-     limit: 10,
-   });
+   try {
+     const results = await searchService.find(searchText, {
+       rootUris,
+       useGitIgnore: true,
+       noIgnoreParent: true,
+       fuzzyMatch: true,
+       limit: 10,
+     });
+     return Promise.all(
+       results.map(async (file) => {
+         // 现有处理逻辑...
+       }),
+     );
+   } catch (error) {
+     console.error('文件搜索失败:', error);
+     return [];
+   }
-   return Promise.all(
-     results.map(async (file) => {
-       // 现有处理逻辑...
-     }),
-   );
  }
},

39-40: 建议统一使用英文注释

为保持代码注释风格一致性,建议将中文注释 "指令命令激活组件" 改为英文注释。

-// 指令命令激活组件
+// Command activation component
packages/ai-native/src/browser/chat/chat.view.tsx (1)

590-629: 添加了文件和文件夹上下文处理功能

handleSend 函数的重构实现了文件和文件夹占位符的处理,增强了聊天功能。不过,建议增加以下改进:

  1. 添加更健壮的错误处理机制,特别是针对无效的文件或文件夹 URI
  2. 考虑重构正则表达式匹配部分,以避免重复代码模式
async (message: string, agentId?: string, command?: string) => {
  // ...省略部分代码...
  
  // 提取并替换 {{@file:xxx}} 中的文件内容
  let processedContent = message;
-  const filePattern = /\{\{@file:(.*?)\}\}/g;
-  const fileMatches = message.match(filePattern);
-  let isCleanContext = false;
-  if (fileMatches) {
-    for (const match of fileMatches) {
-      const filePath = match.replace(/\{\{@file:(.*?)\}\}/, '$1');
-      if (filePath && !isCleanContext) {
-        isCleanContext = true;
-        llmContextService.cleanFileContext();
-      }
-      const fileUri = new URI(filePath);
-      llmContextService.addFileToContext(fileUri, undefined, true);
-      // 获取文件内容
-      // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
-      processedContent = processedContent.replace(match, `\`File:${fileUri.displayName}\``);
-    }
-  }
+  let isCleanContext = false;
+  
+  try {
+    // 处理文件引用
+    const filePattern = /\{\{@file:(.*?)\}\}/g;
+    const fileMatches = message.match(filePattern);
+    if (fileMatches) {
+      if (!isCleanContext) {
+        isCleanContext = true;
+        llmContextService.cleanFileContext();
+      }
+      
+      for (const match of fileMatches) {
+        const filePath = match.replace(/\{\{@file:(.*?)\}\}/, '$1');
+        if (!filePath) continue;
+        
+        const fileUri = new URI(filePath);
+        llmContextService.addFileToContext(fileUri, undefined, true);
+        // 替换占位符
+        processedContent = processedContent.replace(match, `\`File:${fileUri.displayName}\``);
+      }
+    }
+    
+    // 处理文件夹引用
+    const folderPattern = /\{\{@folder:(.*?)\}\}/g;
+    const folderMatches = processedContent.match(folderPattern);
+    if (folderMatches) {
+      for (const match of folderMatches) {
+        const folderPath = match.replace(/\{\{@folder:(.*?)\}\}/, '$1');
+        if (!folderPath) continue;
+        
+        const folderUri = new URI(folderPath);
+        llmContextService.addFolderToContext(folderUri);
+        // 替换占位符
+        processedContent = processedContent.replace(match, `\`Folder:${folderUri.displayName}\``);
+      }
+    }
+  } catch (error) {
+    console.error('处理文件和文件夹引用时出错:', error);
+    // 可以选择添加用户通知或其他错误处理
+  }
-  const folderPattern = /\{\{@folder:(.*?)\}\}/g;
-  const folderMatches = processedContent.match(folderPattern);
-  if (folderMatches) {
-    for (const match of folderMatches) {
-      const folderPath = match.replace(/\{\{@folder:(.*?)\}\}/, '$1');
-      const folderUri = new URI(folderPath);
-      llmContextService.addFolderToContext(folderUri);
-      // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
-      processedContent = processedContent.replace(match, `\`Folder:${folderUri.displayName}\``);
-    }
-  }
  
  return handleAgentReply({ message: processedContent, agentId, command, reportExtra });
},
packages/ai-native/src/browser/components/mention-input/types.ts (3)

29-29: 请将注释翻译为英文以保持一致性

注释行 level: number; // 0: 一级菜单, 1: 二级菜单 包含中文说明。为了代码的国际化和一致性,建议将注释翻译为英文。

-  level: number; // 0: 一级菜单, 1: 二级菜单
+  level: number; // 0: first-level menu, 1: second-level menu

1-11: 建议添加 JSDoc 文档

MentionItem 接口是整个提及功能的核心,建议添加 JSDoc 文档来说明其用途和各个属性的作用,这将有助于其他开发者理解和使用此接口。

+/**
+ * Represents an item that can be mentioned in the chat input.
+ * This can be a file, folder, code snippet, or other mentionable entities.
+ */
 export interface MentionItem {
   id: string;
   type: string;
   text: string;
   value?: string;
   description?: string;
   contextId?: string;
   icon?: string;
   getHighestLevelItems?: () => MentionItem[];
   getItems?: (searchText: string) => Promise<MentionItem[]>;
 }

13-16: 考虑添加更多注释说明菜单配置的用途

SecondLevelMenuConfig 接口定义了二级菜单的配置,但可能不够直观。建议添加注释来说明 getDefaultItemsgetHighestLevelItems 方法的用途和它们之间的区别。

packages/ai-native/src/browser/context/llm-context.service.ts (4)

213-213: 拼写错误:getPartiaFolderStructure

方法名中有拼写错误,应为 getPartialFolderStructure(注意 "Partial" 拼写)。

- await this.getPartiaFolderStructure(folderUri.codeUri.fsPath)
+ await this.getPartialFolderStructure(folderUri.codeUri.fsPath)

194-203: 确保序列化方法的异步性得到正确处理

serialize 方法已经更新为异步方法,确保所有调用此方法的地方都正确使用 await。另外,建议添加注释说明异步的原因,以便其他开发者理解。

 /**
+ * Serializes the context data including recently viewed files, attached files, and attached folders.
+ * This method is async because folder serialization requires file system operations.
+ */
 async serialize(): Promise<SerializedContext> {
   const files = this.getAllContextFiles();
   const workspaceRoot = URI.file(this.appConfig.workspaceDir);

   return {
     recentlyViewFiles: this.serializeRecentlyViewFiles(files.viewed, workspaceRoot),
     attachedFiles: this.serializeAttachedFiles(files.attached, workspaceRoot),
     attachedFolders: await this.serializeAttachedFolders(files.attachedFolders, workspaceRoot),
   };
 }

221-221: 考虑使文件夹深度可配置

当前的实现默认文件夹深度为 2,这对于深层嵌套的文件夹结构可能不够。考虑将此值作为可配置项,或者通过服务配置提供。


205-219: 文件夹序列化方法实现良好,但考虑进一步优化

serializeAttachedFolders 方法的实现是合理的,但考虑在处理大量文件夹时的性能问题。使用 Promise.all 会同时处理所有文件夹,可能导致系统负载过高。对于大量文件夹,考虑使用批处理或限制并行处理数量。

 private async serializeAttachedFolders(folders: FileContext[], workspaceRoot: URI): Promise<string[]> {
   // 去重
   const folderPath = Array.from(new Set(folders.map((folder) => folder.uri.toString())));
+  // 对于大量文件夹,可以考虑分批处理
+  if (folderPath.length > 5) {
+    console.warn(`处理大量文件夹 (${folderPath.length}),可能影响性能`);
+  }
   return Promise.all(
     folderPath.map(async (folder) => {
       // ... existing code ...
     }),
   );
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7038720 and 2900713.

⛔ Files ignored due to path filters (5)
  • packages/components/src/icon/iconfont/iconfont.eot is excluded by !**/*.eot
  • packages/components/src/icon/iconfont/iconfont.svg is excluded by !**/*.svg
  • packages/components/src/icon/iconfont/iconfont.ttf is excluded by !**/*.ttf
  • packages/components/src/icon/iconfont/iconfont.woff is excluded by !**/*.woff
  • packages/components/src/icon/iconfont/iconfont.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (25)
  • package.json (1 hunks)
  • packages/ai-native/src/browser/chat/chat-agent.service.ts (1 hunks)
  • packages/ai-native/src/browser/chat/chat.view.tsx (8 hunks)
  • packages/ai-native/src/browser/components/ChatMentionInput.tsx (1 hunks)
  • packages/ai-native/src/browser/components/chat-context/index.tsx (1 hunks)
  • packages/ai-native/src/browser/components/components.module.less (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-input.module.less (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-input.tsx (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-item.tsx (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-panel.tsx (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/types.ts (1 hunks)
  • packages/ai-native/src/browser/context/llm-context.service.ts (5 hunks)
  • packages/ai-native/src/common/llm-context.ts (3 hunks)
  • packages/ai-native/src/common/prompts/context-prompt-provider.ts (1 hunks)
  • packages/components/src/icon/iconfont/iconMap.ts (4 hunks)
  • packages/components/src/icon/iconfont/iconfont.css (3 hunks)
  • packages/components/src/icon/iconfont/iconfont.html (3 hunks)
  • packages/core-browser/src/ai-native/ai-config.service.ts (0 hunks)
  • packages/core-browser/src/style/icon/ide-iconfont.ts (1 hunks)
  • packages/file-search/src/common/file-search.ts (1 hunks)
  • packages/file-search/src/node/file-search.service.ts (5 hunks)
  • packages/i18n/src/common/en-US.lang.ts (2 hunks)
  • packages/i18n/src/common/zh-CN.lang.ts (2 hunks)
  • packages/theme/src/common/color-tokens/chatColors.ts (1 hunks)
  • packages/theme/src/common/color-tokens/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/core-browser/src/ai-native/ai-config.service.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
  • GitHub Check: build-windows
  • GitHub Check: unittest (ubuntu-latest, 18.x, node)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build (macos-latest, 20.x)
  • GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (44)
packages/core-browser/src/style/icon/ide-iconfont.ts (1)

1-1: 更新了图标字体的 CSS 文件链接

图标字体 CSS 文件链接已从 font_1432262_7wffopowfq.css 更新到 font_1432262_1ef4fl5cm51.css,这个变更与添加新图标相匹配,支持 MCP 模型的聊天提及输入功能。

packages/components/src/icon/iconfont/iconMap.ts (4)

45-45: 添加了新的 codebraces 图标

defaultIconfont 对象添加了 codebraces 图标,这个新图标将用于支持 MCP 模型的聊天提及输入功能。


83-83: 添加了新的 file 图标

defaultIconfont 对象添加了 file 图标,这个图标将用于支持 MCP 模型的聊天提及输入功能。


119-119: 添加了新的 mcp 图标

defaultIconfont 对象添加了 mcp 图标,这个图标将用于支持 MCP 模型的聊天提及输入功能。


168-168: 重命名了 send-hollow 图标

send-hollow 图标重命名为 send-outlined,保持了命名的一致性并更好地描述了图标的视觉风格。

packages/components/src/icon/iconfont/iconfont.html (4)

7-7: 更新了图标字体的 CSS 文件链接

图标字体 CSS 文件链接已从 font_1432262_7wffopowfq.css 更新到 font_1432262_1ef4fl5cm51.css,与 ide-iconfont.ts 中的更新保持一致。


88-89: 更新了 OpenSumi 版本和 CSS 路径

将 OpenSumi 版本从 v2.27.2 更新到 v3.8.1,并更新了对应的图标字体 CSS 路径。


95-112: 添加了新的图标 HTML 元素

添加了三个新的图标元素(codebraces、file 和 mcp),用于支持 MCP 模型的聊天提及输入功能。每个图标都包含了正确的 HTML 结构和类名。


143-147: 重命名了 send-hollow 图标的 HTML 元素

send-hollow 图标元素重命名为 send-outlined,包括 data-icon 属性和显示的图标名称,与 iconMap.ts 中的更改保持一致。

packages/components/src/icon/iconfont/iconfont.css (5)

3-8: 更新了字体文件的时间戳

更新了所有字体文件 URL 中的时间戳,从 t=1709176848484 更新到 t=1741593634138,并更新了 base64 编码的 WOFF2 字体数据。这确保了使用最新版本的图标字体。


19-21: 添加了 codebraces 图标的 CSS 规则

为新添加的 codebraces 图标定义了 CSS 规则,将其内容设置为 Unicode 字符 "\e75f"。


23-25: 添加了 file 图标的 CSS 规则

为新添加的 file 图标定义了 CSS 规则,将其内容设置为 Unicode 字符 "\e63a"。


27-29: 添加了 mcp 图标的 CSS 规则

为新添加的 mcp 图标定义了 CSS 规则,将其内容设置为 Unicode 字符 "\e639"。


51-51: 重命名了 send-hollow 图标的 CSS 规则

.kticon-send-hollow:before 选择器重命名为 .kticon-send-outlined:before,保持了内容值 "\e632" 不变,与其他文件中的更改保持一致。

packages/file-search/src/common/file-search.ts (1)

28-28: 添加 onlyFolders 选项支持仅搜索文件夹

这个新增的可选属性允许文件搜索服务仅返回文件夹类型的结果,为 MCP 模型的聊天提及功能提供了必要的支持。

packages/ai-native/src/browser/components/components.module.less (1)

522-522: 设置模型选择器的最小宽度

.model_selector 类添加最小宽度确保了选择器在各种尺寸下的视觉稳定性,防止内容过多时出现挤压变形的情况。

package.json (1)

22-22: 新增组件库构建脚本

这个新增的构建命令提供了一种简便的方式来单独构建组件库。与 build:components 不同,build:components:lib 执行的是组件库的常规构建而非分发构建,这对于开发新的聊天提及输入功能非常有用。

packages/theme/src/common/color-tokens/index.ts (1)

38-38: 导出聊天相关颜色令牌

添加 chatColors 模块的导出使得新增的聊天组件相关颜色令牌可以被统一管理和访问。这些颜色令牌应该包含了聊天界面元素所需的主题颜色定义,确保了 MCP 模型的聊天提及输入功能能够适配不同的主题风格。

packages/ai-native/src/browser/components/chat-context/index.tsx (1)

18-18: 导入路径更新符合命名规范

将导入路径从 ./ContextSelector 改为 ./context-selector 遵循了kebab-case的命名约定,与项目的其他部分保持一致。

packages/i18n/src/common/en-US.lang.ts (2)

1452-1452: 更新了聊天输入框提示文本,支持新的提及功能

将输入框提示文本从原来的提示改为"Ask anything, @ to mention",清晰地提示用户可以使用@符号进行提及操作,与PR的目标功能一致。


1462-1463: 新增文件和文件夹上下文相关的本地化字符串

添加了两个新的本地化字符串,用于在界面中显示当前文件和当前文件夹的上下文信息,这些字符串将用于新的聊天提及输入功能中,使用户能够更好地理解上下文环境。

packages/ai-native/src/common/prompts/context-prompt-provider.ts (4)

24-28: 改进了上下文提示的格式和结构

重新格式化了XML结构中的<additional_data><recently_viewed_files>部分,使代码更加清晰易读,同时保持了功能不变。


29-43: 优化了附加文件部分的格式

重构了<attached_files>部分的格式,改进了代码缩进和结构,使模板字符串更加清晰,同时保持了相同的功能输出。


44-47: 新增附加文件夹上下文支持

添加了新的<attached_folders>部分,将context.attachedFolders数据添加到提示中,使AI能够获取文件夹上下文信息。这是支持MCP模型中新的聊天提及输入功能的关键部分。


51-54: 优化了当前打开文件部分的格式

调整了<current_opened_file>部分的格式,保持了一致的缩进风格,使代码更加易读。

packages/i18n/src/common/zh-CN.lang.ts (2)

1221-1221: 修改聊天输入提示,使用 @ 引用内容

将输入提示从使用 "/" 改为使用 "@" 引用内容的变更看起来合理,与PR目标中描述的新聊天提及功能一致。


1230-1231: 添加了默认上下文文件和文件夹的本地化字符串

新增了两个本地化字符串,支持在聊天中引用当前文件和当前文件夹的功能,这符合PR描述中提到的新聊天提及输入功能。

packages/file-search/src/node/file-search.service.ts (2)

43-43: 新增只搜索文件夹的选项

添加了onlyFolders选项,默认为false,为文件搜索服务增加了只显示文件夹的功能,这对于MCP聊天提及功能非常有用。


186-189: 为RipGrep添加文件夹类型过滤器

使用RipGrep的类型过滤功能来限制只返回目录类型结果是一个高效的解决方案。这种方法优于在应用层过滤,可以显著提高性能。

packages/ai-native/src/browser/components/mention-input/mention-item.tsx (1)

1-24: 新增MentionItem组件实现提及功能UI

该组件实现了提及功能的单个项目UI,包括图标、文本和描述,并支持嵌套子项目的显示。组件设计简洁明了,功能完整。

几点建议:

  1. 考虑添加键盘导航支持
  2. 可以为组件添加单元测试确保功能稳定性
  3. 建议添加更多的文档注释说明组件的使用方法
packages/ai-native/src/browser/chat/chat-agent.service.ts (2)

155-155: 使用异步方式获取上下文消息

修改了invokeAgent方法,使用await等待provideContextMessage的结果。这个变更是必要的,因为上下文消息现在需要异步获取。

Also applies to: 157-157


165-166: 改进上下文提供方法为异步方法

provideContextMessage方法修改为异步方法,并等待contextService.serialize()的结果。这个变更使得上下文序列化过程可以正确地处理异步操作,特别是在获取文件和文件夹信息时。

packages/ai-native/src/browser/components/mention-input/mention-input.module.less (1)

279-281: 确认:.loading_container 是否有实际使用场景?

此选择器默认被设为 display: none;,但在其他地方似乎并无显式控制其显示状态。如果暂未使用,考虑移除当前代码;如后续功能需要,可在需要时单独引入,避免产生无法追踪的冗余样式。

packages/ai-native/src/browser/chat/chat.view.tsx (2)

134-136: 根据功能支持条件渲染合适的聊天输入组件

代码根据 MCP 配置支持情况选择使用不同的聊天输入组件,这是一个良好的实践,可以根据功能可用性灵活地展示 UI。


758-759: 简化了 onSend 属性传递

代码简化了 onSend 属性传递,直接引用 handleSend 函数而不是创建中间对象,这是一个好的改进。

packages/theme/src/common/color-tokens/chatColors.ts (1)

1-63: 出色的聊天相关颜色主题定义

这个新文件定义了聊天相关UI元素的颜色标记,为不同主题(深色、浅色、高对比度)提供了一致的颜色定义。文件组织清晰,命名规范,每个颜色都有明确的描述说明。

特别好的做法:

  1. 为每个主题场景(暗色、亮色、高对比)都定义了对应的颜色
  2. 使用语义化命名
  3. 提供了每个颜色的用途说明
  4. 使用透明度和颜色组合以保持视觉一致性
packages/ai-native/src/common/llm-context.ts (4)

19-22: 添加了文件夹上下文支持

新增 addFolderToContext 方法扩展了上下文服务的功能,使其能够处理文件夹上下文,与文件和文件夹提及功能很好地配合。


41-41: 将 serialize 方法改为异步

serialize() 方法返回类型更改为 Promise<SerializedContext>,这是一个合理的修改,特别是考虑到序列化大型上下文可能需要时间,异步处理更为合适。


61-61: 扩展了 SerializedContext 接口

SerializedContext 接口添加 attachedFolders: string[] 属性,使其能够存储文件夹引用,与文件夹上下文功能完美配合。


4-12: 为现有方法添加了 JSDoc 注释

startAutoCollectionstopAutoCollection 方法添加了清晰的文档注释,提高了代码的可读性和可维护性。

packages/ai-native/src/browser/components/mention-input/types.ts (2)

47-51: 枚举值看起来很好,但可能需要考虑扩展性

当前的 MentionType 枚举包含了 FILE, FOLDER 和 CODE 类型,这符合当前的需求。但随着功能的发展,可能需要添加更多类型如 USER, ISSUE 等。确保团队了解这种可能的扩展需求。


68-76: 接口设计合理,但建议增加说明

MentionInputProps 接口设计合理,包含了必要的属性和可选配置。为了提高可维护性,建议添加一些额外的注释来说明 onSend 回调中 config 参数的可能值和用途。

packages/ai-native/src/browser/context/llm-context.service.ts (2)

62-72: 代码实现良好,与现有模式一致

addFolderToList 方法实现良好,与 addFileToList 方法保持一致的模式,这有助于代码的可维护性和可读性。


91-100: 良好的文件夹添加实现

addFolderToContext 方法实现简洁明了,适当地处理了空值检查并调用了通知方法。与文件处理保持了一致的模式。

@erha19 erha19 force-pushed the feat/support-chat-input branch from 2900713 to ddb336e Compare March 11, 2025 02:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
packages/ai-native/src/browser/components/mention-input/mention-input.tsx (1)

760-763: ⚠️ Potential issue

存在 XSS 安全风险,需要增加内容清理

与先前的代码审查建议一致,这里仍然存在潜在的 XSS 安全风险。直接使用 innerHTML 设置用户提供的内容而没有进行适当的安全过滤是不安全的。即使在当前上下文中用户输入可能受到限制,良好的安全实践仍然建议始终对 HTML 内容进行清理。

建议使用 DOMPurify 或类似的库进行 HTML 清理:

 const tempDiv = document.createElement('div');
-tempDiv.innerHTML = rawContent;
+// 使用第三方库进行 HTML 清理
+const safeContent = DOMPurify.sanitize(rawContent);
+tempDiv.innerHTML = safeContent;
🧹 Nitpick comments (19)
packages/theme/src/common/color-tokens/chatColors.ts (2)

30-34: 考虑使用颜色变量替代硬编码十六进制值

chatSlashCommandBackground中直接使用了硬编码的十六进制颜色值('#34414b8f', '#d2ecff99'),建议考虑使用其他颜色token或变量,特别是如果这些颜色在代码库其他位置也有使用。这样可以提高可维护性和一致性。


36-40: 考虑使用颜色变量替代硬编码十六进制值

与之前类似,chatSlashCommandForeground也使用了硬编码的十六进制颜色值('#40A6FF', '#306CA2')。若这些颜色在其他地方也有使用,建议提取为共享的颜色常量。

packages/ai-native/src/browser/components/mention-input/mention-input.tsx (4)

13-905: 建议将大型组件拆分为更小的功能单元

当前组件非常庞大(近900行代码),包含多个复杂功能。建议考虑将其拆分为多个较小的组件或提取公共逻辑到单独的 hooks 中,以提高可维护性和可测试性。

可以考虑分离的部分:

  1. 将提及面板逻辑分离到单独的组件
  2. 将输入历史记录功能封装为自定义 hook
  3. 将键盘导航和光标位置计算逻辑提取为工具函数
  4. 将二级菜单处理逻辑封装到单独的组件

这样的重构可以使代码更加模块化,更容易理解和维护。


101-153: 优化异步加载和错误处理逻辑

当前的异步加载逻辑在遇到错误时仍会继续执行后续清理代码,这可能导致状态不一致。建议改进错误处理,添加更多详细的错误信息,并确保在出现错误时状态更新一致。

此外,fetchItems 函数嵌套了多层 try-catch 块,增加了复杂性。可以考虑重构为更扁平的结构,使错误处理更加清晰。


157-168: 改进 getCursorPosition 函数的健壮性

getCursorPosition 函数在处理光标位置时依赖于 window.getSelection(),但没有充分考虑所有边缘情况。例如,当选择范围不在目标元素内部时,可能会导致计算错误。建议添加更多检查以确保光标位置的计算准确无误。


274-437: handleKeyDown 函数过于复杂,建议拆分

handleKeyDown 函数处理多种不同类型的键盘事件,导致函数过长且复杂。建议将其拆分为多个专门的处理函数,例如:

  • handleEscapeKey
  • handleEnterKey
  • handleArrowNavigation
  • handleBackspaceKey

这样可以提高代码的可读性和可维护性。

packages/ai-native/src/browser/components/ChatMentionInput.tsx (4)

64-82: 移除或实现被注释的代码块

这段注释掉的代码块看起来是一个未完成的 "code" 类型提及项的实现。建议要么完成此功能的实现,要么完全删除这段注释代码,以保持代码库的整洁。保留未使用的注释代码会导致混淆并增加维护负担。


172-216: 优化 getItems 方法中的重复代码

文件夹 getItems 方法与文件的 getItems 方法有大量重复代码。建议将共同逻辑提取到一个工具函数中,以减少重复并提高可维护性。例如,文件搜索和 URI 处理的共同逻辑可以被抽取出来。


220-239: 避免硬编码模型选项

模型选项(QWQ 32BDeepSeek R1)是硬编码在组件中的。这种方式在需要添加新模型或更改现有模型时需要修改代码。建议考虑从配置文件或服务中动态获取这些选项,以提高灵活性。

- modelOptions: [
-   { label: 'QWQ 32B', value: 'qwq-32b' },
-   { label: 'DeepSeek R1', value: 'deepseek-r1' },
- ],
- defaultModel: 'deepseek-r1',
+ modelOptions: aiChatService.getAvailableModels(),
+ defaultModel: aiChatService.getDefaultModel(),

245-253: 修复 handleSend 函数的依赖数组

handleSend 函数的依赖数组包含 editorService,但函数体中并未使用该服务。这可能导致不必要的函数重新创建。建议更新依赖数组以匹配实际使用的依赖。

  }, 
-   [onSend, editorService, disabled],
+   [onSend, disabled],
  );
packages/ai-native/src/browser/chat/chat.view.tsx (2)

653-670: 优化重复的正则表达式操作

代码对消息内容进行了多次正则匹配和替换操作。可以优化这部分逻辑,减少重复工作:

- const filePattern = /\{\{@file:(.*?)\}\}/g;
- const fileMatches = message.match(filePattern);
+ const filePattern = /\{\{@file:(.*?)\}\}/g;
+ let match;
+ let processedContent = message;
+ let isCleanContext = false;
+ 
+ // 一次性处理所有文件和文件夹匹配
+ while ((match = filePattern.exec(message)) !== null) {
+   const filePath = match[1];
+   if (filePath && !isCleanContext) {
+     isCleanContext = true;
+     llmContextService.cleanFileContext();
+   }
+   // 处理文件...
+ }

+ const folderPattern = /\{\{@folder:(.*?)\}\}/g;
+ while ((match = folderPattern.exec(processedContent)) !== null) {
+   // 处理文件夹...
+ }

658-665: 添加清理上下文操作的注释说明

在第一个文件匹配时调用 llmContextService.cleanFileContext() 是一个重要的副作用,可能会影响应用程序的其他部分。建议添加详细注释说明此操作的目的和影响,以便其他开发人员理解这一设计决策。

packages/ai-native/src/browser/components/mention-input/types.ts (7)

1-11: 类型定义结构清晰,建议添加详细注释

MentionItem 接口定义了可被提及的项目结构,包含基本属性和获取项目的方法。为提高代码可维护性,建议为每个属性和方法添加详细的 JSDoc 注释,特别是对于 getHighestLevelItemsgetItems 这类关键方法的预期行为和返回值。


13-16: 建议添加二级菜单配置的注释说明

SecondLevelMenuConfig 接口定义了二级菜单的配置,但缺少对接口目的和方法预期行为的说明。建议添加 JSDoc 注释,明确说明 getDefaultItemsgetHighestLevelItems 方法的使用场景和返回值的意义。


23-35: MentionState 接口中的中文注释需要国际化

接口中包含中文注释,建议将其替换为英文注释以保持代码的国际化标准。同时,建议为复杂的状态属性添加更详细的说明,特别是状态转换逻辑。

- level: number; // 0: 一级菜单, 1: 二级菜单
+ level: number; // 0: first level menu, 1: second level menu
- parentType: string | null; // 二级菜单的父类型
+ parentType: string | null; // parent type for second level menu
- secondLevelFilter: string; // 二级菜单的筛选文本
+ secondLevelFilter: string; // filter text for second level menu
- inlineSearchActive: boolean; // 是否在输入框中进行二级搜索
+ inlineSearchActive: boolean; // whether to perform second-level search in the input field
- inlineSearchStartPos: number | null; // 内联搜索的起始位置
+ inlineSearchStartPos: number | null; // starting position for inline search
- loading: boolean; // 加载状态
+ loading: boolean; // loading state

37-40: 建议将 ModelOption 接口导出

ModelOption 接口定义了模型选择项,但未被导出。考虑到它可能在其他组件中被使用,建议添加 export 关键字使其可在模块外部访问。

-interface ModelOption {
+export interface ModelOption {
  label: string;
  value: string;
}

53-59: 建议将 FooterButton 接口导出

ModelOption 类似,FooterButton 接口可能在其他组件中被使用,建议添加 export 关键字使其可在模块外部访问。同时,onClick 方法可以考虑添加参数以提供更多的灵活性。

-interface FooterButton {
+export interface FooterButton {
  id: string;
  icon: string;
  title: string;
-  onClick?: () => void;
+  onClick?: (event?: React.MouseEvent) => void;
  position: FooterButtonPosition;
}

68-76: MentionInputProps 接口可添加更多说明

MentionInputProps 接口中的注释需要更新为英文,并建议为 onSendonStop 方法添加更详细的说明。特别是 onSend 方法的 config 参数包含一个 model 属性和其他任意属性,这种灵活性可能需要更清晰的文档说明。

export interface MentionInputProps {
-  mentionItems?: MentionItem[]; // 简化为单一菜单项配置
+  mentionItems?: MentionItem[]; // simplified as single menu item configuration
  onSend?: (content: string, config?: { model: string; [key: string]: any }) => void;
  onStop?: () => void;
  placeholder?: string;
  loading?: boolean;
-  footerConfig?: FooterConfig; // 新增配置项
+  footerConfig?: FooterConfig; // new configuration item
  mentionKeyword?: string;
}

78-78: 默认提及关键字可考虑支持配置

MENTION_KEYWORD 常量定义了默认的提及触发字符为 '@'。考虑到不同应用场景可能需要不同的触发字符,建议提供一种机制来配置或覆盖此默认值,或者添加注释说明此常量的使用场景和如何通过 mentionKeyword 属性自定义。

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2900713 and ddb336e.

⛔ Files ignored due to path filters (5)
  • packages/components/src/icon/iconfont/iconfont.eot is excluded by !**/*.eot
  • packages/components/src/icon/iconfont/iconfont.svg is excluded by !**/*.svg
  • packages/components/src/icon/iconfont/iconfont.ttf is excluded by !**/*.ttf
  • packages/components/src/icon/iconfont/iconfont.woff is excluded by !**/*.woff
  • packages/components/src/icon/iconfont/iconfont.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (25)
  • package.json (1 hunks)
  • packages/ai-native/src/browser/chat/chat-agent.service.ts (1 hunks)
  • packages/ai-native/src/browser/chat/chat.view.tsx (7 hunks)
  • packages/ai-native/src/browser/components/ChatMentionInput.tsx (1 hunks)
  • packages/ai-native/src/browser/components/chat-context/index.tsx (1 hunks)
  • packages/ai-native/src/browser/components/components.module.less (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-input.module.less (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-input.tsx (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-item.tsx (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-panel.tsx (1 hunks)
  • packages/ai-native/src/browser/components/mention-input/types.ts (1 hunks)
  • packages/ai-native/src/browser/context/llm-context.service.ts (5 hunks)
  • packages/ai-native/src/common/llm-context.ts (3 hunks)
  • packages/ai-native/src/common/prompts/context-prompt-provider.ts (1 hunks)
  • packages/components/src/icon/iconfont/iconMap.ts (4 hunks)
  • packages/components/src/icon/iconfont/iconfont.css (3 hunks)
  • packages/components/src/icon/iconfont/iconfont.html (3 hunks)
  • packages/core-browser/src/ai-native/ai-config.service.ts (0 hunks)
  • packages/core-browser/src/style/icon/ide-iconfont.ts (1 hunks)
  • packages/file-search/src/common/file-search.ts (1 hunks)
  • packages/file-search/src/node/file-search.service.ts (5 hunks)
  • packages/i18n/src/common/en-US.lang.ts (2 hunks)
  • packages/i18n/src/common/zh-CN.lang.ts (2 hunks)
  • packages/theme/src/common/color-tokens/chatColors.ts (1 hunks)
  • packages/theme/src/common/color-tokens/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/core-browser/src/ai-native/ai-config.service.ts
🚧 Files skipped from review as they are similar to previous changes (18)
  • packages/file-search/src/common/file-search.ts
  • packages/ai-native/src/browser/components/chat-context/index.tsx
  • packages/ai-native/src/browser/components/components.module.less
  • packages/theme/src/common/color-tokens/index.ts
  • packages/components/src/icon/iconfont/iconMap.ts
  • packages/core-browser/src/style/icon/ide-iconfont.ts
  • packages/ai-native/src/common/prompts/context-prompt-provider.ts
  • packages/i18n/src/common/en-US.lang.ts
  • packages/ai-native/src/browser/chat/chat-agent.service.ts
  • packages/file-search/src/node/file-search.service.ts
  • packages/ai-native/src/common/llm-context.ts
  • packages/i18n/src/common/zh-CN.lang.ts
  • packages/components/src/icon/iconfont/iconfont.html
  • packages/ai-native/src/browser/components/mention-input/mention-panel.tsx
  • package.json
  • packages/ai-native/src/browser/components/mention-input/mention-input.module.less
  • packages/ai-native/src/browser/context/llm-context.service.ts
  • packages/components/src/icon/iconfont/iconfont.css
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
  • GitHub Check: unittest (ubuntu-latest, 18.x, node)
  • GitHub Check: ubuntu-latest, Node.js 20.x
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build-windows
🔇 Additional comments (11)
packages/theme/src/common/color-tokens/chatColors.ts (7)

1-7: 导入模块结构清晰,符合主题模块设计规范

导入的模块和工具函数与颜色主题相关,结构组织合理。从相关模块中导入了必要的工具类和颜色常量,为后续的聊天相关颜色token定义做好了准备。


8-17: 聊天请求边框颜色定义合理

chatRequestBorder的颜色定义符合常见UI设计模式,为不同主题模式(暗色、亮色、高对比度)提供了适当的边框颜色。


42-46: 聊天头像背景颜色定义合理

chatAvatarBackground的颜色定义为不同主题提供了适当的背景颜色,符合UI设计最佳实践。


48-52: 验证所有主题使用相同的前景色是否为预期行为

chatAvatarForeground对所有主题变体都使用相同的foreground值。虽然这可能是有意为之,但值得检查这是否为预期行为,或者不同主题是否应有不同的前景色。


54-63: 编辑文件前景色定义完整

chatEditedFileForeground为所有主题模式提供了适当的颜色值,描述清晰说明了其用途。


1-64: 总体评价:结构良好的主题颜色定义模块

整个文件结构清晰,命名遵循一致的'chat'前缀模式,每个颜色token都有适当的不同主题定义和说明文档。代码符合项目的主题系统设计模式,为聊天界面提供了必要的颜色定义。

这些新增的颜色token很好地支持了PR目标中提到的"新的聊天提及输入功能",为MCP模型提供了必要的主题支持。


19-28:

✅ Verification successful

注意:hcLight主题下为null值

在chatRequestBackground的定义中,hcLight主题设置为null。这可能是有意为之,但通常所有主题变体都应有有效的颜色值。请确认这是预期行为而非疏忽。


🏁 Script executed:

#!/bin/bash
# 检查代码库中其他颜色token是否有类似的null值定义
rg -A 1 -B 1 "hcLight: null" --type ts

Length of output: 60466


请确认 hcLight 为空值是否符合设计预期

chatColors.ts 中,chatRequestBackgroundhcLight 属性设置为 null。经过全局搜索,发现代码库中许多其他颜色 token(例如在 ai-native.ts 等文件中)也采用了 hcLight: null 的配置。这表明这种写法很可能是设计上的一致做法。

如果该设定是符合设计预期的,则可以忽略此疑问;如果需要在高对比度(hc)轻主题下显示有效的颜色,请根据产品需求修改该值。

packages/ai-native/src/browser/components/mention-input/mention-item.tsx (1)

1-25: 代码结构良好,无需修改!

此组件实现了提及项的渲染,结构简洁明了,逻辑清晰。组件通过 isActive 状态适当地应用样式,支持键盘导航,并且正确处理子项指示器的条件渲染。使用 CSS 模块进行样式隔离也是一个很好的做法。

packages/ai-native/src/browser/chat/chat.view.tsx (2)

189-194: 优雅处理 MCP 配置条件渲染

使用条件渲染来决定使用哪种输入组件是一个好方法。这段代码通过检查 aiNativeConfigService.capabilities.supportsMCP 来决定是否使用新的 ChatMentionInput 组件,实现了 PR 描述中提到的"此功能仅在启用 MCP 配置时显示"的需求。

代码实现良好,逻辑清晰。


831-832: 正确更新了 onSend 属性的传递方式

onSend 属性直接设置为 handleSend 函数而不是包装在对象中是一个好的改进,简化了接口并减少了额外的包装层。这种更改与 ChatMentionInput 组件的新接口保持一致。

packages/ai-native/src/browser/components/mention-input/types.ts (1)

47-51: MentionType 枚举可能需要扩展

当前 MentionType 枚举仅包含 FILE、FOLDER 和 CODE 三种类型。根据 PR 目标描述,该功能是为 MCP 模型设计的,可能需要考虑未来扩展其他类型的可能性。建议添加注释说明每种类型的用途,并考虑是否需要一个默认类型。

Ricbet
Ricbet previously approved these changes Mar 11, 2025
Copy link

codecov bot commented Mar 11, 2025

Codecov Report

Attention: Patch coverage is 13.38028% with 123 lines in your changes missing coverage. Please review.

Project coverage is 53.18%. Comparing base (d6ddd66) to head (b09ee35).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...-native/src/browser/context/llm-context.service.ts 0.00% 40 Missing and 11 partials ⚠️
...tive/src/common/prompts/context-prompt-provider.ts 0.00% 35 Missing and 7 partials ⚠️
...ckages/file-search/src/node/file-search.service.ts 28.57% 9 Missing and 1 partial ⚠️
...tive/src/browser/components/mention-input/types.ts 0.00% 6 Missing and 2 partials ⚠️
...s/ai-native/src/browser/chat/chat-agent.service.ts 0.00% 7 Missing ⚠️
packages/ai-native/src/common/utils.ts 16.66% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4466      +/-   ##
==========================================
- Coverage   53.24%   53.18%   -0.06%     
==========================================
  Files        1663     1665       +2     
  Lines      102619   102749     +130     
  Branches    22201    22235      +34     
==========================================
+ Hits        54639    54650      +11     
- Misses      39920    40016      +96     
- Partials     8060     8083      +23     
Flag Coverage Δ
jsdom 48.68% <10.56%> (-0.06%) ⬇️
node 12.20% <11.97%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/ai-native/src/browser/components/ChatMentionInput.tsx (2)

106-149: ⚠️ Potential issue

文件搜索操作缺少错误处理机制

在文件搜索相关的异步操作中没有添加错误处理逻辑。如果 recentFilesManager.getMostRecentlyOpenedFiles()searchService.find() 等异步操作抛出异常,当前代码没有捕获和处理这些错误,可能导致组件崩溃或功能失效。

建议添加适当的 try-catch 块来处理可能出现的异常:

  getItems: async (searchText: string) => {
+   try {
      if (!searchText) {
        const recentFile = await recentFilesManager.getMostRecentlyOpenedFiles();
        return Promise.all(
          recentFile.map(async (file) => {
            // ...
          }),
        );
      } else {
        const rootUris = (await workspaceService.roots).map((root) => new URI(root.uri).codeUri.fsPath.toString());
        const results = await searchService.find(searchText, {
          // ...
        });
        return Promise.all(
          results.map(async (file) => {
            // ...
          }),
        );
      }
+   } catch (error) {
+     console.error('获取文件列表时出错:', error);
+     return []; // 返回空数组作为回退
+   }
  },

174-218: ⚠️ Potential issue

文件夹搜索操作同样缺少错误处理

与文件搜索类似,文件夹搜索的异步操作也没有添加错误处理逻辑。如果网络请求失败或服务不可用,可能会导致未捕获的异常。

建议添加适当的错误处理机制:

  getItems: async (searchText: string) => {
+   try {
      if (!searchText) {
        const recentFile = await recentFilesManager.getMostRecentlyOpenedFiles();
        const recentFolder = Array.from(new Set(recentFile.map((file) => new URI(file).parent.codeUri.fsPath)));
        return Promise.all(
          // ...
        );
      } else {
        const rootUris = (await workspaceService.roots).map((root) => new URI(root.uri).codeUri.fsPath.toString());
        const results = await searchService.find(searchText, {
          // ...
        });
        return Promise.all(
          // ...
        );
      }
+   } catch (error) {
+     console.error('获取文件夹列表时出错:', error);
+     return []; // 返回空数组作为回退
+   }
  },
🧹 Nitpick comments (4)
packages/ai-native/src/browser/components/ChatMentionInput.tsx (4)

64-82: 建议移除或完成未使用的代码段

当前代码中包含一段被注释掉的"code"提及类型实现。如果这是一个待实现的功能,建议添加TODO注释说明;如果是不再需要的代码,建议完全移除以避免代码混淆。

- // {
-   //   id: 'code',
-   //   type: 'code',
-   //   text: 'Code',
-   //   icon: getIcon('codebraces'),
-   //   getHighestLevelItems: () => [],
-   //   getItems: async (searchText: string) => {
-   //     const currentEditor = editorService.currentEditor;
-   //     if (!currentEditor) {
-   //       return [];
-   //     }
-   //     const currentDocumentModel = currentEditor.currentDocumentModel;
-   //     if (!currentDocumentModel) {
-   //       return [];
-   //     }
-   //     const symbols = await commandService.executeCommand('_executeFormatDocumentProvider', currentDocumentModel.uri.codeUri);
-   //     return [];
-   //   },
- // },

或者添加明确的TODO注释:

+ // TODO: 后续实现代码片段提及功能
  // {
  //   id: 'code',
  //   ...

247-255: handler参数与依赖项不匹配

handleSend函数的依赖项数组中包含了editorService,但函数实现中并没有使用这个服务。这可能导致不必要的函数重新创建。

  const handleSend = useCallback(
    async (content: string, option?: { model: string; [key: string]: any }) => {
      if (disabled) {
        return;
      }
      onSend(content, undefined, undefined, option);
    },
-   [onSend, editorService, disabled],
+   [onSend, disabled],
  );

109-123: 考虑优化大量文件处理的性能

当处理大量文件时,Promise.allmap的组合可能导致性能问题。对于大型工作区,同时处理太多文件可能会影响用户体验。

建议考虑以下优化方案:

  1. 限制同时处理的文件数量
  2. 使用批处理方式处理文件
  3. 考虑使用虚拟列表渲染大量结果

示例实现:

- return Promise.all(
-   recentFile.map(async (file) => {
-     const uri = new URI(file);
-     const relatveParentPath = (await workspaceService.asRelativePath(uri.parent))?.path;
-     return {
-       id: uri.codeUri.fsPath,
-       type: MentionType.FILE,
-       text: uri.displayName,
-       value: uri.codeUri.fsPath,
-       description: relatveParentPath || '',
-       contextId: uri.codeUri.fsPath,
-       icon: labelService.getIcon(uri),
-     };
-   }),
- );
+ // 限制处理的文件数量
+ const filesToProcess = recentFile.slice(0, 20); // 只处理最近的20个文件
+ return Promise.all(
+   filesToProcess.map(async (file) => {
+     const uri = new URI(file);
+     const relatveParentPath = (await workspaceService.asRelativePath(uri.parent))?.path;
+     return {
+       id: uri.codeUri.fsPath,
+       type: MentionType.FILE,
+       text: uri.displayName,
+       value: uri.codeUri.fsPath,
+       description: relatveParentPath || '',
+       contextId: uri.codeUri.fsPath,
+       icon: labelService.getIcon(uri),
+     };
+   }),
+ );

Also applies to: 133-147


43-50: 建议添加清理逻辑

组件中注入了多个服务,但没有添加清理逻辑。虽然大多数服务的清理可能由框架处理,但最好显式处理一些可能的副作用,比如事件监听器或订阅。

建议添加 useEffect 的清理函数来处理可能的资源释放:

+ // 组件卸载时清理资源
+ useEffect(() => {
+   return () => {
+     // 取消正在进行的请求
+     aiChatService.cancelRequest();
+     // 清理其他可能的副作用...
+   };
+ }, [aiChatService]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a0d315 and 94a62bd.

📒 Files selected for processing (2)
  • packages/ai-native/src/browser/components/ChatMentionInput.tsx (1 hunks)
  • packages/ai-native/src/browser/components/components.module.less (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ai-native/src/browser/components/components.module.less
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
  • GitHub Check: unittest (ubuntu-latest, 18.x, node)
  • GitHub Check: unittest (macos-latest, 18.x, jsdom)
  • GitHub Check: unittest (macos-latest, 18.x, node)
  • GitHub Check: ubuntu-latest, Node.js 20.x
  • GitHub Check: build-windows
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build (macos-latest, 20.x)

@opensumi
Copy link
Contributor

opensumi bot commented Mar 12, 2025

🎉 PR Next publish successful!

3.8.3-next-1741774345.0

@erha19
Copy link
Member Author

erha19 commented Mar 12, 2025

/next

@opensumi
Copy link
Contributor

opensumi bot commented Mar 12, 2025

🎉 PR Next publish successful!

3.8.3-next-1741774971.0

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/ai-native/src/browser/chat/chat.view.tsx (1)

669-710: 🛠️ Refactor suggestion

优化文件和文件夹路径处理逻辑

handleSend 函数的重构引入了对文件和文件夹路径的处理能力,但当前实现存在改进空间:

  1. 上下文清理逻辑需要统一 - 在处理文件夹时没有检查 isCleanContext 标志,可能导致重复清理或遗漏清理
  2. 缺少路径验证和错误处理机制

建议:

  • 在文件夹处理部分也检查 isCleanContext 标志
  • 添加路径验证和错误处理
let isCleanContext = false;
if (fileMatches) {
  for (const match of fileMatches) {
    const filePath = match.replace(/\{\{@file:(.*?)\}\}/, '$1');
    if (filePath && !isCleanContext) {
      isCleanContext = true;
      llmContextService.cleanFileContext();
    }
    const fileUri = new URI(filePath);
    llmContextService.addFileToContext(fileUri, undefined, true);
    const relativePath = (await workspaceService.asRelativePath(fileUri))?.path || fileUri.displayName;
    // 获取文件内容
    // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
    processedContent = processedContent.replace(match, `\`<attached_file>${relativePath}\``);
  }
}

const folderPattern = /\{\{@folder:(.*?)\}\}/g;
const folderMatches = processedContent.match(folderPattern);
if (folderMatches) {
  for (const match of folderMatches) {
    const folderPath = match.replace(/\{\{@folder:(.*?)\}\}/, '$1');
+   // 如果尚未清理上下文,但遇到文件夹提及,则清理
+   if (!isCleanContext) {
+     isCleanContext = true;
+     llmContextService.cleanFileContext();
+   }
    const folderUri = new URI(folderPath);
+   // 验证文件夹路径是否有效
+   try {
      llmContextService.addFolderToContext(folderUri);
      const relativePath = (await workspaceService.asRelativePath(folderUri))?.path || folderUri.displayName;
      // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
      processedContent = processedContent.replace(match, `\`<attached_folder>${relativePath}\``);
+   } catch (error) {
+     console.error(`Invalid folder path: ${folderPath}`, error);
+     // 可以考虑用特殊标记替换无效路径,告知用户
+     processedContent = processedContent.replace(match, `\`<invalid_folder>${folderPath}\``);
+   }
  }
}
🧹 Nitpick comments (11)
packages/ai-native/src/browser/components/mention-input/mention-input.module.less (11)

1-6: 建议:使用主题变量统一设计风格
.input_container 的基本布局定义清晰,定位和宽度设置合理。建议将如 border-radius: 4px 这样的硬编码值用全局主题变量替换,以便全局统一风格和便于后续维护。


7-9: 问题:重复样式定义
在此处针对 .model_selector 定义了 margin-right: 5px,但文件后部(行 280-282)也有类似定义。建议核查是否为重复,如样式一致则统一到一个定义中,避免冗余。


18-30: 建议:优化编辑器体验
.editor 的基础样式设置良好,但建议考虑增加平滑的过渡效果或更细致的状态样式,以提升用户输入时的响应和体验,同时确认 overflow-y: auto 在所有主流浏览器下表现一致。


38-44: 建议:验证伪元素兼容性
通过 :empty:before[data-content=''] + .editor:before 实现占位符文本的显示非常巧妙。建议在各大浏览器中测试此选择器组合,确保在所有环境下均能稳定展示。


146-155: 说明:提及面板样式设计合理
.mention_panel 定义了背景、边距及滚动行为,为长列表内容提供了良好展示。可考虑在触摸设备上加入 -webkit-overflow-scrolling: touch,以改善滚动体验。


231-237: 建议:增强面板标题的视觉层次
.mention_panel_title 采用 Flex 布局,确保内容均匀分布。建议考虑加入分割线或额外的视觉提示,以进一步突出标题信息。


239-246: 建议:为返回按钮增加焦点样式
.back_button 的基础样式简洁,但建议在焦点状态下添加明显的样式(如轮廓或背景变化),以提升无障碍性和键盘导航体验。


252-263: 说明:提及标签样式完整
第二处 .mention_tag 定义了背景色、文字颜色、内边距和圆角等,适用于显示提及状态。请确认与编辑器内的 .mention_tag 效果一致,如存在风格冲突建议统一。


280-282: 问题:重复定义 .model_selector
此处 .model_selector 定义与之前(行 7-9)出现重复,建议统一合并,避免样式定义冗余。


298-300: 建议:清理无用注释
.popover_icon 部分的注释“移除 margin-left: auto”已说明开发意图,若该注释仅用于调试建议删除,以保持代码简洁。


306-316: 建议:验证加载条动画平滑性
.loading_bar 利用渐变和关键帧动画实现了流畅的加载效果,建议在不同设备和浏览器中测试动画的平滑性,如有必要可考虑增加浏览器前缀。

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2881d9 and b287076.

📒 Files selected for processing (3)
  • packages/ai-native/src/browser/chat/chat.view.tsx (11 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-input.module.less (1 hunks)
  • packages/file-tree-next/src/browser/file-tree-contribution.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
  • GitHub Check: build-windows
  • GitHub Check: unittest (ubuntu-latest, 18.x, node)
  • GitHub Check: unittest (macos-latest, 18.x, jsdom)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: unittest (macos-latest, 18.x, node)
  • GitHub Check: build (macos-latest, 20.x)
  • GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (27)
packages/ai-native/src/browser/components/mention-input/mention-input.module.less (21)

11-16: 说明:编辑区域样式设计合理
.editor_area 通过设置内边距及高度区间(min-height 与 max-height)有效管理了文本输入区域的高度和空间,整体设计清晰。


32-35: 说明:内联提及标签明确
内部嵌套的 .mention_tag 定义仅设置了间距和垂直对齐,这为内联文本中的提及标签提供了基本而简洁的样式。


75-115: 说明:页脚区域布局清晰
.footer 以及其子类(如 .left_control.right_control)使用 Flex 布局来实现控件的对齐,结构清晰且设计合理。建议再检查一下内外边距的统一性,确保与整体风格一致。


117-131: 说明:按钮样式与悬停效果良好
.send_button 的样式和悬停状态定义清晰,使用了平滑的背景色过渡,为用户提供了直观的交互反馈。


134-144: 说明:提及面板容器定位准确
.mention_panel_container 通过绝对定位和 transform: translateY(-100%) 实现了面板的悬浮展示,适合用于动态显示提及列表。建议确认在不同内容量下该定位能始终保持预期效果。


157-174: 说明:提及项布局规范
通过 Flex 布局处理 .mention_item 的对齐和间距,及 :last-child 的特殊处理,保证了列表项的整齐和交互反馈清晰。


176-180: 说明:列表样式设置得当
.mention_list 取消了默认的边距和列表样式,适合用作自定义列表项的容器,设计上较为简洁。


182-190: 说明:激活和悬停状态样式明确
.mention_item.active.mention_item:hover:not(.active) 分别为激活和悬停状态定义了对比鲜明的颜色和背景,有助于用户识别当前选中项。


192-196: 说明:左侧容器布局合理
.mention_item_left 使用 Flex 布局确保文本和图标能够自然排列,样式简洁且易于理解。


198-205: 说明:图标容器样式明确
.mention_item_icon 为图标设置了确定的尺寸和对齐方式,有助于保证图标在不同分辨率下均保持一致的展示效果。


207-212: 说明:文本显示设置清晰
.mention_item_text 的字体大小和间距设置合理,且采用了 white-space: pre; 保持了原有文本格式,适合需要展示格式化文本的场景。


214-224: 说明:描述文本样式设计合理
通过设置 text-overflow: ellipsisoverflow: hidden 以及 white-space: nowrap,确保文本超长情况时不会破坏整体布局,用户体验较好。


226-229: 说明:右侧文本简洁清晰
.mention_item_right 定义了合适的字体颜色和大小,有助于与左侧主要内容形成区分,视觉层次感较好。


248-250: 说明:返回按钮悬停效果清晰
在悬停状态下添加下划线的视觉反馈能有效提示按钮可交互,设计上较为到位。


265-271: 说明:图标样式设置得当
.mention_icon 定义了图标的尺寸和间距,确保图形和文本的整体对齐,视觉上清晰明确。


273-278: 说明:空状态提示样式设计合理
.empty_state 的内边距、文本对齐和字体设置能够有效地引导用户在无匹配项时获得提示信息。


284-286: 说明:MCP Logo 样式简洁
.mcp_logo 通过适当的右侧间距为后续图标排列提供了良好支持,符合 MCP 模型下的设计要求。


288-296: 说明:发送图标的交互反馈明确
.send_logo 定义的颜色、悬停状态与边距设置合理,确保图标在不同状态下均有清晰反馈。建议核查悬停颜色与整体主题的一致性。


302-304: 说明:加载容器处理恰当
.loading_container 当前设置为 display: none;,确保在必要时可以通过样式或脚本控制显示状态,整体设计合理。


318-326: 说明:关键帧动画定义清晰
@keyframes loading-bar 的动画细节设置得当,能实现加载条平滑移动。建议核查目标浏览器对该动画的支持情况。


328-332: 说明:无结果状态样式设计合理
.no_results 为无数据匹配的情况设置了内边距和字体样式,能够清晰地提示用户当前状态。

packages/ai-native/src/browser/chat/chat.view.tsx (5)

5-9: 加入新服务以支持高级文件系统功能和配置

增加了 AINativeConfigServiceLLMContextService 的注入服务,为支持新的文件和文件夹提及功能提供了基础设施。这是一个良好的改进,使组件能够处理更复杂的上下文相关操作。

Also applies to: 118-119


202-204: 基于配置条件渲染聊天输入组件

根据 MCP 支持状态选择性地使用 ChatMentionInput 组件,符合 PR 目标中提到的"仅在 MCP 设置启用时显示"的需求。这种条件渲染方式确保了功能只在支持的环境中可用。


370-372: 优化组件间数据传递

将核心服务注入到 CodeBlockWrapperInput 组件中,确保子组件可以访问文件系统和命令功能。这种依赖注入模式符合框架设计原则,使子组件能够独立处理文件和文件夹引用。

Also applies to: 479-486


283-283: 更新消息处理流程

将调用从旧的 handleSend(message.message) 更新为新的 handleSend(message.message, message.agentId, message.command),使消息处理更加灵活,能够携带更多上下文信息。


856-857: 简化聊天输入组件的接口

onSend 属性简化为直接引用 handleSend 函数,而不是通过中间结构传递,提高了代码可读性和维护性。

packages/file-tree-next/src/browser/file-tree-contribution.ts (1)

447-453: 增强文件定位命令的用户体验

通过确保文件浏览器面板在执行文件定位命令时可见,改进了用户体验。代码逻辑在处理 LOCATION 命令时,先检查面板是否可见,如果不可见则激活它并展开资源视图。这种改进使得导航到文件位置的操作更加流畅,特别是在通过聊天界面中的文件引用导航时。

唯一需要注意的是硬编码的 200ms 延迟,虽然在大多数情况下足够,但在某些低性能环境可能不足。考虑使用一个可配置的延迟值或基于事件的方式。

@erha19
Copy link
Member Author

erha19 commented Mar 13, 2025

/next

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/ai-native/src/browser/chat/chat.view.tsx (1)

670-711: 🛠️ Refactor suggestion

改进 handleSend 函数以支持文件和文件夹提及功能

handleSend 函数已被重构为支持文件和文件夹的提及功能,包括:

  1. 使用正则表达式识别 {{@file:xxx}}{{@folder:xxx}} 格式的占位符
  2. 对匹配到的文件和文件夹路径进行处理
  3. 使用 llmContextService 将文件和文件夹添加到上下文中
  4. 将占位符替换为带有特殊标记的格式以便于后续渲染

然而,当前实现在处理文件和文件夹路径时缺乏足够的验证和错误处理。

建议增加以下改进:

 if (fileMatches) {
   for (const match of fileMatches) {
     const filePath = match.replace(/\{\{@file:(.*?)\}\}/, '$1');
+    // 验证文件路径是否有效
+    try {
       if (filePath && !isCleanContext) {
         isCleanContext = true;
         llmContextService.cleanFileContext();
       }
       const fileUri = new URI(filePath);
+      // 检查文件是否存在
+      if (!await workspaceService.fileExists(fileUri)) {
+        // 处理文件不存在的情况,可以添加警告或者在消息中标记
+        continue;
+      }
       llmContextService.addFileToContext(fileUri, undefined, true);
       const relativePath = (await workspaceService.asRelativePath(fileUri))?.path || fileUri.displayName;
       // 获取文件内容
       // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
       processedContent = processedContent.replace(match, `\`<attached_file>${relativePath}\``);
+    } catch (error) {
+      // 处理URI创建失败或其他错误
+      console.error(`处理文件路径失败: ${filePath}`, error);
+      // 可以选择保留原始文本或者替换为错误提示
+    }
   }
 }

类似的改进也应该应用于文件夹处理部分。

🧹 Nitpick comments (4)
packages/ai-native/src/common/prompts/context-prompt-provider.ts (1)

84-92: 构建最近查看文件部分

方法检查文件列表是否为空,为空时返回空字符串,否则格式化文件列表并使用适当的 XML 标签包装。使用模板字符串使代码易于阅读。

建议考虑添加注释说明这个部分在整个提示中的作用。

packages/ai-native/src/browser/components/ChatMentionInput.tsx (3)

248-250: 缺少错误处理机制

handleStop 函数调用了 aiChatService.cancelRequest(),但没有处理可能出现的异常。如果取消请求失败,将导致未捕获的异常。

建议:

const handleStop = useCallback(() => {
+  try {
    aiChatService.cancelRequest();
+  } catch (error) {
+    console.error('Failed to cancel AI chat request:', error);
+  }
}, []);

227-246: 配置硬编码和国际化问题

defaultMentionInputFooterOptions 中的模型选项标签是硬编码的,而不是使用国际化函数(如 localize)。这可能导致这些标签无法在不同语言环境中正确显示。

建议:

const defaultMentionInputFooterOptions: FooterConfig = useMemo(
  () => ({
    modelOptions: [
-     { label: 'QWQ 32B', value: 'qwq-32b' },
-     { label: 'DeepSeek R1', value: 'deepseek-r1' },
+     { label: localize('aiNative.chat.model.qwq32b', 'QWQ 32B'), value: 'qwq-32b' },
+     { label: localize('aiNative.chat.model.deepseekR1', 'DeepSeek R1'), value: 'deepseek-r1' },
    ],
    defaultModel: 'deepseek-r1',
    buttons: [
      {
        id: 'mcp-server',
        icon: 'mcp',
-       title: 'MCP Server',
+       title: localize('aiNative.chat.mcpServer', 'MCP Server'),
        onClick: handleShowMCPConfig,
        position: FooterButtonPosition.LEFT,
      },
    ],
    showModelSelector: true,
  }),
  [handleShowMCPConfig],
);

19-37: 接口文档缺失

IChatMentionInputProps 接口为组件定义了多个属性,但没有为这些属性提供文档注释,这使得其他开发者难以理解各个属性的用途和预期行为。

建议添加 JSDoc 注释:

export interface IChatMentionInputProps {
+  /** 发送消息的回调函数 */
  onSend: (value: string, agentId?: string, command?: string, option?: { model: string; [key: string]: any }) => void;
+  /** 输入值变化的回调函数 */
  onValueChange?: (value: string) => void;
+  /** 输入框展开/收起状态变化的回调函数 */
  onExpand?: (value: boolean) => void;
+  /** 输入框占位符文本 */
  placeholder?: string;
  // 其他属性...
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b287076 and 3adf557.

📒 Files selected for processing (7)
  • packages/ai-native/src/browser/chat/chat.view.tsx (12 hunks)
  • packages/ai-native/src/browser/components/ChatHistory.tsx (5 hunks)
  • packages/ai-native/src/browser/components/ChatMentionInput.tsx (1 hunks)
  • packages/ai-native/src/browser/components/chat-history.module.less (5 hunks)
  • packages/ai-native/src/browser/components/mention-input/mention-input.module.less (1 hunks)
  • packages/ai-native/src/common/prompts/context-prompt-provider.ts (2 hunks)
  • packages/ai-native/src/common/utils.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/ai-native/src/browser/components/ChatHistory.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ai-native/src/browser/components/mention-input/mention-input.module.less
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: 🚀🚀🚀 Next Version for pull request
  • GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
  • GitHub Check: unittest (ubuntu-latest, 18.x, node)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build (macos-latest, 20.x)
  • GitHub Check: unittest (macos-latest, 18.x, jsdom)
  • GitHub Check: unittest (macos-latest, 18.x, node)
  • GitHub Check: build-windows
  • GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (25)
packages/ai-native/src/common/prompts/context-prompt-provider.ts (11)

3-3: 引入必要的工作区服务

正确引入了 IWorkspaceService,这是获取文件相对路径所必需的。


14-14: 接口方法返回类型更改为异步

provideContextPrompt 方法的返回类型从 string 更改为 Promise<string> 是合适的,因为现在该方法涉及异步操作(如获取文件信息)。


22-24: 注入工作区服务依赖

通过依赖注入正确引入了工作区服务,与代码中已有的模式保持一致。


25-35: 重构上下文提示方法为异步实现

将方法重构为异步实现,并通过多个辅助方法构建提示模板,使代码更加模块化和易于理解。现在方法能够获取当前文件信息并将其合并到上下文中。


37-53: 实现获取当前文件信息的异步方法

方法正确检查了当前模型的存在性,并使用工作区服务获取相对路径,这在提示上下文中可能更有用。

注意:如果 currentModel.uri 存在但 asRelativePath 方法失败,会回退到使用 fsPath,这是一个好的兼容处理。


55-82: 构建结构化的提示模板

使用结构化方法来构建提示模板,按照清晰的顺序组合不同部分。使用 filter(Boolean) 移除空部分是一个很好的处理方式。

模板结构清晰,包含了附加数据和用户查询两个主要部分,有助于 AI 模型更好地理解上下文。


94-116: 构建已附加文件部分

方法首先过滤掉已在最近查看文件中的文件,避免重复。然后为每个文件构建内容部分和错误部分(如果有)。最后将所有内容用 XML 标签包装。

代码逻辑清晰,过滤重复文件的步骤很有必要。


118-124: 构建文件内容部分

使用代码块格式(```)包装文件内容,同时显示文件路径,这对于 AI 模型理解文件内容的上下文很有帮助。


126-132: 构建行错误部分

方法正确处理了没有错误的情况,返回空字符串。当有错误时,将它们用适当的 XML 标签包装。

代码简洁有效,符合方法的目的。


134-140: 构建已附加文件夹部分

方法检查文件夹列表是否为空,为空时返回空字符串,否则将文件夹列表用 XML 标签包装。

这与其他辅助方法的模式一致,保持了代码的一致性。


142-151: 构建当前打开文件部分

方法处理了无当前文件的情况,并为当前文件内容创建了格式良好的输出,包括语言 ID 和路径信息。

使用代码块格式并包含语言 ID 有助于 AI 模型正确理解和处理文件内容。

packages/ai-native/src/common/utils.ts (1)

56-62: 实现了清理附加文件和文件夹标记的实用函数

这个新增的 cleanAttachedTextWrapper 函数实现了从文本中移除 <attached_file><attached_folder> 标签的功能,保留了标签内的内容。这是一个有用的实用函数,可以在显示对话标题或处理消息内容时使用。

正则表达式和替换逻辑都很清晰。

packages/ai-native/src/browser/chat/chat.view.tsx (8)

4-45: 导入模块和服务优化

新增的导入包括 AINativeConfigServiceLabelServiceLLMContextServiceCommandServiceIWorkspaceService 等,以支持新的文件和文件夹提及功能。同时导入了 cleanAttachedTextWrapper 工具函数来处理消息中的标记。

导入的顺序和分组符合项目规范。


119-132: 新增服务注入以支持文件和文件夹功能

为组件注入了多个新服务:

  • aiNativeConfigService 用于访问 MCP 配置
  • llmContextService 用于管理文件和文件夹上下文
  • labelService 用于标签管理
  • workspaceService 用于工作区路径处理
  • commandService 用于命令执行

这些服务的注入为新的文件和文件夹提及功能提供了必要的依赖。


199-207: 根据 MCP 配置选择合适的输入组件

现在根据 aiNativeConfigService.capabilities.supportsMCP 条件来决定使用 ChatMentionInput 还是 ChatInput 组件,增加了配置的灵活性。这个条件判断使得新的聊天提及输入功能只在 MCP 模式下显示,符合 PR 目标。


479-487: 为代码块包装器输入添加新的服务属性

CodeBlockWrapperInput 组件现在接收了额外的服务属性:

  • labelService 用于标签管理
  • workspaceService 用于工作区路径处理
  • commandService 用于命令执行

这些新属性能够支持文件和文件夹的处理功能。


698-709: 优化上下文清理逻辑

当前代码中 isCleanContext 标志仅在处理文件时设置,但文件夹处理部分没有考虑这个标志,可能导致上下文清理逻辑不一致。

建议统一管理上下文清理逻辑:

const folderMatches = processedContent.match(folderPattern);
if (folderMatches) {
  for (const match of folderMatches) {
    const folderPath = match.replace(/\{\{@folder:(.*?)\}\}/, '$1');
+   // 如果尚未清理上下文,但遇到文件夹提及,则清理
+   if (folderPath && !isCleanContext) {
+     isCleanContext = true;
+     llmContextService.cleanFileContext();
+   }
    const folderUri = new URI(folderPath);
    llmContextService.addFolderToContext(folderUri);
    const relativePath = (await workspaceService.asRelativePath(folderUri))?.path || folderUri.displayName;
    // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
    processedContent = processedContent.replace(match, `\`<attached_folder>${relativePath}\``);
  }
}

857-857: 简化了 ChatInputWrapperRender 的 onSend 属性

onSend 属性直接设置为 handleSend 函数,而不是创建一个中间对象,简化了代码并提高了可读性。这个变更与函数签名的改变相匹配,允许直接传递消息、代理ID和命令。


914-916: 使用 cleanAttachedTextWrapper 处理对话标题

现在使用 cleanAttachedTextWrapper 函数处理对话标题,确保标题中不会包含文件和文件夹的特殊标记。这样可以使标题更加简洁和易读。


921-922: 优化历史记录标题处理

类似地,这里也使用 cleanAttachedTextWrapper 函数处理历史记录的标题,保持了用户界面的一致性和清晰度。

packages/ai-native/src/browser/components/ChatMentionInput.tsx (2)

106-149: 添加错误处理以增强稳定性。

文件搜索相关的异步操作中缺少错误处理。如果 recentFilesManager.getMostRecentlyOpenedFiles()searchService.find() 抛出异常,当前代码没有捕获和处理这些错误,可能导致组件崩溃或功能失效。

建议添加适当的 try-catch 块来处理可能的异常:

  getItems: async (searchText: string) => {
+   try {
      if (!searchText) {
        const recentFile = await recentFilesManager.getMostRecentlyOpenedFiles();
        return Promise.all(/* ... */);
      } else {
        const rootUris = (await workspaceService.roots).map((root) => new URI(root.uri).codeUri.fsPath.toString());
        const results = await searchService.find(searchText, {/* ... */});
        return Promise.all(/* ... */);
      }
+   } catch (error) {
+     console.error('Error fetching file items:', error);
+     return []; // 返回空数组作为回退
+   }
  },

262-275: 组件接口实现不完整

组件没有处理 props 中的一些回调函数和属性,如 onValueChangeonExpandautoFocus 等。这些属性在接口中定义但未在组件中使用,可能导致用户期望的行为未被实现。

建议:

  1. 检查所有未使用的属性,实现它们的功能
  2. 或者从接口中移除不需要的属性
  3. 或者在代码注释中说明哪些属性暂不支持

例如,实现 onValueChange 回调:

  const [value, setValue] = useState(props.value || '');
+ 
+ // 更新内部值的同时调用外部回调
+ const updateValue = useCallback((newValue: string) => {
+   setValue(newValue);
+   props.onValueChange?.(newValue);
+ }, [props.onValueChange]);

  useEffect(() => {
    if (props.value !== value) {
-     setValue(props.value || '');
+     updateValue(props.value || '');
    }
  }, [props.value]);

然后在 MentionInput 组件上添加必要的属性:

  <MentionInput
    mentionItems={defaultMenuItems}
    onSend={handleSend}
    onStop={handleStop}
    loading={disabled}
    labelService={labelService}
    workspaceService={workspaceService}
    placeholder={localize('aiNative.chat.input.placeholder.default')}
    footerConfig={defaultMentionInputFooterOptions}
+   autoFocus={props.autoFocus}
+   value={value}
+   onChange={updateValue}
  />
packages/ai-native/src/browser/components/chat-history.module.less (3)

39-53: 新增全局弹出层样式增强用户体验

添加的全局弹出层(popover)样式使界面更好地适应主题颜色,提高了用户体验的一致性。


90-94: 新增搜索样式提升功能可用性

添加新的 .chat_history_search 类为历史记录搜索提供了明确的样式定义,增强了组件的可用性。


1-157:

❓ Verification inconclusive

CSS 类名统一更改为下划线命名方式

类名从 kebab-case 形式(如 .dm-chat-history-header)更改为 snake_case 形式(如 .chat_history_header),这种更改提高了命名一致性,但需确保所有使用这些类的组件都已相应更新。

运行以下脚本以确认所有引用都已更新:


🏁 Script executed:

#!/bin/bash
# 检查是否存在使用旧类名格式的文件

# 搜索包含旧类名格式的文件
echo "检查是否有文件仍在使用旧的 dm-chat-history 类名格式..."
rg -l "dm-chat-history" --type=tsx --type=jsx --type=ts --type=js

Length of output: 178


请注意旧类名引用检查

已将 CSS 类名统一调整为蛇形命名方式(如 .chat_history_header),请确认所有组件中对该类名(及其变体 dm-chat-history)的引用均已更新。为验证这一改动,请使用下面修订过的脚本检查:

#!/bin/bash
echo "正在检查是否存在使用旧的 dm-chat-history 类名的文件..."
rg -l "dm-chat-history" --glob "*.tsx" --glob "*.jsx" --glob "*.ts" --glob "*.js"

若该脚本返回任何文件路径,说明部分文件仍在使用旧类名格式,请及时更新这些引用以保持命名一致性。

@opensumi
Copy link
Contributor

opensumi bot commented Mar 13, 2025

🎉 PR Next publish successful!

3.8.3-next-1741848368.0

@erha19
Copy link
Member Author

erha19 commented Mar 13, 2025

/next

@opensumi
Copy link
Contributor

opensumi bot commented Mar 13, 2025

🎉 PR Next publish successful!

3.8.3-next-1741850419.0

Ricbet
Ricbet previously approved these changes Mar 13, 2025
life2015
life2015 previously approved these changes Mar 13, 2025
Copy link
Member

@life2015 life2015 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@erha19 erha19 dismissed stale reviews from life2015 and Ricbet via bfca2ca March 13, 2025 09:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/ai-native/src/browser/components/ChatEditor.tsx (1)

169-170: ⚠️ Potential issue

修复正则表达式中的空字符类问题

这两个正则表达式中使用了空字符类 [^],可能在某些正则引擎中出现兼容性问题。建议修改为更标准的写法。

- const rgBlockCode = /```([^]+?)```/g;
- const rgBlockCodeBefore = /```([^]+)?/g;
+ const rgBlockCode = /```([\s\S]+?)```/g;
+ const rgBlockCodeBefore = /```([\s\S]+)?/g;

使用 [\s\S] 可以明确表示匹配任何字符(包括换行符)的意图,更具可读性和兼容性。

🧰 Tools
🪛 Biome (1.9.4)

[error] 169-169: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)


[error] 170-170: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)

🧹 Nitpick comments (5)
packages/ai-native/src/browser/components/ChatEditor.tsx (5)

171-172: 添加的正则表达式命名可以更具描述性

新增的正则表达式命名可以改进,使其更清晰地表达其用途。

- const rgAttachedFile = /<attached_file>(.*)/g;
- const rgAttachedFolder = /<attached_folder>(.*)/g;
+ const rgAttachedFilePattern = /<attached_file>(.*)/g;
+ const rgAttachedFolderPattern = /<attached_folder>(.*)/g;

这样可以更清晰地表明这些变量是用于匹配模式的正则表达式。


173-194: handleAttachmentClick 函数的错误处理可以改进

当前的错误处理逻辑使用了不必要的 continue 语句,并且缺乏明确的错误日志。

const handleAttachmentClick = useCallback(
  async (text: string, type: MentionType) => {
    const roots = await workspaceService?.roots;
    let uri;
    if (!roots) {
+     console.warn('无法获取工作区根目录');
      return;
    }
    for (const root of roots) {
      uri = new URI(root.uri).resolve(text);
      try {
        await commandService?.executeCommand(FILE_COMMANDS.REVEAL_IN_EXPLORER.id, uri);
        if (type === MentionType.FILE) {
          await commandService?.executeCommand(EDITOR_COMMANDS.OPEN_RESOURCE.id, uri);
        }
        break;
      } catch (e) {
-       continue;
+       console.debug(`在路径 ${root.uri} 中无法找到 ${text}`, e);
      }
    }
+   if (!uri) {
+     console.warn(`在所有工作区根目录中都无法找到 ${text}`);
+   }
  },
  [commandService, workspaceService],
);
🧰 Tools
🪛 Biome (1.9.4)

[error] 189-189: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


308-310: CodeBlockWrapper 中的依赖注入处理

当前实现将服务直接作为 props 传递,但可以考虑使用 React context 或自定义 hooks 来改进依赖注入。

建议创建一个自定义 hook,统一处理这些服务的注入,而不是在每个组件中单独传递。比如:

// 自定义 hook
function useChatServices() {
  const labelService = useInjectable<LabelService>(LabelService);
  const commandService = useInjectable<CommandService>(CommandService);
  const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
  
  return {
    labelService,
    commandService,
    workspaceService,
  };
}

// 在组件中使用
export const CodeBlockWrapper = ({ text, renderText, relationId, agentId }: {
  text?: string;
  relationId: string;
  renderText?: (t: string) => React.ReactNode;
  agentId?: string;
}) => {
  const { labelService, commandService, workspaceService } = useChatServices();
  
  return (
    <div className={styles.ai_chat_code_wrapper}>
      <div className={styles.render_text}>
        <CodeBlock
          content={text}
          labelService={labelService}
          renderText={renderText}
          relationId={relationId}
          agentId={agentId}
          commandService={commandService}
          workspaceService={workspaceService}
        />
      </div>
    </div>
  );
}

这样可以简化组件接口,减少 props 传递,提高代码可维护性。

Also applies to: 317-318, 323-330


340-342: CodeBlockWrapperInput 中的 props 顺序

该组件中,props 的顺序与其他地方不一致,建议保持一致以提高代码可读性。

export const CodeBlockWrapperInput = ({
  text,
  relationId,
  agentId,
  command,
- labelService,
- workspaceService,
- commandService,
+ labelService,
+ commandService,
+ workspaceService,
}: {
  text: string;
  relationId: string;
  agentId?: string;
  command?: string;
- labelService?: LabelService;
- workspaceService?: IWorkspaceService;
- commandService?: CommandService;
+ labelService?: LabelService;
+ commandService?: CommandService;
+ workspaceService?: IWorkspaceService;
}) => {
  // ...
}

将 props 的顺序调整为与 CodeBlockWrapper 一致,保持代码风格统一。

Also applies to: 349-350, 384-391


251-253: 正则表达式匹配结果处理方式可优化

使用 .matchAll() 后立即展开为数组可能不是最优的处理方式,特别是对于大文本。

- const fileMatches = [...text.matchAll(rgAttachedFile)];
- const folderMatches = [...text.matchAll(rgAttachedFolder)];
- if (fileMatches.length || folderMatches.length) {
+ // 先检查是否有匹配项,再进行处理
+ const hasFileMatch = rgAttachedFile.test(text);
+ const hasFolderMatch = rgAttachedFolder.test(text);
+ if (hasFileMatch || hasFolderMatch) {
+   // 只有确定有匹配项时才进行完整匹配
+   const fileMatches = hasFileMatch ? [...text.matchAll(rgAttachedFile)] : [];
+   const folderMatches = hasFolderMatch ? [...text.matchAll(rgAttachedFolder)] : [];

这种方式可以避免对没有匹配项的文本进行不必要的处理,提高性能。

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1e7383 and bfca2ca.

📒 Files selected for processing (2)
  • packages/ai-native/src/browser/components/ChatEditor.tsx (7 hunks)
  • packages/file-tree-next/src/browser/file-tree-contribution.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/file-tree-next/src/browser/file-tree-contribution.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/ai-native/src/browser/components/ChatEditor.tsx

[error] 169-169: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)


[error] 170-170: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)


[error] 189-189: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
  • GitHub Check: unittest (ubuntu-latest, 18.x, node)
  • GitHub Check: build-windows
  • GitHub Check: unittest (macos-latest, 18.x, jsdom)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build (macos-latest, 20.x)
  • GitHub Check: ubuntu-latest, Node.js 20.x
  • GitHub Check: unittest (macos-latest, 18.x, node)
🔇 Additional comments (1)
packages/ai-native/src/browser/components/ChatEditor.tsx (1)

195-204: renderAttachment 函数中的 URI 构建可能存在问题

当前函数在获取图标时直接构建了 URI,但没有考虑 text 是否为有效路径的情况。

const renderAttachment = (text: string, isFolder = false, key: string) => (
  <span
    className={styles.attachment}
    key={key}
    onClick={() => handleAttachmentClick(text, isFolder ? MentionType.FOLDER : MentionType.FILE)}
  >
-   <Icon iconClass={isFolder ? getIcon('folder') : labelService?.getIcon(new URI(text || 'file'))} />
+   <Icon iconClass={isFolder ? getIcon('folder') : labelService?.getIcon(text ? new URI(text) : new URI('file'))} />
    <span className={styles.attachment_text}>{text}</span>
  </span>
);

请验证 text 参数在传递给 new URI() 构造函数时是否总是有效的路径。如果 text 为空或无效,可能会导致异常。

ensorrow
ensorrow previously approved these changes Mar 13, 2025
Copy link
Contributor

@ensorrow ensorrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
packages/ai-native/src/browser/components/ChatEditor.tsx (1)

169-170: ⚠️ Potential issue

需要修复正则表达式中的空字符类问题

正则表达式中的 [^] 是空字符类的否定形式,可能在某些环境中有兼容性问题。建议使用 [\s\S] 替代,这样可以明确表示匹配任何字符(包括换行符)。

-const rgBlockCode = /```([^]+?)```/g;
-const rgBlockCodeBefore = /```([^]+)?/g;
+const rgBlockCode = /```([\s\S]+?)```/g;
+const rgBlockCodeBefore = /```([\s\S]+)?/g;
🧰 Tools
🪛 Biome (1.9.4)

[error] 169-169: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)


[error] 170-170: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)

packages/ai-native/src/browser/context/llm-context.service.ts (2)

205-217: 🛠️ Refactor suggestion

建议优化错误处理机制

尽管已添加了一个基本的文件夹序列化方法,但该方法缺少适当的错误处理机制。如果在获取文件夹结构时发生错误,可能会导致整个 Promise.all 失败。

建议增加错误处理:

 private async serializeAttachedFolders(folders: FileContext[], workspaceRoot: URI): Promise<string[]> {
   // 去重
   const folderPath = Array.from(new Set(folders.map((folder) => folder.uri.toString())));
-  return Promise.all(
-    folderPath.map(async (folder) => {
+  const results = await Promise.all(
+    folderPath.map(async (folder) => {
+      try {
         const folderUri = new URI(folder);
         const root = workspaceRoot.relative(folderUri)?.toString() || '/';
         return `\`\`\`\n${root}\n${(await this.getPartiaFolderStructure(folderUri.codeUri.fsPath))
           .map((line) => `- ${line}`)
           .join('\n')}\n\`\`\`\n`;
+      } catch (error) {
+        console.error(`序列化文件夹时出错: ${folder}`, error);
+        return `\`\`\`\n错误: 无法加载文件夹 ${folder}\n\`\`\`\n`;
+      }
     }),
   );
+  return results;
 }

219-253: 🛠️ Refactor suggestion

改进文件夹结构获取方法的错误处理

getPartiaFolderStructure 方法中的错误处理过于简单,只是捕获错误并返回空结果,没有提供任何错误信息或日志记录。

建议对错误处理进行以下改进:

 private async getPartiaFolderStructure(folder: string, level = 2): Promise<string[]> {
   const result: string[] = [];
   try {
     const stat = await this.fileService.getFileStat(folder);
+    if (!stat) {
+      return [`无法访问文件夹: ${folder}`];
+    }

     for (const child of stat?.children || []) {
       const relativePath = new URI(folder).relative(new URI(child.uri))!.toString();

       if (child.isSymbolicLink) {
         // 处理软链接
+        try {
           const target = await this.fileService.getFileStat(child.realUri || child.uri);
           if (target) {
             result.push(`${relativePath} -> ${target} (symbolic link)`);
           } else {
             result.push(`${relativePath} (broken symbolic link)`);
           }
+        } catch (error) {
+          result.push(`${relativePath} (symbolic link, 无法访问目标)`);
+          console.error(`访问软链接目标时出错: ${child.uri}`, error);
+        }
         continue;
       }

       if (child.type === FileType.Directory) {
         result.push(`${relativePath}/`);
         if (level > 1) {
+          try {
             const subDirStructure = await this.getPartiaFolderStructure(child.uri, level - 1);
             result.push(...subDirStructure.map((subEntry) => `${relativePath}/${subEntry}`));
+          } catch (error) {
+            result.push(`${relativePath}/... (无法访问子目录)`);
+            console.error(`访问子目录时出错: ${child.uri}`, error);
+          }
         }
       } else if (child.type === FileType.File) {
         result.push(relativePath);
       }
     }
-  } catch {
+  } catch (error) {
+    console.error(`获取文件夹结构时出错: ${folder}`, error);
+    result.push(`获取文件夹结构时出错: ${error.message || '未知错误'}`);
     return result;
   }

   return result;
 }
🧹 Nitpick comments (2)
packages/ai-native/src/browser/components/ChatEditor.tsx (2)

189-189: 移除不必要的 continue 语句

在 catch 块中的 continue 语句是不必要的,因为它是循环体中的最后一条语句。

-        } catch {
-          continue;
-        }
+        } catch {
+          // 无需操作,继续尝试下一个根目录
+        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 189-189: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


195-204: 添加图标服务未定义时的默认处理

renderAttachment 函数中,当 labelService 未定义时,文件图标可能会显示为空。建议添加默认图标处理。

-      <Icon iconClass={isFolder ? getIcon('folder') : labelService?.getIcon(new URI(text || 'file'))} />
+      <Icon iconClass={isFolder ? getIcon('folder') : (labelService?.getIcon(new URI(text || 'file')) || getIcon('file'))} />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfca2ca and 32a276d.

📒 Files selected for processing (2)
  • packages/ai-native/src/browser/components/ChatEditor.tsx (7 hunks)
  • packages/ai-native/src/browser/context/llm-context.service.ts (5 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/ai-native/src/browser/components/ChatEditor.tsx

[error] 169-169: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)


[error] 170-170: The regular expression includes this negated empty character class.

Negated empty character classes match anything.
If you want to match against [, escape it [.
Otherwise, remove the character class or fill it.

(lint/correctness/noEmptyCharacterClassInRegex)


[error] 189-189: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: unittest (ubuntu-latest, 18.x, node)
  • GitHub Check: build-windows
  • GitHub Check: unittest (macos-latest, 18.x, jsdom)
  • GitHub Check: ubuntu-latest, Node.js 20.x
  • GitHub Check: build (macos-latest, 20.x)
  • GitHub Check: unittest (macos-latest, 18.x, node)
🔇 Additional comments (11)
packages/ai-native/src/browser/components/ChatEditor.tsx (5)

173-194: 点击附件处理函数实现良好

处理附件点击的函数实现考虑周全,能够处理多个工作区根目录情况,并正确区分文件和文件夹的处理方式。

🧰 Tools
🪛 Biome (1.9.4)

[error] 189-189: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


257-277: 代码重构实现得很好

将处理文件和文件夹标记的通用逻辑抽象为 processMatches 函数,减少了代码重复,提高了代码可维护性。这种重构方式非常好。


280-281: 文件和文件夹处理实现简洁有效

使用重构后的 processMatches 函数处理文件和文件夹标记,代码变得更加简洁和可维护。


324-332: 组件参数传递完整

CodeBlock 组件参数传递完整,确保了文件和文件夹附件功能可以正常工作。


385-393: 参数传递结构保持一致

CodeBlockWrapperInput 组件中的参数传递与 CodeBlockWrapper 保持一致,这种一致性有助于代码的可维护性。

packages/ai-native/src/browser/context/llm-context.service.ts (6)

62-72: 代码实现规范

addFolderToList 方法实现与 addFileToList 方法几乎完全相同,遵循了良好的代码一致性。


91-100: 功能设计合理

addFolderToContext 方法的设计与现有的 addFileToContext 方法保持一致,简洁明了地实现了文件夹添加功能。


194-202: 方法升级为异步处理

serialize 方法改为异步是必要的,因为它现在需要等待文件夹结构的异步序列化。实现正确且符合最佳实践。


13-13: 导入依赖合理

导入 FileTypeIFileServiceClient 是必要的,用于支持新增的文件夹管理功能。


107-109: 清理方法更新完善

cleanFileContext 方法正确地更新为同时清除 attachedFilesattachedFolders 数组,保持了功能的完整性。


112-119: 上下文获取方法更新合理

getAllContextFiles 方法正确地更新为包含 attachedFolders,保持了数据结构的一致性。

@erha19 erha19 merged commit 82ddf9a into main Mar 14, 2025
11 checks passed
@erha19 erha19 deleted the feat/support-chat-input branch March 14, 2025 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🎨 feature feature required
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants