-
Notifications
You must be signed in to change notification settings - Fork 407
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
Conversation
Walkthrough此次提交涉及多个模块的更新和新功能引入。主要改动包括:新增构建组件库的脚本;修改 AI 聊天服务中异步请求上下文的逻辑;调整并扩展聊天视图的消息处理流程;新增专门处理文件和文件夹提及的输入组件及其样式与类型定义;扩展上下文服务以支持文件夹管理;更新图标库与文件搜索逻辑;并调整本地化文本和主题色彩 tokens。 Changes
Suggested labels
Suggested reviewers
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
yarn install v1.22.22 Tip ⚡🧪 Multi-step agentic review comment chat (experimental)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 componentpackages/ai-native/src/browser/chat/chat.view.tsx (1)
590-629
: 添加了文件和文件夹上下文处理功能handleSend 函数的重构实现了文件和文件夹占位符的处理,增强了聊天功能。不过,建议增加以下改进:
- 添加更健壮的错误处理机制,特别是针对无效的文件或文件夹 URI
- 考虑重构正则表达式匹配部分,以避免重复代码模式
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
接口定义了二级菜单的配置,但可能不够直观。建议添加注释来说明getDefaultItems
和getHighestLevelItems
方法的用途和它们之间的区别。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
⛔ 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,包括图标、文本和描述,并支持嵌套子项目的显示。组件设计简洁明了,功能完整。
几点建议:
- 考虑添加键盘导航支持
- 可以为组件添加单元测试确保功能稳定性
- 建议添加更多的文档注释说明组件的使用方法
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元素的颜色标记,为不同主题(深色、浅色、高对比度)提供了一致的颜色定义。文件组织清晰,命名规范,每个颜色都有明确的描述说明。
特别好的做法:
- 为每个主题场景(暗色、亮色、高对比)都定义了对应的颜色
- 使用语义化命名
- 提供了每个颜色的用途说明
- 使用透明度和颜色组合以保持视觉一致性
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 注释为
startAutoCollection
和stopAutoCollection
方法添加了清晰的文档注释,提高了代码的可读性和可维护性。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
方法实现简洁明了,适当地处理了空值检查并调用了通知方法。与文件处理保持了一致的模式。
packages/ai-native/src/browser/components/mention-input/mention-input.tsx
Show resolved
Hide resolved
2900713
to
ddb336e
Compare
There was a problem hiding this 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 中,以提高可维护性和可测试性。
可以考虑分离的部分:
- 将提及面板逻辑分离到单独的组件
- 将输入历史记录功能封装为自定义 hook
- 将键盘导航和光标位置计算逻辑提取为工具函数
- 将二级菜单处理逻辑封装到单独的组件
这样的重构可以使代码更加模块化,更容易理解和维护。
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 32B
和DeepSeek 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 注释,特别是对于getHighestLevelItems
和getItems
这类关键方法的预期行为和返回值。
13-16
: 建议添加二级菜单配置的注释说明
SecondLevelMenuConfig
接口定义了二级菜单的配置,但缺少对接口目的和方法预期行为的说明。建议添加 JSDoc 注释,明确说明getDefaultItems
和getHighestLevelItems
方法的使用场景和返回值的意义。
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
接口中的注释需要更新为英文,并建议为onSend
和onStop
方法添加更详细的说明。特别是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
⛔ 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 tsLength of output: 60466
请确认 hcLight 为空值是否符合设计预期
在
chatColors.ts
中,chatRequestBackground
的hcLight
属性设置为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 模型设计的,可能需要考虑未来扩展其他类型的可能性。建议添加注释说明每种类型的用途,并考虑是否需要一个默认类型。
packages/ai-native/src/browser/components/mention-input/mention-input.tsx
Outdated
Show resolved
Hide resolved
Codecov ReportAttention: Patch coverage is
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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this 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.all
与map
的组合可能导致性能问题。对于大型工作区,同时处理太多文件可能会影响用户体验。建议考虑以下优化方案:
- 限制同时处理的文件数量
- 使用批处理方式处理文件
- 考虑使用虚拟列表渲染大量结果
示例实现:
- 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
📒 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)
🎉 PR Next publish successful! 3.8.3-next-1741774345.0 |
/next |
🎉 PR Next publish successful! 3.8.3-next-1741774971.0 |
There was a problem hiding this 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
函数的重构引入了对文件和文件夹路径的处理能力,但当前实现存在改进空间:
- 上下文清理逻辑需要统一 - 在处理文件夹时没有检查
isCleanContext
标志,可能导致重复清理或遗漏清理- 缺少路径验证和错误处理机制
建议:
- 在文件夹处理部分也检查
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
📒 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: ellipsis
、overflow: 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
: 加入新服务以支持高级文件系统功能和配置增加了
AINativeConfigService
和LLMContextService
的注入服务,为支持新的文件和文件夹提及功能提供了基础设施。这是一个良好的改进,使组件能够处理更复杂的上下文相关操作。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 延迟,虽然在大多数情况下足够,但在某些低性能环境可能不足。考虑使用一个可配置的延迟值或基于事件的方式。
packages/ai-native/src/browser/components/mention-input/mention-input.module.less
Outdated
Show resolved
Hide resolved
/next |
There was a problem hiding this 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
函数已被重构为支持文件和文件夹的提及功能,包括:
- 使用正则表达式识别
{{@file:xxx}}
和{{@folder:xxx}}
格式的占位符- 对匹配到的文件和文件夹路径进行处理
- 使用
llmContextService
将文件和文件夹添加到上下文中- 将占位符替换为带有特殊标记的格式以便于后续渲染
然而,当前实现在处理文件和文件夹路径时缺乏足够的验证和错误处理。
建议增加以下改进:
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
📒 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
: 导入模块和服务优化新增的导入包括
AINativeConfigService
、LabelService
、LLMContextService
、CommandService
和IWorkspaceService
等,以支持新的文件和文件夹提及功能。同时导入了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
中的一些回调函数和属性,如onValueChange
、onExpand
、autoFocus
等。这些属性在接口中定义但未在组件中使用,可能导致用户期望的行为未被实现。建议:
- 检查所有未使用的属性,实现它们的功能
- 或者从接口中移除不需要的属性
- 或者在代码注释中说明哪些属性暂不支持
例如,实现
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=jsLength 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"若该脚本返回任何文件路径,说明部分文件仍在使用旧类名格式,请及时更新这些引用以保持命名一致性。
🎉 PR Next publish successful! 3.8.3-next-1741848368.0 |
/next |
🎉 PR Next publish successful! 3.8.3-next-1741850419.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this 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
📒 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
为空或无效,可能会导致异常。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this 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
📒 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
: 导入依赖合理导入
FileType
和IFileServiceClient
是必要的,用于支持新增的文件夹管理功能。
107-109
: 清理方法更新完善
cleanFileContext
方法正确地更新为同时清除attachedFiles
和attachedFolders
数组,保持了功能的完整性。
112-119
: 上下文获取方法更新合理
getAllContextFiles
方法正确地更新为包含attachedFolders
,保持了数据结构的一致性。
Types
Background or solution
仅在 MCP 配置开启时展示,支持纯键盘操作
new.input.mp4
Changelog
support new chat mention input for MCP model
Summary by CodeRabbit
Summary by CodeRabbit
新功能
样式更新
修复