Skip to content

feat(editor): 支持在编辑器中发送选中内容和文件到会话#341

Open
lwt-sadais wants to merge 9 commits intoJ3n5en:mainfrom
lwt-sadais:feat/editor-send-to-session
Open

feat(editor): 支持在编辑器中发送选中内容和文件到会话#341
lwt-sadais wants to merge 9 commits intoJ3n5en:mainfrom
lwt-sadais:feat/editor-send-to-session

Conversation

@lwt-sadais
Copy link
Contributor

功能描述

支持在编辑器中通过右键菜单将选中内容或文件发送到会话,提升代码讨论效率。

新增功能

1. 编辑器选中代码发送到会话

  • 操作方式:在编辑器中选中多行代码 → 右键 → 点击「发送到会话」
  • 发送格式@相对路径#L起始行-L结束行 (单行时为 #L行号
  • 示例@src/main.ts#L10-L20

2. Tab 标签发送到会话

  • 操作方式:在 Tab 标签上右键 → 点击「发送到会话」
  • 发送格式@相对路径
  • 示例@src/main.ts

技术实现

  • 使用 Monaco Editor 的 addAction API 注册右键菜单项
  • 复用现有的 useTerminalWriteStore 进行会话通信
  • 路径处理逻辑与文件树右键功能保持一致(使用 normalizePath
  • 仅在存在 sessionId 时显示菜单项,向后兼容

修改文件

  • src/renderer/components/files/EditorArea.tsx:新增编辑器右键菜单和 Tab 发送逻辑
  • src/renderer/components/files/EditorTabs.tsx:新增 Tab 右键菜单项

测试建议

  1. 打开文件,选中多行代码,右键验证「发送到会话」功能
  2. 在 Tab 标签上右键验证「发送到会话」功能
  3. 验证相对路径转换是否正确
  4. 验证发送后自动聚焦到会话输入框
  5. 验证 toast 提示显示正常

相关 Issue

Closes #(issue number)

- 编辑器选中代码右键菜单新增「发送到会话」功能,支持发送带行号引用的代码片段
- Tab 标签右键菜单新增「发送到会话」功能,支持发送文件路径引用
- 发送格式与文件树右键功能保持一致,使用 @文件路径 或 @文件路径#L行号 格式
- 自动转换为相对路径,聚焦到会话输入框,显示成功提示
@github-actions

This comment has been minimized.

sadais-lwt and others added 2 commits March 5, 2026 14:21
Remove the unused rootPath parameter from EditorTabs component. The path normalization logic is already handled in EditorArea's handleSendToSession function, making this parameter redundant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 统一使用 normalizePath 处理路径归一化
- 修复选区结束在行首时的行号偏移问题
- 清理 i18n 中重复的翻译键

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

- 统一路径处理逻辑:新增 getRelativePath 辅助函数
- 修复 normalizePath 调用错误(该函数只接受一个参数)
- 修复翻译键重复问题('Send to session' 在两处定义)
- 优化 useEffect 依赖数组,使用 getRelativePath 替代 rootPath

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

- 修复 FileTree.tsx 中的翻译键不一致
- t('Send to Session') → t('Send to session')

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

- 新增 formatLineRef 辅助函数统一处理行号引用格式化
- 消除两处重复的 endLine + lineRef 计算逻辑
- 更新相关 Hook 依赖数组

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

- 添加 sessionIdRef 来存储当前的 sessionId
- 在发送内容到终端时使用 sessionIdRef.current
- 添加检查确保 sessionId 存在
@github-actions

This comment has been minimized.

使用 activeTabPathRef.current 替代闭包捕获的 activeTabPath,确保始终获取最新的活动标签路径。

修复位置:
- addAction 回调中的路径获取
- showCommentForm 函数中的路径获取

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

- 移除 addAction 外层的 sessionId 判断,改为仅在 run 回调内检查
- 从 handleEditorMount 依赖数组中移除 sessionId,减少不必要的回调重建

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude finished @lwt-sadais's task in 3m 2s —— View job


🔍 代码审查报告

👋 欢迎查看审查结果! 本次审查已完成,以下是详细分析和建议。
💡 使用提示:点击 📁 文件链接可直接跳转到代码位置,建议按优先级处理问题。

📊 审查概览 · 一目了然

📋 审查项目 🔢 发现数量 📈 占比 🎯 处理状态
📁 审查文件 4 100% 已完成
🚨 严重问题 0 0%
⚠️ 中等问题 2 50% 🟡 本版本修复
轻微问题 2 50% 🔵 后续优化

🏆 代码质量评分

📊 综合评分: 82/100  🌟🌟🌟🌟⭐
📋 本次等级: 良好

📋 问题分布

🚨 严重: ░░░░░░░░░░ 0%
⚠️ 中等: ████░░░░░░ 40%
⚡ 轻微: ████░░░░░░ 40%
总体评分: ⭐⭐⭐⭐☆ (4/5)

🎯 关键发现

🏷️ 类别 🔍 发现数量 🎯 优先级 📈 趋势
🔒 安全性 0 个 ✅ 无 ✅ 良好
⚡ 性能 1 个 🟡 中 📊 平稳
🎨 代码质量 3 个 🔵 低 📊 平稳

🎯 问题详细分析 · 📊 点击展开查看

📊 **👆 点击此处展开详细统计和图表分析**

📈 问题趋势分析

本次审查: 🟡🟡🔵🔵 (发现 4 个问题)
代码质量: ████████░░ 80%
安全评分: ██████████ 100% ✅
性能评分: ████████░░ 80%  ✅
🏆 质量指标 📊 当前分数 🎯 目标分数 📈 改进方向
代码质量 82/100 90+ ⬆️ 可优化
安全等级 100/100 100 ✅ 优秀
性能优化 80/100 85+ ⬆️ 可优化

🐛 问题清单与修复指南 · 📋 点击展开查看

📋 **👆 点击此处展开详细问题列表 (共 4 个问题) · 一键复制追踪 📌**

🚀 快速开始

⚠️ 中等级别 (Major) 建议修复

🟡 #1 src/renderer/components/files/EditorArea.tsx:441-477

🏷️ 类型: 功能缺陷 | ⏰ 优先级: P1 - 本版本修复 | 🎯 影响范围: 编辑器右键菜单

  • 🔍 问题描述editor.addActionhandleEditorMount 中注册,但使用了 t('Send to session') 作为 label。由于 addAction 只在编辑器挂载时执行一次,如果用户切换语言,菜单项的文本不会更新。同时 t 函数是在闭包中捕获的,后续语言切换不会反映到已注册的 action 上。
// src/renderer/components/files/EditorArea.tsx:441-477
editor.addAction({
  id: 'send-to-session',
  label: t('Send to session'),  // captured at mount time, won't update on locale change
  contextMenuGroupId: 'navigation',
  contextMenuOrder: 1.5,
  precondition: 'editorHasSelection',
  run: (ed) => {
    // ...
  },
});
  • 🛠️ 解决方案:这是 Monaco addAction API 的固有限制,label 在注册时就固定了。如果需要支持动态语言切换,需要在语言变更时 dispose 旧的 action 并重新注册。当前如果应用不要求运行时切换语言,此问题影响较小,可以标记为已知限制。
  • ⚠️ 潜在影响:切换语言后右键菜单项显示为旧语言文本

🟡 #2 src/renderer/components/files/EditorArea.tsx:463-464

🏷️ 类型: 代码质量 | ⏰ 优先级: P1 - 本版本修复 | 🎯 影响范围: 编辑器发送到会话

  • 🔍 问题描述:在 addActionrun 回调中,通过 useTerminalWriteStore.getState() 直接访问 store 来获取 writefocus。虽然这是为了解决闭包问题(避免捕获过期的 write/focus 引用),但组件顶层已经通过 hook 获取了 writefocus(第 133-134 行),这里又用了不同的方式获取,模式不一致。
// src/renderer/components/files/EditorArea.tsx:463-464
const terminalWrite = useTerminalWriteStore.getState().write;
const terminalFocus = useTerminalWriteStore.getState().focus;

而在 handleSendToSession(第 161-175 行)中使用的是通过 hook 获取的 writefocus

// src/renderer/components/files/EditorArea.tsx:133-134
const write = useTerminalWriteStore((state) => state.write);
const focus = useTerminalWriteStore((state) => state.focus);
  • 🛠️ 解决方案:建议添加注释说明为什么在 addAction 内部需要使用 getState() 而不是闭包中的 write/focus——因为 Monaco addActionrun 回调闭包在 mount 时固定,使用 getState() 可确保获取最新的 store 方法。这是正确的做法,但缺少解释。
  • ⚠️ 潜在影响:代码可读性和维护性

⚡ 轻微级别 (Minor) 优化建议

🔵 #3 src/renderer/components/files/EditorArea.tsx:146-158

🏷️ 类型: 代码优化 | ⏰ 优先级: P2 - 后续版本 | 🎯 影响范围: 局部

  • 🔍 问题描述getRelativePath 函数与组件中已有的 breadcrumbSegments(第 241-256 行)中的相对路径计算逻辑存在重复。两处都在做 absolutePath → relativePath 的转换,只是方式略有不同(一个用 normalizePath 做大小写标准化比较,一个用 startsWith 直接比较)。
// src/renderer/components/files/EditorArea.tsx:146-158
const getRelativePath = useCallback(
  (absolutePath: string): string => {
    if (!rootPath) return absolutePath;
    const normalizedRoot = normalizePath(rootPath);
    const normalizedPath = normalizePath(absolutePath);
    if (normalizedPath.startsWith(`${normalizedRoot}/`)) {
      return absolutePath.slice(rootPath.length + 1);
    }
    return absolutePath;
  },
  [rootPath]
);
  • 🛠️ 解决方案:当前实现功能上是正确的,getRelativePath 使用 normalizePath 进行大小写不敏感匹配更加健壮(与 FileTree 保持一致),这是好的设计。可以考虑将 breadcrumb 的路径计算也统一使用此函数,但这超出了本 PR 的范围。
  • ⚠️ 潜在影响:代码维护性,路径处理逻辑分散

🔵 #4 src/renderer/components/files/EditorTabs.tsx:241-255

🏷️ 类型: 代码优化 | ⏰ 优先级: P2 - 后续版本 | 🎯 影响范围: Tab 右键菜单

  • 🔍 问题描述:Tab 右键菜单中的「发送到会话」菜单项通过条件渲染 {sessionId && onSendToSession && (...)} 控制显示。当 sessionIdnull 时会隐藏整个菜单项,这是正确的。但分隔符 <MenuSeparator /> 也在条件块内部,这意味着没有 session 时不会有多余的分隔线,设计是合理的。
// src/renderer/components/files/EditorTabs.tsx:241-255
{sessionId && onSendToSession && (
  <>
    <MenuSeparator />
    <MenuItem
      disabled={!menuTabPath}
      onClick={() => {
        if (!menuTabPath) return;
        onSendToSession(menuTabPath);
        setMenuOpen(false);
      }}
    >
      {t('Send to session')}
    </MenuItem>
  </>
)}
  • 🛠️ 解决方案:此处实现已经是合理的。一个小建议:可以考虑给菜单项添加图标(如 MessageSquarePlus),与 FileTree 中的发送到会话菜单项保持视觉一致性。
  • ⚠️ 潜在影响:UI 一致性

📊 修复优先级矩阵

🎯 优先级 🚨 严重 ⚠️ 中等 ⚡ 轻微 📈 总计
🔴 P0 0 个 - - 0 个
🟡 P1 - 2 个 - 2 个
🔵 P2 - - 2 个 2 个
📊 合计 0 2 2 4 个

✅ 亮点与优秀实践

本次 PR 有几个值得肯定的设计决策:

  1. 使用 ref 解决闭包问题sessionIdRefactiveTabPathRef 确保 Monaco addAction 回调中始终访问到最新状态,这是处理 Monaco 事件回调的正确模式
  2. 提取 formatLineRef 辅助函数 — 行号引用逻辑被独立为可复用函数,且正确处理了光标在行首时 endLine 的边界情况(endColumn === 1 时减 1)
  3. 路径处理与 FileTree 保持一致 — 使用 normalizePath 进行大小写不敏感路径比较,与项目其他模块风格统一
  4. 向后兼容设计 — 通过 sessionId 条件控制菜单项显示,不影响无 session 场景
  5. i18n 翻译键统一 — 从多次提交可见修复了翻译键格式问题,最终统一使用 'Send to session' 风格

🎯 合并决策建议

📋 合并评估结果

📊 评估维度 ⭐ 得分 🎯 状态 💭 说明
🔐 安全性 10/10 ✅ 通过 无安全风险,仅涉及 UI 交互
📊 代码质量 8/10 ✅ 良好 结构清晰,闭包问题处理得当
⚡ 性能影响 9/10 ✅ 无影响 使用 useCallback 和 ref 避免不必要渲染
🛠️ 功能完整性 8/10 ✅ 完整 编辑器和 Tab 两个入口均已实现
🧪 测试覆盖 5/10 ⚠️ 基本 无自动化测试,依赖手动验证

📈 综合评分: 40/50 分 · 等级: 良好


🚦 最终建议

建议合并

📝 详细理由

  • 🟢 功能实现完整,编辑器选中代码和 Tab 标签两个入口均可正常使用
  • 🟢 闭包问题通过 ref + getState() 模式正确解决
  • 🟢 路径处理逻辑与 FileTree 保持一致
  • 🟢 无安全风险,无性能退化
  • 🟡 i18n 动态切换存在已知限制,但影响极小

🎯 具体行动建议

  1. 可选改进(非阻断)

  2. 后续版本改进


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants