Skip to content

fix:macOS 项目树支持多选与批量文件操作 - #991

Merged
1lck merged 16 commits into
1lck:previewfrom
Absinthe-yl:preview
Oct 2, 2026
Merged

1lck merged 16 commits into
1lck:previewfrom
Absinthe-yl:preview

Conversation

@Absinthe-yl

@Absinthe-yl Absinthe-yl commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Closes #987

问题

macOS 项目树只能对单个项目操作,没有多选状态和范围选择锚点,无法对一组选中项批量拷贝、创建副本或移到废纸篓。

定位

按 issue 中维护者最新的评论,在上游 preview 上确认:

  • 递归渲染的文件行点击后直接打开文件或折叠目录,没有维护选中集合。
  • 右键菜单和文件操作都只接收单个 URL。
  • 项目树需要自己管理快捷键归属,否则在项目树里复制文件时会同时触发编辑器的文本复制。

修复

选择

  • 新增选中集合、焦点和范围锚点。⌘ 点击增减选择;Shift 点击按当前显示顺序选择连续范围。带修饰键点击只改选择,不会打开文件或折叠目录。
  • 修饰键从鼠标按下事件本身读取:每行叠加 ProjectTreeModifiedClick 覆盖层,只在 ⌘ / Shift 左键按下时命中。SwiftUI Button 的动作在鼠标抬起后才执行,那时 NSApp.currentEvent 已不是这次点击,读不到修饰键。
  • Control 点击保持 macOS 惯例,打开右键菜单,不参与多选。
  • 右键点在已选中的项上时保留当前选中组;点在未选中的项上时改为单选。
  • 目录折叠、节点消失或切换工作区时,同步清理失效的选择。
  • 只有一个选中项时,高亮跟随当前编辑器打开的文件;有多个选中项时保持用户选的组不变。
  • ⌘A 选中焦点项的同级项:
    • 焦点是文件时,选中同一目录下的项目。
    • 焦点是文件夹时,选中它旁边的同级项目,而不是它里面的内容。
    • 没有焦点或焦点在项目根节点时,选中项目顶层的项目。
    • 同级关系按界面显示的树结构计算,所以合并显示的 Java 包也能正确处理。
  • 选中文件夹时,它下面展开的内容也一起显示为选中,和批量操作会包含文件夹内容的行为一致。这些子项只用于显示,不会重复加入操作集合。

剪贴板与快捷键

  • 新增"拷贝 / 粘贴"文件菜单,以及项目树内的 ⌘A / ⌘C / ⌘V。读写的是 macOS 系统文件剪贴板,可以和 Finder 互相拷贝粘贴。
  • 快捷键只在用户点击过项目树后由项目树处理。点击了其他区域,或键盘焦点移到文本框(例如全局搜索)、其他控件后,快捷键交还给当前焦点。
  • 项目树处理过的快捷键不再继续传给编辑器。

批量文件操作

  • 支持批量创建副本、粘贴和移到废纸篓:
    • 删除前统一弹出确认,并复用原有的未保存文件保护。
    • 排除项目根目录;同时选中父目录和其中的子项时只处理父目录。
    • 与已有文件同名时生成唯一的副本名,不覆盖原文件;禁止把目录复制到它自身或子目录中。
    • 操作期间切换了工作区,返回的过期结果会被忽略。
  • 批量删除时,每处理一项前都重新检查该项是否有未保存的修改。确认删除后、移到废纸篓之前才改动的文件不会被删除。
  • 某个文档在自身移到废纸篓的过程中被修改时,保持打开以便保存;同一批里其他未修改的文档照常关闭。
  • 批量删除中途失败,或因未保存修改而停止时,提示"已停止将剩余项目移到废纸篓"。

文案与构建

  • 中文界面区分"拷贝"(剪贴板)和"复制"(创建副本),与 Finder 一致。
  • 补充中英文文案:粘贴菜单、复制/粘贴完成通知、批量删除确认、粘贴校验失败提示和中途停止提示。
  • 构建官方插件时优先使用显式设置的 SDKROOT,与宿主构建保持一致,修复本机安装多个 SDK 时插件选到不兼容 SDK 的问题。
  • 更新跨平台功能矩阵和生成的 Markdown / CSV:macOS 已实现,本轮修复后状态改回"待验证";Windows 尚未实现。

审查意见处理

  • [P1] 批量删除期间编辑后续文件会被丢弃:每项删除前重新检查未保存状态;移到废纸篓期间才被修改的文档不会被关闭。新增可控暂停第一项删除的回归测试。
  • [P2] 键盘切换焦点后项目树仍拦截文本快捷键:快捷键归属改为看当前实际焦点。焦点移到搜索框或其他控件后,⌘A / ⌘C / ⌘V 交还给它。新增焦点切换测试。
  • 功能矩阵冲突:合并最新上游 preview,保留双方的 JSON 内容后重新生成 Markdown / CSV,共 121 项能力。

验证

  • 已合并上游 preview 最新提交 afb7a766。
  • macOS Swift 6.3.3:scripts/build-macos.sh --configuration debug --triple arm64-apple-macosx 和 scripts/preview.sh 构建通过。
  • ./.agents/skills/write-stable-tests/scripts/test-stability-macos.sh -- --disable-xctest --filter 'LitheTests':1345 个测试全部通过,并生成每个测试的计时报告。新增和修改的测试包括:
    • 批量删除:删除期间修改后续文件、删除期间修改自身文档、删除中途失败。
    • 选择:⌘A 同级范围(含文件夹、压缩包、同名前缀目录)、已选文件夹内容显示为选中。
    • 点击:窗口级测试经 NSWindow.sendEvent 和真实命中测试分发,覆盖普通点击建立锚点、Shift 正反向范围、⌘ 增减选择、修饰点击不触发 Button(不打开文件、不折叠目录)、Control 点击穿透。
    • 快捷键:焦点切换后归还快捷键、已处理的快捷键不再传给编辑器。
    • 应用级集成测试 ProjectTreeBatchFileWorkflowTests:使用真实的 AppModel、服务容器、Rust 工作区扫描和磁盘文件。
  • 真实 macOS Lithe 界面手动复测通过:
    • ⌘ / Shift / Control 点击。
    • 右键保留选中组。
    • ⌘A 只作用于项目树和同级范围。
    • ⌘C / ⌘V 不再同时作用到编辑器。
    • 已选文件夹内容显示为选中。
    • 点击项目树后双击 Shift 打开全局搜索,⌘A / ⌘C / ⌘V 作用于搜索框。
  • 应用启动和操作前后,bundle 内 988 个文件的哈希不变;验证用的应用实例已关闭,没有残留进程。
  • 以下检查通过:
    • ./scripts/verify-runtime-bundle-immutability.sh
    • ./.agents/skills/write-stable-tests/scripts/verify-test-stability.sh
    • ./scripts/verify-shared-contracts.sh
    • ./scripts/verify-module-boundaries.sh
    • ./scripts/verify-platform-feature-matrix-change.sh origin/preview HEAD
    • ./scripts/verify-platform-feature-matrix.sh
    • git diff --check
  • ./scripts/verify-service-boundaries.sh 未通过:被上游已有的 AppModel+RunConfiguration.swift(616 行,超过 600 行上限)阻断。该文件与上游一致,本 PR 未修改。
  • Windows 本次未运行。

@ghfind-review ghfind-review Bot added the review: medium ghfind author score; see https://ghfind.com label Sep 30, 2026
@Absinthe-yl Absinthe-yl reopened this Oct 1, 2026
@Absinthe-yl

Copy link
Copy Markdown
Contributor Author

补充:macOS 项目树真实组合输入验证

2026-10-01 在本地构建的 Lithe 中完成手动实测,贡献者确认以下操作均正常:

  • ⌘ / Ctrl + 点击:增减选中项。
  • Shift + 点击:正向及反向范围选择。
  • 右键已选项:保留当前选中组,显示批量操作菜单。

截图显示 alpha.txt、beta.txt、delta.txt、gamma.txt 同时选中,打开右键菜单后选择仍保留。菜单包含 Copy、Paste、Duplicate 和 Move to Trash;当前中文界面中 Copy 与 Duplicate 都显示为“复制”。

macOS 项目树多选及右键保留选中组

该截图已随 PR 提交,功能矩阵的 macOS 验证状态已更新为 verified。批量文件操作、编辑器文本复制和应用包哈希检查的验证结果见 PR 正文。

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

已按仓库 lithe-code-review / develop-lithe 规则审查。结论:存在一项阻塞问题,建议修复后合并;另有一处生成文件冲突需要处理。

审查 head:1f999d74318821465817eaee0d3a12ababc9e22f,共 19 个变更文件。冲突检查使用远端 preview:4773688eb91b04c56caaf8d909b0e220de5d98f6。

1. [P1] 批量删除期间,后续文件的新编辑可能被直接丢弃

位置:WorkspaceFeatureModel.swift:741–748

未保存检查只在 requestDeleteProjectItems 弹出确认前执行。新增批量循环逐项 await 删除,期间编辑器仍可使用,后续项目删除前没有再次检查脏状态。

触发路径:选中目录 A 和文件 B → 确认批量删除 → A 的废纸篓操作较慢时编辑 B → 循环继续删除 B。删除成功后通过 WorkspaceProjectionComposition 调用 DocumentFeatureModel.closeDocuments(containedIn:),后者直接关闭文档并取消自动保存;这段新增的内存修改无法从废纸篓恢复。

来源:本次批量流程引入了首项等待期间修改后续文件的路径。此结论来自静态调用链,尚未在 macOS 实机复现。

建议:每项删除前重新检查未保存状态,并确保原生删除等待期间产生的编辑不会被无提示关闭。增加可控暂停首项删除、将后续文档改脏再继续的回归测试,断言修改被保留且没有静默删除/关闭。

2. [P2] 键盘切换焦点后,项目树仍会吞掉文本快捷键

位置:ProjectTreeKeyboardCommands.swift:55–63

ownsKeyboard 只随鼠标事件改变,没有检查当前第一响应者。点击项目树后,用快捷键打开全局搜索;SearchEverywhereView 自动聚焦输入框,但同窗口内仍可见的项目树继续拦截 ⌘A / ⌘C / ⌘V。搜索框无法执行这些文本操作;剪贴板含文件 URL 时,⌘V 还可能向项目目录复制文件。

来源:本次引入。建议依据实际焦点或 responder chain 分发快捷键,并覆盖“项目树 → 键盘打开搜索 → 文本复制粘贴”的集成测试。P2 为建议修改项,不单独作为阻塞项。

冲突文件与处理建议

使用 git merge-tree --write-tree --name-only 检查以上精确 base/head,只有一处文本冲突:

  • docs/development/platform-parity-matrix.md 顶部功能数量及实现/验证状态统计。

没有 Swift 源码冲突。双方分别增加了能力记录,shared/platform-feature-matrix.json 和 CSV 能自动合并。请保留双方 JSON 内容,再重新生成两个视图,不要直接选择一方的 Markdown 统计:

node scripts/generate-platform-feature-matrix.mjs
./scripts/verify-platform-feature-matrix.sh

按上述两个提交合并,JSON 中应有 121 项能力。后续目标分支若有新提交,以重新生成的结果为准。

范围与验证

  • #987 要求的多选、范围选择、右键保留集合及批量操作已有实现;原生剪贴板通过平台适配层接入,整体分层合理。
  • 已有 CI 记录通过,PR 也提供了 macOS 手动验证记录,但新增测试未覆盖上述两个场景。
  • 本次实际通过:运行时资源只读、变更测试稳定性、功能矩阵一致性及变更检查、git diff --check。
  • 审查环境为 Linux,未重新运行 macOS 构建、完整测试或 UI 验证。
  • 本次仅审查,没有修改实现或解决冲突。

# Conflicts:
#	docs/development/platform-parity-matrix.md
- 批量删除:每项删除前重新检查未保存文档;废纸篓操作期间被改脏的文档保持打开,
  仅关闭未修改的文档;中途失败或被未保存文档阻止时提示剩余项目未移动。
- 快捷键归属:点击项目树后若键盘焦点移到文本框(如全局搜索)或其他控件,
  ⌘A/⌘C/⌘V 交还给当前焦点,不再误复制或粘贴文件。
- Control 点击恢复为 macOS 右键菜单,⌘ 点击负责增减选择;移除为此新增的
  litheContextMenu(capturesControlClick:) 参数。
- 单选高亮跟随活动编辑器文档;去掉 ProjectFileTreeContent 中冗余的可见节点比较。
- 中文“拷贝”与“复制(创建副本)”区分;补齐粘贴校验和批量删除提示的中英文案。
- 新增单元、焦点与应用级集成测试;功能矩阵改回待验证。
本地事件监听里的 `self?.handle(event) ?? event` 会把表示“已处理”的 nil
展平成原事件,导致 ⌘A/⌘C/⌘V 在项目树处理后仍传给编辑器:⌘A 同时全选编辑器文本,
⌘V 同时把剪贴板文本粘进编辑器。改为直接返回 handle 的结果并补回归测试。

粘贴完成的提示由“已复制文件”改为“已粘贴文件”,避免复制和粘贴各弹一条相同提示。
焦点在展开的子目录内时,⌘A 只选中该目录下的可见项目;焦点在项目顶层时选中项目下全部可见项目(含子目录及其展开内容,不含根行);焦点在根行或无焦点时选中整棵可见树。重复 ⌘A 保持同一范围,并补充同名前缀目录不被误选的测试。
点击文件夹后按 ⌘A,选中与该文件夹同级的项目,而不是它的内容;焦点在文件上时选中同目录文件;无焦点或焦点在项目行时选中项目顶层项目。同级关系按显示的树结构计算,压缩显示的 Java 包也使用其显示父节点。批量操作会自动包含已选文件夹的内容。
文件夹被选中时,其下展开的文件和子目录也显示选中高亮,与批量操作会包含文件夹内容保持一致。这些子项只用于显示,不会重复加入操作集合;在多选组内右键这些子项仍保持整组,单独选中一个文件夹时右键其中的文件则以该文件为目标。
@Absinthe-yl

Absinthe-yl commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

@1lck 感谢审查。两项问题和冲突都已处理,当前 head 是 00e0eb38。

1. [P1] 批量删除期间,后续文件的新编辑可能被直接丢弃

5e7e2f6c 已修复:

  • confirmProjectItemDeletion 在处理每一项之前都会重新检查未保存状态。确认删除后,或前一项还在移入废纸篓时才改脏的文件,会被跳过,并提示先保存或丢弃修改。
  • 某项正在移入废纸篓时被改脏,删除完成后只关闭该项下未修改的文档,被改脏的文档保持打开,可以继续保存。
  • 批量删除中途因失败或未保存修改停止时,提示"已停止将剩余项目移到废纸篓",不会静默停下。
  • 回归测试:
    • batchTrashKeepsLaterItemEditedWhileEarlierItemIsTrashed 用 TestGate 暂停第一项的废纸篓操作,把后一项改脏后再放行,断言该文件没有被删除,文档也没有被关闭。
    • trashKeepsDocumentEditedWhileItsTrashOperationRuns 覆盖文档自身在删除过程中被改脏的情况。
    • batchTrashReportsSkippedItemsAfterFailure 覆盖中途删除失败的情况。

2. [P2] 键盘切换焦点后,项目树仍会吞掉文本快捷键

5e7e2f6c 和 486b8a86 已修复:

  • 项目树的快捷键归属会检查当前实际焦点。点击项目树后,如果焦点移到文本框(例如键盘打开的全局搜索),或者移到其他控件,⌘A / ⌘C / ⌘V 都交还给当前焦点。同一窗口的多个文本框共用一个字段编辑器,所以判断按实际被编辑的文本框,而不是字段编辑器本身。
  • 修复过程中在真实界面发现了另一个问题:本地事件监听里写的 self?.handle(event) ?? event 会把表示"已处理"的 nil 合并回原事件,导致项目树处理过的快捷键又传给了编辑器。已改为直接返回处理结果。
  • 回归测试:
    • treeShortcutsYieldToKeyboardFocusMovedAfterTheTreeClick 依次覆盖:项目树 → 焦点到文本框 → 焦点到另一个文本框 → 焦点到其他控件 → 点击项目树以外的区域。
    • consumedTreeShortcutIsNotAlsoDeliveredToTheEditor 检查事件监听最终交给 AppKit 的返回值。

冲突

已合并最新的 preview(afb7a766),保留双方的 shared/platform-feature-matrix.json 内容后,重新生成 Markdown 和 CSV,共 121 项能力。目前 GitHub 显示这个 PR 可以合并。

其他改动

  • Control 点击恢复为 macOS 的右键菜单,⌘ 点击负责增减选择,并移除了 litheContextMenu(capturesControlClick:) 参数。
  • ⌘A 按焦点项的同级范围全选:点中文件夹时选它旁边的项目,而不是它里面的内容;同级关系按界面显示的树结构计算,合并显示的 Java 包也能正确处理。已选文件夹下展开的内容同时显示为选中,但不会重复加入操作集合。
  • 只选中一项时,项目树高亮跟随当前编辑器文档。
  • 中文界面区分"拷贝"(剪贴板)和"复制"(创建副本);粘贴完成的提示改为"已粘贴文件"。
  • 新增应用级集成测试 ProjectTreeBatchFileWorkflowTests:使用真实的 AppModel、服务容器、Rust 工作区扫描和磁盘文件,跑通拷贝、粘贴(同名不覆盖)、创建副本、禁止粘贴到自身,以及确认删除后才修改的文件会被保留。

验证

  • 用 test-stability-macos.sh 跑完整的 LitheTests:1344 个测试全部通过。
  • 功能矩阵、模块边界、共享契约、测试稳定性、运行时 bundle 只读检查均通过。
  • verify-service-boundaries.sh 仍被上游已有的 AppModel+RunConfiguration.swift(616 行)阻断,这个文件本 PR 没有改动。
  • 在真实 macOS 界面中,选择、右键、⌘A / ⌘C / ⌘V 的作用范围都已手动复测通过;点击项目树后双击 Shift 打开全局搜索,搜索框内的 ⌘A / ⌘C / ⌘V 正常。功能矩阵中 macOS 一项改回"待验证",等待复验。

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Lithe Review|最新提交复核

结论:未发现需要阻塞合并的问题。此前指出的删除期间丢编辑 P1 和搜索框快捷键 P2,在 00e0eb3 中已有针对性修复,不再按旧代码结论列为阻塞。
范围:preview afb7a76 ← 00e0eb3,19 个变更文件,重点追踪两项旧 finding 及其新增回归测试。

旧问题复核

  1. WorkspaceFeatureModel.swift:739–766 现在逐项执行删除,并在每项前从当前 documentsProvider 重查未保存状态;真实 composition 读取的是最新 openDocuments。815–825 行只关闭仍干净的文档,Trash 等待期间新增的编辑会保留。新增可控暂停首项删除、后续文件变脏、自身变脏和失败用例与问题路径对应。
  2. ProjectTreeKeyboardCommands.swift:77–113 已检查实际 firstResponder,并把共享 NSTextView field editor 归一到所属 NSTextField。搜索输入框获得焦点后交还快捷键;已消费事件直接返回 nil,避免继续传给编辑器。

Scope Check
#987 的多选、范围选择、右键保留选中组,以及批量拷贝/粘贴、创建副本和移到废纸篓已有实现。选择状态、平台剪贴板 adapter 和工作流编排分工合理。Windows 未实现,普通批量剪切/跨目录移动也不在本 PR 范围。

非阻塞建议
可再补“点击项目树后,不先按普通键,直接双 Shift 打开搜索,再首次按 ⌘A”的自动回归,锁住真实焦点切换入口。作者已提供该路径的手动复测记录,本次没有新增高置信缺陷。

Verification
本次仅静态只读复核,未运行应用、测试或仓库脚本。作者报告的 1,344 个测试及实机验收未由本次独立重跑。查询时当前 HEAD 的部分 CI 仍在运行/排队,不能称为全部通过。此前生成文件冲突已处理,当前 GitHub 显示 mergeable=true;这不等于所有合并门禁已满足。

1lck commented Oct 1, 2026

Copy link
Copy Markdown
Owner

补充 00e0eb3 的最终 CI 状态:macOS、Windows、Database 和功能矩阵已通过;macOS 主 Swift lane 的 1,583 项测试通过,也覆盖这次批量删除竞态、焦点切换和文件工作流的新增回归。

但 macOS Plugin CI 仍失败:带 960 秒上限的测试步骤在编译 LitheTests 时到期,日志尚未进入实际测试执行,后面的官方插件打包也被跳过。编译期间另有 DecodingError / unexpected end of file,和超时是否相关还需要定位。

请查清并恢复这项 CI 后再收尾。目前没有证据把它归因为项目树逻辑回归,也不能直接认定是偶发环境问题;此前“未发现代码层面阻塞问题”的静态审查结论不等于 CI 全绿。
失败任务:https://github.com/1lck/Lithe-IDEA/actions/runs/36898829552/job/110492636452

上次运行在 960 秒时限内仍在冷编译 LitheTests(Rust 4m39s,正常约 3m),尚未进入测试执行。同一提交的 macOS 主 Swift lane 已通过,本地按相同过滤条件运行 59 项插件测试全部通过。

1lck commented Oct 1, 2026

Copy link
Copy Markdown
Owner

重试结果已核对:a5a79395 是空提交,代码与本次审过的 00e0eb3 相同。当前 macOS、Windows、Database、Plugin 和功能矩阵五项工作流均已通过,之前的 Plugin CI 门禁失败已恢复。

Plugin 重试中 59 项测试和两个官方插件包验证通过:https://github.com/1lck/Lithe-IDEA/actions/runs/36902954392
macOS 主 CI:https://github.com/1lck/Lithe-IDEA/actions/runs/36902954411

上次超时的确切原因仍未证实;本条只更新已核实的重试结果。此前代码初审结论保持不变,原生交互验收仍以对应平台的实际记录为准。

SwiftUI Button 的动作在鼠标抬起后才执行,此时 NSApp.currentEvent 已不再是这次点击,
读到的修饰键为空,Shift 点击因此退化成普通单选、范围不生效。

- 新增 ProjectTreeModifiedClick 覆盖在每一行上:只在带 ⌘/Shift 的左键按下时命中,
  直接用该 mouseDown 事件的修饰键更新选择,并且不再传给 Button,因此不会打开文件或折叠目录。
- 普通点击和 Control 点击(右键菜单)都穿透到原有的 Button 和右键捕获视图,行为不变。
- selectRow 改为接收修饰键参数,Button 动作只做普通单选。
- 新增窗口级回归测试,覆盖 Shift 正向/反向范围选择、⌘ 取消选择、修饰点击不触发打开,以及 Control+Shift 不被捕获。
@Absinthe-yl

Copy link
Copy Markdown
Contributor Author

补充修复 f5ea06bc:项目树 Shift / ⌘ 点击在真实界面中不生效。

  • 现象:先点第一行,再 Shift 点第三行,第二行没有被选中;⌘ 点击也只是普通单选。
  • 原因:选择逻辑原来在 SwiftUI Button 的动作里读取 NSApp.currentEvent?.modifierFlags。这个动作要等鼠标抬起后才执行,那时 currentEvent 已经不是这次点击了(在 NSHostingView 里实测为 nil),所以修饰键始终是空的。范围计算本身没有问题,原有的单元测试只覆盖了范围计算,没有覆盖"点击时能否拿到修饰键"这一步。
  • 修复:
    • 新增 macos/Sources/Lithe/Platform/MacOS/UI/ProjectTreeModifiedClick.swift,叠加在每一行上。它只在按下鼠标左键且带有 ⌘ / Shift 时才响应命中检测,直接用这次 mouseDown 事件自带的 modifierFlags 更新选择,并且不再把事件传给 Button,因此不会打开文件,也不会展开或折叠文件夹。
    • 普通点击和 Control 点击(右键菜单)会穿透这一层,交给原有的处理,行为不变。
    • selectRow 改为接收修饰键参数。
  • 测试:新增窗口级测试 modifiedRowClicksReadModifiersFromTheMouseDownEvent,覆盖 Shift 正向 / 反向范围选择、⌘ 取消选择、带修饰键的点击不触发打开,以及 Control+Shift 不被捕获。
  • 验证:LitheTests 全部 1345 项通过,测试稳定性、模块边界、功能矩阵和 bundle 只读检查均通过;真实 macOS 界面中 Shift 正反向范围、⌘ 增减选择和 Control 右键都已手动复测通过。

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Lithe Review|修饰点击增量复核

范围:a5a79395 → f5ea06b,1 个提交、6 个文件。
结论:未发现需要阻塞合并的问题。

把修饰键读取移到原生 mouseDown,并在文件行、目录行接入独立捕获层,能够针对这次 SwiftUI Button 延迟执行导致丢失修饰键的问题;Control 点击仍交给原有右键入口。选择算法及此前删除保护没有改动。

当前五项 CI 均通过,macOS 主 Swift lane 的 1,584 项测试包含本次新增回归。

非阻塞建议(P2):新增窗口测试在 ProjectTreeSelectionTests.swift:156–169 直接调用 overlay.mouseDown,绕过 hitTest 和真实事件分发。即使生产 hitTest 始终返回 nil,此测试也可能通过,因此 opened.isEmpty 尚不能证明修饰点击实际命中 overlay 且不会触发底层 Button。
建议补经过实际 AppKit/SwiftUI 事件分发的集成或 UI 回归,覆盖普通点击建立锚点、Shift/⌘ 多选、不打开文件或折叠目录,以及 Control 右键穿透。
https://github.com/Absinthe-yl/Lithe-IDEA/blob/f5ea06bc00b1941c99f4c5d59afdf3c27132e5ce/macos/Tests/LitheTests/ProjectTreeSelectionTests.swift#L156-L183

这是本次新增测试的证据缺口,不是已经证实的运行时回归。本轮仅静态增量复审及现有 CI 日志核对,没有独立运行原生界面;作者手动验证与自动测试覆盖范围分开看待。

审查指出原测试直接调用 overlay.mouseDown,绕过 hitTest 和窗口分发,即使
生产 hitTest 恒返回 nil 也会通过。

- 测试改为把合成的鼠标按下/抬起事件交给 NSWindow.sendEvent,由窗口命中测试
  决定交给覆盖层还是底层 Button;覆盖新增普通点击建立锚点、Shift 正反向范围、
  ⌘ 增减选择、修饰点击不触发 Button(不打开文件、不折叠目录)以及 Control
  点击穿透到底层行。
- ProjectTreeModifiedClickView 的当前事件来源改为可替换的 currentEvent,默认仍是
  NSApp.currentEvent。测试进程里经 NSApp 事件队列取出事件会让 swift-testing 的
  主队列提前退出,后续测试不再执行,所以测试只替换这一处查询。
- 反向验证:hitTest 恒返回 nil、或捕获 Control 点击时,新测试均失败。
@Absinthe-yl

Copy link
Copy Markdown
Contributor Author

@1lck 已处理修饰点击测试的 P2,提交 77e00522(只改测试,外加一处测试注入点)。

改动

  • modifiedRowClicksReachTheOverlayThroughWindowDispatch 替换原测试。它不再直接调用 overlay.mouseDown,而是把合成的按下/抬起事件交给 NSWindow.sendEvent。事件交给覆盖层还是底层 Button,由窗口自己的命中测试决定。窗口用 orderFrontRegardless() 上屏,因为不上屏的 NSHostingView 不做命中测试。
  • 覆盖的路径:
    • 普通点击走到 Button,建立范围锚点;
    • Shift 正向和反向范围选择;
    • ⌘ 增减选择(含目录行);
    • 带修饰键的点击不触发 Button,不打开文件,也不折叠目录;
    • Control+Shift 点击穿透到底层行。
  • ProjectTreeModifiedClickView 新增 currentEvent 闭包,默认值仍是 NSApp.currentEvent,生产行为不变。测试只替换这一处查询。

为什么没有让测试经过 NSApp 事件队列
试过 NSApp.postEvent + nextEvent(…, dequeue: true)。单个测试能过,但之后 swift-testing 的异步主队列会提前排空,进程以 0 退出,其余约 320 个测试不再执行,也不会报失败。用最小用例单独复现过:只要 post 一个事件、nextEvent 取出后再 await,后续测试就不再运行。CI 上这会让测试数悄悄变少,风险比替换一处查询更大。

反向验证(在生产代码里临时制造错误,确认新测试会失败)

  • hitTest 恒返回 nil:7 处断言失败。
  • 覆盖层也捕获 Control 点击:Control 穿透的断言失败。

验证

  • LitheTests 串行全量:1345 项通过,汇总行正常输出。
  • 测试稳定性、模块边界、功能矩阵、运行时 bundle 只读检查均通过。

@xiaoyumuxi xiaoyumuxi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Lithe Review|当前 HEAD 复核

范围:preview afb7a76 ← 77e0052。

未发现需要阻塞合并的 P0/P1 问题。此前批量删除丢编辑、搜索框快捷键归属,以及修饰点击测试证据缺口,在当前 HEAD 都已有针对性处理。

本轮留下 2 条非阻塞建议:一处批量文件操作的 operation ownership 并发窗口(P2),以及一处中文“拷贝 / 复制”文案不一致(P3)。

当前 Swift tests、Platform Feature Matrix、Database CI、Windows CI、macOS Plugin CI 已通过;提交本 review 时 macOS CI 的部分 package / bridge job 仍在运行,因此不把当前状态描述为全部 CI 已完成。

let sources = topLevelProjectItems(urls)
for directory in Set(sources.map { $0.deletingLastPathComponent() }).sorted(by: { $0.path < $1.path }) {
guard workspaceGeneration == generation else { return }
await pasteProjectItems(sources.filter { $0.deletingLastPathComponent() == directory }, in: directory)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] 这里的 batch ownership 没有覆盖整个批次。pasteProjectItems 在单组 copy 完成后会先把 isPerformingProjectItemOperation 设回 false,再 await refreshCurrent();于是第一组 refresh 期间,用户可以启动另一个 Paste/Duplicate/Delete。

若新操作此时重新占用 busy,原批次进入下一目录时这里再次调用 pasteProjectItems,会被它开头的 guard !isPerformingProjectItemOperation 静默跳过,导致一次批量 Duplicate 只完成前半段。批量 Trash 在单项完成后提前释放 busy、再 refresh/继续下一项,也有同类窗口。

建议让 busy/token 由外层 batch 持有到整个批次结束,内层拆成不自行 acquire/release 的单项 primitive,并补一个 gated-refresh 回归测试覆盖“第一项完成、refresh 未结束时触发第二个文件操作”的场景。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已在 cd073b6c 修复。

  • duplicateProjectItems 和 confirmProjectItemDeletion 改为由外层批次持有 isPerformingProjectItemOperation,直到整批结束才释放,最后只刷新一次。
  • 单项逻辑拆成 pasteProjectItemsWithinOperation / trashProjectItemWithinOperation,这两个函数不自行加锁,也不刷新。单项粘贴和单项删除走同一套实现,行为不变。
  • 新增回归测试 batchFileOperationsHoldTheOperationUntilTheWholeBatchFinishes:
    • 第一组复制在工作线程上暂停时,操作锁保持占用;这时插入的粘贴被拒绝,两组副本都能完成。
    • 批量复制和批量删除都只在最后一项完成后各扫描一次(记录为 [[2, 0], [2, 2]])。
  • 反向验证:恢复旧的逐组释放锁实现后,记录变成 [[1, 0], [2, 0], [2, 2]],测试失败。

"Choose a default web browser in System Settings, then retry." = "请在系统设置中选择默认网页浏览器,然后重试。";

"Paste" = "粘贴";
"Copied files" = "已复制文件";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P3] 这里建议改成“已拷贝文件”。本 PR 已明确用“拷贝”表示写入剪贴板、用“复制”表示 Duplicate;菜单的 Copy Files 也已经翻成“拷贝”。执行“拷贝”后提示“已复制文件”会再次把两种操作混在一起。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已在 cd073b6c 修改:"Copied files" 改为"已拷贝文件",和菜单里的"拷贝"保持一致。

处理 1lck#991 审查的 P2/P3:

- 批量创建副本和批量移到废纸篓原来逐项调用单项入口,每项结束后都会先释放
  isPerformingProjectItemOperation 再 await 刷新。刷新期间用户可以启动别的文件操作,
  原批次的后续项随后被入口守卫静默跳过。现在由外层批次持有操作锁直到整批结束,
  内层拆成不自行加锁、不刷新的单项实现,整批完成后只刷新一次。
- 单项粘贴和单项删除仍通过同一实现,行为不变。
- 中文“Copied files”改为“已拷贝文件”,与菜单“拷贝”一致。
- 新增回归:第一组复制暂停时操作锁保持占用、插入的粘贴被拒绝,批量复制和
  批量删除各只在最后一项完成后刷新一次。恢复旧实现时该测试失败。
Absinthe-yl and others added 2 commits October 2, 2026 16:23
@1lck
1lck merged commit 2f70823 into 1lck:preview Oct 2, 2026
4 checks passed
1lck added a commit that referenced this pull request Oct 2, 2026
* fix(agent): 统一权限菜单与英文思考强度显示 (#1008)

* Merge pull request #1023 from Mucheen/fix/agent-selector-labels

fix(macos): 收紧 Agent 配置菜单并修复双行说明裁剪

* fix:macOS 项目树支持多选与批量文件操作 (#991)

* fix(macOS): 支持项目树多选与批量文件操作 (#987)

* docs: 记录 #987 macOS 项目树手动验证结果

* docs: 添加 #987 项目树多选手动验证截图

* fix(macOS): 处理 #991 审查意见并补齐项目树批量操作边界

- 批量删除:每项删除前重新检查未保存文档;废纸篓操作期间被改脏的文档保持打开,
  仅关闭未修改的文档;中途失败或被未保存文档阻止时提示剩余项目未移动。
- 快捷键归属:点击项目树后若键盘焦点移到文本框(如全局搜索)或其他控件,
  ⌘A/⌘C/⌘V 交还给当前焦点,不再误复制或粘贴文件。
- Control 点击恢复为 macOS 右键菜单,⌘ 点击负责增减选择;移除为此新增的
  litheContextMenu(capturesControlClick:) 参数。
- 单选高亮跟随活动编辑器文档;去掉 ProjectFileTreeContent 中冗余的可见节点比较。
- 中文“拷贝”与“复制(创建副本)”区分;补齐粘贴校验和批量删除提示的中英文案。
- 新增单元、焦点与应用级集成测试;功能矩阵改回待验证。

* fix(macOS): 项目树快捷键被处理后不再同时传给编辑器

本地事件监听里的 `self?.handle(event) ?? event` 会把表示“已处理”的 nil
展平成原事件,导致 ⌘A/⌘C/⌘V 在项目树处理后仍传给编辑器:⌘A 同时全选编辑器文本,
⌘V 同时把剪贴板文本粘进编辑器。改为直接返回 handle 的结果并补回归测试。

粘贴完成的提示由“已复制文件”改为“已粘贴文件”,避免复制和粘贴各弹一条相同提示。

* fix(macOS): 项目树 ⌘A 按焦点所在目录全选

焦点在展开的子目录内时,⌘A 只选中该目录下的可见项目;焦点在项目顶层时选中项目下全部可见项目(含子目录及其展开内容,不含根行);焦点在根行或无焦点时选中整棵可见树。重复 ⌘A 保持同一范围,并补充同名前缀目录不被误选的测试。

* fix(macOS): 项目树 ⌘A 选中焦点项的同级项目

点击文件夹后按 ⌘A,选中与该文件夹同级的项目,而不是它的内容;焦点在文件上时选中同目录文件;无焦点或焦点在项目行时选中项目顶层项目。同级关系按显示的树结构计算,压缩显示的 Java 包也使用其显示父节点。批量操作会自动包含已选文件夹的内容。

* fix(macOS): 已选文件夹下的展开内容同时显示为选中

文件夹被选中时,其下展开的文件和子目录也显示选中高亮,与批量操作会包含文件夹内容保持一致。这些子项只用于显示,不会重复加入操作集合;在多选组内右键这些子项仍保持整组,单独选中一个文件夹时右键其中的文件则以该文件为目标。

* ci: 重新触发 macOS Plugin CI

上次运行在 960 秒时限内仍在冷编译 LitheTests(Rust 4m39s,正常约 3m),尚未进入测试执行。同一提交的 macOS 主 Swift lane 已通过,本地按相同过滤条件运行 59 项插件测试全部通过。

* fix(macOS): 项目树 Shift/⌘ 点击从鼠标按下事件读取修饰键

SwiftUI Button 的动作在鼠标抬起后才执行,此时 NSApp.currentEvent 已不再是这次点击,
读到的修饰键为空,Shift 点击因此退化成普通单选、范围不生效。

- 新增 ProjectTreeModifiedClick 覆盖在每一行上:只在带 ⌘/Shift 的左键按下时命中,
  直接用该 mouseDown 事件的修饰键更新选择,并且不再传给 Button,因此不会打开文件或折叠目录。
- 普通点击和 Control 点击(右键菜单)都穿透到原有的 Button 和右键捕获视图,行为不变。
- selectRow 改为接收修饰键参数,Button 动作只做普通单选。
- 新增窗口级回归测试,覆盖 Shift 正向/反向范围选择、⌘ 取消选择、修饰点击不触发打开,以及 Control+Shift 不被捕获。

* test(macOS): 项目树修饰点击回归改为经窗口命中测试分发

审查指出原测试直接调用 overlay.mouseDown,绕过 hitTest 和窗口分发,即使
生产 hitTest 恒返回 nil 也会通过。

- 测试改为把合成的鼠标按下/抬起事件交给 NSWindow.sendEvent,由窗口命中测试
  决定交给覆盖层还是底层 Button;覆盖新增普通点击建立锚点、Shift 正反向范围、
  ⌘ 增减选择、修饰点击不触发 Button(不打开文件、不折叠目录)以及 Control
  点击穿透到底层行。
- ProjectTreeModifiedClickView 的当前事件来源改为可替换的 currentEvent,默认仍是
  NSApp.currentEvent。测试进程里经 NSApp 事件队列取出事件会让 swift-testing 的
  主队列提前退出,后续测试不再执行,所以测试只替换这一处查询。
- 反向验证:hitTest 恒返回 nil、或捕获 Control 点击时,新测试均失败。

* fix(macOS): 批量文件操作在整批结束前持有操作锁

处理 #991 审查的 P2/P3:

- 批量创建副本和批量移到废纸篓原来逐项调用单项入口,每项结束后都会先释放
  isPerformingProjectItemOperation 再 await 刷新。刷新期间用户可以启动别的文件操作,
  原批次的后续项随后被入口守卫静默跳过。现在由外层批次持有操作锁直到整批结束,
  内层拆成不自行加锁、不刷新的单项实现,整批完成后只刷新一次。
- 单项粘贴和单项删除仍通过同一实现,行为不变。
- 中文“Copied files”改为“已拷贝文件”,与菜单“拷贝”一致。
- 新增回归:第一组复制暂停时操作锁保持占用、插入的粘贴被拒绝,批量复制和
  批量删除各只在最后一项完成后刷新一次。恢复旧实现时该测试失败。

---------

Co-authored-by: Lichenkang <2188718831@qq.com>

* fix(agent): 修复 Claude 鉴权、会话切换和品牌图标 (#1007)

* fix(agent): 修复 Claude 鉴权、会话切换和品牌图标

通过 Claude 适配器公开会话参数传递 API Key 路由,同时覆盖新建与历史恢复,清除冲突鉴权并保留 Codex gateway 行为。
直接观察所选 Agent 连接,等待会话设置确认后展示模型,防止旧配置闪现及准备期间提交。
统一底栏、模型入口与 Agent 菜单品牌图标,保留原生单选勾选,并补充回归、共享契约、Agent Note 与功能矩阵。

* test(git): bound graph frame sampling to visible viewports

修复 PR #1007 CI:原用例重复绘制完整 1000 行历史,CI 耗时 11.791 秒超过未调整的 10 秒上限。改为开头、中段、末段 40 行视口采样,校验实际像素,保留完整结构、单帧预算与 Release 基线;同步 CI 文档和中文工程笔记。

* feat(agent): 显示 Agent 思考、计划与斜杠命令补全 (#1016)

* feat(agent): 显示 Agent 思考、计划与斜杠命令补全

共享 Host 原本就透传 ACP SessionUpdate,但 macOS 只处理消息、工具、
标题、配置和用量,其余更新被丢弃。本次补齐四类标准更新:

- agent_thought_chunk:独立的可折叠思考块,流式输出时展开、回复或
  工具到达后收起;缓冲按角色切分,思考与回复不再混在一起;导出
  Markdown 以引用块保留,历史消息数不计思考
- plan:计划栏固定在活动统计栏上方,显示进度与当前步骤;每次整体
  替换,空计划清除
- available_commands_update:输入 / 弹出 Agent 上报的命令补全,前缀
  优先;Codex 的 $Skill 补全为 $name(codex-acp 不解析 /$name),
  输入 $ 只列 Skill;macOS 14+ 支持方向键与 Tab
- current_mode_update:Agent 自行切换模式时同步权限模式选择器

另外把模型层的兜底文案、stopReason 提示和工具证据标题改为本地化,
补齐 zh-Hans 翻译。

fixture 新增四类事件样例并加入 Rust 往返测试;更新功能矩阵与
shared-acp Agent Note。真实 Agent 已在协议层确认 Codex/Claude 会上报
命令列表、Claude 会上报思考,界面层验证仍待完成。

* fix(agent): 修复命令补全布局和思考搜索展开

为输入区保留可编辑行,空间不足时在对话布局中显示可点击的命令浮层。搜索临时展开匹配思考块,清空后恢复手动选择。补充三项回归测试并同步功能矩阵与架构笔记。

* fix(ci): release Windows Bun install process trees before retry

---------

Co-authored-by: Yao Jingxi <23722032@bjtu.edu.cn>

* feat(windows): 活动栏/标题栏/Run 面板图标替换为 IDEA 图标,并对齐 Commit 面板与共用控件 (#1025)

* feat(windows): 共用 UI 组件补齐 IDEA 风格交互基础能力

- 滚动条:修复 ScrollArea 滑块带 flex-1 被拉满整条轨道的问题,改由
  Base UI 按可视比例计算滑块长度(全局所有滚动区域受益)
- 触控板:ScrollArea 的滚轮转发支持横向滑动,仅横向可滚动的容器才
  处理,纵向列表行为不变
- 树行 SidebarTreeRow:
  - 新增 leadingAction 左侧操作位(位于展开箭头之后、图标之前),
    渲染在行按钮外并按缩进定位,避免按钮嵌套交互元素
  - 新增 variant="idea":16px 细线 chevron(IDEA expui 路径、
    tree-chevron 颜色、悬停不变色),箭头/复选框/图标/文字 4px 间距,
    图标与文字垂直居中
  - 文字区增加 data-sidebar-tree-label 供横向滚动测量
- 复选框:支持 indeterminate 半选状态,显示短横
- 拖动会话 document-resize-session:新增 axis: "y",可驱动纵向高度
- 新增对应单测(横向滑动、纵向拖动)

* feat(windows): 默认主题 Git 状态配色与表单控件 token 对齐 IDEA

配色取自 intellij-community expUI 明暗配色方案(FILESTATUS_* 与 *_LINES_COLOR):
- modified / renamed:#0033B3 / #70AEFF;暂存修改为浅蓝派生 #2652BE / #85BAFF
- added:#067D17 / #73BD79;untracked:#B23247 / #E88F89
- 新增可选 token git-file-deleted(删除文件灰 #6C707E / #6F737A);
  git-deleted 仍为红色,继续用于 diff 删除行与 -N 计数
- 新增可选 token git-gutter-added/modified/deleted,编辑器 gutter 使用
  IDEA 浅色行标记(#7FC784/#88ADF7/#767A8A,暗色 #549159/#375FAD/#868A91)
- 变更列表、提交文件树、文件树的状态颜色统一走 git-* token,
  不再使用 text-info / 灰色 / 红色临时映射
- 新增 IDEA 表单控件 token:control-border、checkbox-border、
  checkbox-background、tree-chevron
- 以上新 token 在其他主题中未定义时,均回退到原有颜色
- rebase / patch / 历史改写对话框的警告文案与操作横幅由误用的
  text-git-modified 改回 text-warning,避免随 modified 变蓝

* feat(windows): Commit 面板对齐 IntelliJ(提交区、变更列表、Amend 与视图选项)

面板与入口
- 侧栏 git 入口与面板标题由 Changes / Source Control 更名为 Commit(提交),
  图标换为 IntelliJ commit 图标;中英文案、tooltip、aria 同步
- 移除面板内 Changes / History 标签栏与 git-commit-history 组件,提交历史
  统一通过底部 Git Log 工具窗浏览;命令面板去掉 "Git: Show History"
- 清理仅服务于标签的设置 rememberLastGitPanelMode / gitLastPanelMode /
  gitSidebarTabOrder,并加入 RETIRED_SETTINGS_KEYS 清理升级用户的残留键
- Git 数据控制器不再读取 `git log --all`;"与提交比较"选择器打开时才按需
  加载最近 50 条提交

提交区(对齐 NonModalCommitPanel / CommitInputBorder / CommitLegendComponent)
- 布局:顶部左侧 Amend、右侧已选文件图例(N 添加 / N 修改 / N 删除,未跟踪
  计入添加并显示为“新增+未跟踪”,重命名计入修改,按状态着色);中间提交
  说明框;底部 Commit 与 Commit and Push... 线框按钮,右侧推送/拉取计数、
  AI 与设置
- 与变更列表之间只有一条分割线、无外框;拖动分割线或聚焦后按上/下方向键
  调整说明框高度(变更列表至少 96px,说明框最小 48px),松手后持久化
- 说明框:占位文字 "Commit Message / 提交说明";4px 圆角、1px 控件边框,
  聚焦 2px 强调色,出错时错误色
- 按钮不再因未选文件或说明为空而置灰;点击后按 IntelliJ CommitProgressPanel
  文案提示(请选择要提交的文件 / 请填写提交说明 / 二者合并),修正后消失;
  焦点在提交区内时 Commit 变为蓝色默认按钮
- Amend:勾选时读取当前 HEAD 的完整说明(标题 + 正文,新增
  getHeadCommitMessage,不再取 `git log --all` 列表首条);空草稿自动载入、
  不覆盖已输入内容、取消勾选且未编辑时恢复原草稿;HEAD 无提交时提示;
  Amend 模式下主按钮显示 Amend Commit,Commit and Push... 禁用(修正后
  需要强制推送),多仓库确认对话框按钮同步

变更列表
- 分组改为 IntelliJ 的 "Changes / 更改" 与 "Unversioned Files / 未版本控制
  的文件" 两个可展开树节点,节点名后显示灰色 "N 个文件"
- 文件、文件夹、分组节点的包含复选框移到左侧(箭头之后、图标之前),
  IDEA 新 UI 样式,分组与文件夹支持三态;文件夹名后紧跟灰色文件数
- 去掉文件行末尾的暂存 +/- 按钮及其工具代码
- 长文件名不截断,改为横向滚动:按文字真实小数宽度判断截断并留 1px 余量,
  面板从隐藏切回显示、侧栏改宽或字体加载完成后重新测量;状态刷新不再重置宽度
- 参考 IntelliJ ExpandableItemsHandler:名称超出可视右边缘时,悬停该行在
  行上方弹出完整名称(含文件数或目录),移开、滚动或点击即消失
- 工具栏新增 View Options(眼睛图标),菜单直接列出 "Directory / 目录" 与
  "Flat List / 平铺显示" 并勾选当前项,两次点击切换

其他
- 新增/更新单测:提交面板 Amend 与提示、拖动高度、分组节点与三态、复选框
  位置、行宽测量、悬停提示判断、视图选项切换、数据控制器不读取历史
- 更新 platform-feature-matrix 的 git-status-commit、git-history 行,新增
  git-status-colors-idea-alignment 行,并重新生成视图

* feat(windows): 活动栏、标题栏菜单与 Run 面板图标替换为 IDEA 图标

- 活动栏 Project / Git Log / Terminal / Run / Diagnostics 改用 IntelliJ expui toolwindows 图标
  (project、vcs、terminal、run、problems),亮色与暗色各一套
- 标题栏紧凑菜单开关新增 MenuIcon(expui/general/menu),不影响其他使用 ListIcon 的位置
- 新增 RunToolWindowIcon / ProblemsToolWindowIcon,避免改动全局 25 处使用的 WarningIcon
- Run 面板标题、空状态与命令面板统一使用 RunToolWindowIcon,移除自绘 RunIcon
- generate-idea-icons.ts 支持映射项 destination 字段,用于终端图标这类
  位于 platform/icons/src 之外(Jewel showcase 资源)的素材
- 更新 icons.test.tsx 的导出数量与双色渲染断言

* test(windows): svg-editor 测试结束后还原 locale-provider mock

bun 的 mock.module 在同一进程内会泄漏到后续测试文件。svg-editor.test.tsx
把 @/i18n/locale-provider 替换成只含 useTranslation 的桩且不还原,导致全量运行时
Commit 面板等依赖真实 LocaleProvider 的测试拿到桩而失败。

在安装 happy-dom 之前保存真实模块,并在 afterAll 中还原。buffer.store 的 mock
无法同样处理(导入真实模块会触发 Tauri 窗口 API),保持不变。

* fix(windows): Amend 按实际暂存仓库读取 HEAD,提交说明分隔条从显示高度起算

- Amend 目标由有暂存文件的仓库决定,而不是活动仓库:单仓库参与时读取该仓库的 HEAD,
  多仓库同时有暂存文件时禁用 Amend 并给出提示;目标仓库变化时撤销已加载的说明,
  并在说明未被修改时恢复原草稿
- 提交说明编辑框最多显示视口的 60%,拖动与方向键调整改为从实际显示高度开始,
  渲染与交互共用同一上限常量
- 补充 A 活动 / B 暂存、活动仓库无 HEAD、多仓库禁用、目标变化,以及窗口缩小后
  拖动与键盘调整的回归测试

* fix(git): Amend 绑定到明确的仓库与 HEAD,由 Core 拒绝多仓库改写

Core 建立提交计划时会重新读取所有仓库的暂存状态,而界面只基于缓存的状态读取
并展示了某一个仓库的提交说明。若读取之后又有别的仓库被暂存(例如外部终端,
且关闭了自动刷新),一个全局 amend 会让两个仓库共用同一条说明并同时被改写。

- Core: amend 时有暂存更改的仓库最多一个,否则 invalid_request 且不写入
- Core: 新增可选 amendTarget{repositoryId, expectedHead},拒绝未知目标、
  不一致的暂存仓库以及 HEAD 已移动(新提交或切分支)的请求;重试时目标
  已提交则跳过 HEAD 检查
- Core: 约束只在 prepare 校验,不写入 Plan,避免 macOS 的计划模型丢弃未知
  字段后 amend 悄悄变成普通提交;validate_session 兜底拒绝涉及多个暂存仓库的
  amend 续接。缺省 amendTarget 时保持单仓库下限,macOS 无需改动
- Windows: getHeadCommitMessage 同时返回 HEAD hash;面板记录读取说明的仓库与
  hash,提交与重试时转换为仓库标识发送,无法确认时提示重新加载
- 补充 Rust 单元与真实 Git 集成回归、前端面板与 API 测试
- 更新 rust-core-api 契约与工作区提交 Agent Note

* fix(git): 重试沿用计划记录的 Amend 仓库,父仓库不再被误改写

子仓库 Amend 成功而父仓库提交被钩子拒绝后,父仓库已暂存了新的子模块引用。
重试时 Core 根据当前暂存区重新推断 Amend 目标,会把它当作唯一的暂存仓库并
改写其原 HEAD,用户确认的“新增父引用提交”变成了历史改写。

- Core: Plan 新增 amendRepositoryId,首次建立计划时确定;step 只 Amend 该
  仓库,其余仓库(含只记录子模块引用的父仓库)做普通提交
- Core: 重试沿用 previous.plan 记录的仓库,指定不同 amendTarget 时拒绝;
  validate_session 拒绝指向未知仓库或非 Amend 计划的续接
- Core: 字段缺失(丢弃未知字段的客户端)时退回按暂存路径判断并限制为一个仓库,
  行为与此前一致
- Windows: 重试不再根据组件 ref 推断目标,直接交还上一次计划
- 补充真实 Git 回归(子 Amend 成功、父钩子失败、恢复后重试)及单元、前端测试
- 更新 rust-core-api 契约与工作区提交 Agent Note

* revert(windows): 移除 Commit 面板的 Amend 功能

Amend 在多仓库工作区里需要把“每个仓库的提交意图”做成 Swift 与 Rust 共享的
模型,当前 PR 内无法在 macOS 缺少构建验证的情况下安全完成,且反复引入回归。
本次先移除,Windows 提交面板恢复为只做普通提交。

- Windows: 删除 Amend 复选框、HEAD 说明读取、目标仓库推断、重试目标处理,
  以及确认框的 Amend Commit 文案和相关 i18n 键
- Core: workspace_commit 及其测试、共享契约与 Agent Note 还原为与 preview 一致,
  macOS 的 amend 布尔字段保持不变,Swift 无需改动
- 测试: 删除全部 Amend 用例,新增“提交始终为普通提交且不显示 Amend”断言
- 矩阵: 去掉功能说明中的 Amend 描述并重新生成
- 保留与 Amend 无关的修复:提交说明分隔条按实际显示高度调整、图标替换、
  svg-editor 测试的 locale mock 还原

* feat(agent): 显示 Agent 思考、计划与斜杠命令补全 (#1016)

* feat(agent): 显示 Agent 思考、计划与斜杠命令补全

共享 Host 原本就透传 ACP SessionUpdate,但 macOS 只处理消息、工具、
标题、配置和用量,其余更新被丢弃。本次补齐四类标准更新:

- agent_thought_chunk:独立的可折叠思考块,流式输出时展开、回复或
  工具到达后收起;缓冲按角色切分,思考与回复不再混在一起;导出
  Markdown 以引用块保留,历史消息数不计思考
- plan:计划栏固定在活动统计栏上方,显示进度与当前步骤;每次整体
  替换,空计划清除
- available_commands_update:输入 / 弹出 Agent 上报的命令补全,前缀
  优先;Codex 的 $Skill 补全为 $name(codex-acp 不解析 /$name),
  输入 $ 只列 Skill;macOS 14+ 支持方向键与 Tab
- current_mode_update:Agent 自行切换模式时同步权限模式选择器

另外把模型层的兜底文案、stopReason 提示和工具证据标题改为本地化,
补齐 zh-Hans 翻译。

fixture 新增四类事件样例并加入 Rust 往返测试;更新功能矩阵与
shared-acp Agent Note。真实 Agent 已在协议层确认 Codex/Claude 会上报
命令列表、Claude 会上报思考,界面层验证仍待完成。

* fix(agent): 修复命令补全布局和思考搜索展开

为输入区保留可编辑行,空间不足时在对话布局中显示可点击的命令浮层。搜索临时展开匹配思考块,清空后恢复手动选择。补充三项回归测试并同步功能矩阵与架构笔记。

* fix(ci): release Windows Bun install process trees before retry

---------

Co-authored-by: Yao Jingxi <23722032@bjtu.edu.cn>

---------

Co-authored-by: muchen <145200703+Mucheen@users.noreply.github.com>
Co-authored-by: Yao Jingxi <23722032@bjtu.edu.cn>
Co-authored-by: lick <2188718831@qq.com>

* refactor(matrix): split capability records and generate views in CI (#1026)

---------

Co-authored-by: muchen <145200703+Mucheen@users.noreply.github.com>
Co-authored-by: sinth <2235390062@qq.com>
Co-authored-by: Yao Jingxi <23722032@bjtu.edu.cn>
Co-authored-by: 若水 <frankw.wang@qq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: medium ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants