Skip to content

feat(windows): 对齐项目与提交面板交互并完善 Shelf 恢复 - #1341

Merged
Mucheen merged 6 commits into
1lck:previewfrom
mirakyux:codex/fix-project-tree-drag-session
Oct 19, 2026
Merged

Mucheen merged 6 commits into
1lck:previewfrom
mirakyux:codex/fix-project-tree-drag-session

Conversation

@mirakyux

@mirakyux mirakyux commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Windows 项目树在长文件名、定位当前文件和多选操作时使用不便,提交面板的结构与交互也与当前 macOS Swift 实现存在差异。本次按 Swift 源码补齐主要工作流,并为文件移动及 Shelf/Stash 恢复加入会话和数据保护。

Refs #1257

改动

  • 项目树支持长文件名横向滚动、虚拟列表离屏定位及重复定位;定位时清除搜索并从 Dependencies 返回 Project,等待祖先目录展开后再滚动,忽略过期请求。

  • 补齐 Ctrl/Shift 多选、同级全选、复制/剪切/删除及集合拖动;行选择、键盘焦点、范围锚点跟随真实编辑器目标的变化,明确多选不被编辑器切换覆盖;目录点击引发的共享侧栏路径往返不会把选择和复制/剪切目标改回已打开文件。移动整批预检重名和父子重复,原生移动不覆盖目标;工作区切换或 A→B→A 后停止旧批次,已成功项仍同步原工作区。

  • 项目右键补齐查找/替换、目录标记和项目本地历史入口。查找/替换复用既有全局搜索流程及工作区状态;目录标记跟随移动/删除,并按 Windows、WSL 和远程路径身份隔离。

  • 新增 Project/Dependencies 切换,展示现有 Maven owner 的模块与依赖快照;只有用户明确点击解析才启动既有 Maven 操作。POM 导航与 Maven 面板共用路径解析,支持根项目、嵌套 reactor 目录和 WSL UNC。

  • 按当前 Swift 组件对齐 Commit/Shelf 页签、紧凑操作栏、ChangeList 分组及复选框顺序、Amend、提交说明和按钮布局。保留用户字号缩放和已有尺寸偏好;说明框最小高度为 50px,窄面板布局调整,拖动结束时才保存尺寸。常驻范围说明移入提示,刷新、AI 和丢弃按钮使用对应 IDEA 图标。

  • 提交列表区分行选择与实际暂存状态,默认和命名列表一致呈现;工具栏暂存限定活动列表,Diff 预览打开完整首项,父仓库引用选项由共享 owner 控制并保留到重试计划。

  • 新增 Shelf 原生持久化与两层原始补丁恢复:先可靠保存暂存/工作区版本,再验证快照并清理;恢复先暂存后工作区,全部完成后删除记录。冲突和部分失败保留完整恢复材料,重试检查已应用层,支持未跟踪和二进制文件。工作区层通过兼容选项 git.patchExport(source: "unstaged", includeUntracked: true) 从临时索引导出,覆盖暂存删除后保留原路径、暂存重命名后重建源路径;真实索引及旧调用语义不变。

  • Stash 列表、Diff 缓存及写操作使用不可变 OID,Core 在仓库写锁内重新解析当前位置,兼容既有位置引用。Shelf/Stash 的列表、选择、确认和恢复绑定仓库会话;操作锁跨面板卸载保留,仅由原操作释放,并与提交、暂存和 ChangeList 修改互斥。

  • 本地历史按工作区隔离同名相对路径并统一时间单位,保留旧数据;WSL/remote 等尚无 Core 支持的项目历史入口保持关闭。Shelf 数据写入 app data,WSL 大小写独立,保留无法证明归属的旧桶,不写发行目录。

  • 同步共享契约与 fixtures、功能矩阵、Agent Notes、IDEA 图标资源和 Windows Cargo lock;Windows CI 注册项目树与四组 Shelf/Stash 安全回归,各组使用计时报告和本地超时,Shelf/Stash 分进程隔离 mock。

  • 为五个真实 Git Patch 集成用例同步本地 SharedRust 与 real-JDT CI 的单项预算:四项 30 秒、一次完整双层恢复 60 秒。用例实测约 17~50 秒;保留每次 Patch 请求 15 秒及原套件总期限,不改变断言或其他用例上限。

  • 修正两个上游 Swift 测试夹具:插件构建使用既有只读 fixture 目录作为必填包 URL,Web Bridge 的工作区 URL 明确标记为目录;保留所有产品逻辑、断言、等待、清理与超时。

验证

PR base:upstream/preview 的 7121aee6。当前 head:84008cee。

  • 本轮本地 Windows Release 构建、类型检查通过,使用固定 Bun 1.3.12;完成前端生产包和原生 Release 构建。
  • CI 同分组 Project 树计时回归:40 项通过,186 次断言。测试挂载真实 FileExplorerPane 并连接侧栏、缓冲区 store,覆盖目录选择、复制/剪切、键盘焦点、范围锚点、真实编辑器切换、明确多选、滚动定位和拖动生命周期。修改的选择测试最慢 94ms,目录选择回归 78ms。
  • WindowsRust 计时 harness:Project 31、Terminal 37、Host 282 项通过,另 1 项原有跳过;合计 350 项通过、1 项跳过。两个既有性能警告为 7.414s、1.130s,均未超过硬预算。
  • 最终 HTML/JSON/JUnit:.artifacts/test-stability/issue-1257-editor-selection/final/;旧实现的目录选择失败报告保留在 before/。全目录单进程试跑遇到了仓库 CI 已要求隔离的 debugger mock 冲突,已停止;相同 adapter 分进程运行 5 项通过。该混跑不计为全量测试通过。
  • lint exit 0,本次修改文件没有新增警告;测试稳定性静态检查、110 条 Agent Notes、172 项矩阵及相对实际 PR base 的变更门禁、git diff --check 通过。
  • 本轮按原脚本表达式执行 macOS/Windows 发行目录写入的等价静态扫描通过;本机没有 zsh,未将其标记为原脚本或安装包运行时哈希验收。
  • Shelf Patch/Stash 的 17 项 Core 真实 Git 回归及前端 Shelf/Maven 的 30 项回归此前在 71659004 通过,报告保留在 .artifacts/test-stability/issue-1257-review/final/。Shelf 暂存删除/保留原路径和暂存重命名/重建源路径分别为 51.8s、54.9s,使用已有 60s 单项预算;原始计时异常与最终结果均保留,没有放宽每次请求的 15s 期限。

CI 状态

  • 594df568 的 Swift tests、插件测试与包校验、macOS/Plugin/Windows/Database gates、Notes 和矩阵均已通过,确认上述两处 Swift 夹具修复;该 head 的 Windows/Core/Release 测试 lane 按范围跳过,不计作重跑通过。
  • 71659004 的 Windows 前端、Windows Rust、真实 JDT、Rust Core、macOS arm64 打包此前已通过。打包日志证实原版 verify-runtime-bundle-immutability.sh 实际执行通过;不等同于完整实机工作流哈希验收。
    当前 head 84008cee93847d2a6d1e5d94b27d8c816b580962 的所有 checks 已结束,没有排队、运行中或失败的 check;成功 gate 不代表被跳过的测试重新执行。2026-10-19 已重新读取 GitHub job 状态,结果如下。
检查 当前 84008cee 可核对证据
Windows frontend 实际执行 SUCCESS 当前 job,包含 Project 树 40 项 / 186 次断言
Windows Rust SKIPPED,未重新执行 当前 job;前序 71659004 实跑成功
Windows real JDT SKIPPED,未重新执行 当前 job;前序 71659004 实跑成功
Rust Core SKIPPED,未重新执行 当前 job;前序 71659004 实跑成功
macOS Swift tests SKIPPED,未重新执行 当前 job;前序 594df568 实跑成功
macOS Plugin tests / package SKIPPED,未重新执行 当前 job;前序 594df568 实跑成功
macOS Release package SKIPPED,未重新执行 当前 job;前序 71659004 arm64 实跑成功
Rust database tests SKIPPED,未重新执行 当前 job
Agent Notes / 功能矩阵 实际执行 SUCCESS Notes / 矩阵
Windows / macOS / Plugin / Database CI gate 全部 SUCCESS Windows、macOS、Plugin、Database

原版 scripts/verify-runtime-bundle-immutability.sh 的实际成功证据来自前序 71659004 的 macOS arm64 打包 job,Build complete macOS test package 步骤日志:

2026-10-18T17:11:43.6531080Z Runtime bundle immutability verification passed

该原始 job 日志已保存到 .artifacts/issue-1257/suggestions-20261019/71659004-macos-arm64-package.log。本轮确认 71659004..84008cee 之间该脚本及其扫描的 macos/Sources、windows/tauri/src-tauri/src 没有改动。当前 head 的打包 lane 跳过,因此没有把这条历史成功记录表述为当前 head 重新执行,也不把静态脚本视为完整安装包工作流的运行时哈希验收。

bot 已完成当前 head 的审查:没有 P0/P1/P2 finding,两条非阻塞 Suggestions 分别要求视觉证据和 CI 证据。CI 证据已补齐;当前正式 review 仍为 0,REVIEW_REQUIRED 尚未满足。

验收边界

  • 真实 WebView2 明暗主题、窄面板、拖动和完整恢复交互,以及真实 WSL/macOS 对照尚待实机验收,相关矩阵保持 pending。

  • 2026-10-19 本轮准备了独立 Git fixture 以补原生截图,但 Windows 桌面处于锁屏,已立即停止 UI 操作;没有取得可用界面截图,不将此次尝试计入 WebView2 验收。本轮新启动的 Lithe 实例及遗留 Java 子进程已关闭,未保留验证应用。解锁后可继续,macOS 前后对照和完整视觉证据仍待提供。

  • Dependencies 当前覆盖 Maven 快照;通用插件依赖、归档及虚拟文档浏览尚未达到 macOS 全部能力。

  • macOS 的完整契约脚本依赖 zsh/Ruby;本机完成共享 JSON、定向 Core 测试和发行目录写入等价静态扫描,不将这些结果视为 macOS 实机或安装包哈希验收。

  • 新上游 ci-workflow-gates.test.mjs 依赖 Linux 的 LF 文本、Bash/jq 及进程组语义;原生 Windows CRLF checkout 的直接执行在提取 gate 时失败,留待 Linux CI 验证。工作流 YAML 已单独解析检查。

  • 本轮同步引入的 macOS Swift 功能与测试、关闭 PR 后取消历史 CI 的 Bash 集成测试由完整 CI 验证,本机 Windows 未运行这些 macOS/Linux 专用检查。

@ghfind-review ghfind-review Bot added the review: medium ghfind author score; see https://ghfind.com label Oct 18, 2026
@mirakyux
mirakyux marked this pull request as ready for review October 18, 2026 16:24
@lithe-code-review

lithe-code-review Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⚠️ 有建议
范围: preview ← codex/fix-project-tree-drag-session · head: 5bd870df

未发现需要阻塞合并的问题。

Findings

  1. [P2] Shelf 的未跟踪文件层应以索引为基线
    windows/tauri/src/features/git/api/git-shelf-api.ts:50

    • 实际影响:合法的“暂存删除并保留同路径文件”状态无法完成搁置,保存的两层补丁也不能正确恢复该状态。
    • 触发条件:未跟踪路径仍存在于 HEAD,例如对已提交文件执行 git rm --cached -- file.txt,保留磁盘文件。
    • 复现步骤:保持文件内容与 HEAD 相同后点击 Shelve。Core 的 workingTree 导出以 HEAD 为基线,因此这里得到空补丁;随后 capture 逆向应用暂存删除,相当于新增仍然存在的文件,检查失败。文件内容不同时,得到的也是 HEAD→工作文件的修改补丁,而非索引缺失→新增文件的补丁,仍无法正确组合两层。
    • 来源:本次引入。既有 Core 导出符合其契约,新增 Shelf 将不同基线的补丁拼成了工作区层。
  2. [P2] Dependencies 导航应包含 Maven 项目相对路径
    windows/tauri/src/features/maven/components/project-dependencies.tsx:50

    • 实际影响:双击嵌套 Maven 项目的依赖节点,会打开不存在或属于其他项目的 pom.xml。
    • 触发条件:Maven reactor 位于工作区子目录,project.relativePath 不为 .。
    • 复现步骤:工作区为 C:/work,项目为 backend/pom.xml,依赖的 modulePath 为 service。当前代码打开 C:/work/service/pom.xml,正确位置应为 C:/work/backend/service/pom.xml。现有 maven-pane.tsx:370 的导航已包含这一级路径。
    • 来源:本次引入,位于新增 Dependencies 视图的导航处理器。

Scope Check

  • 关联需求:[Feature] win的项目/提交向mac对齐 #1257 的长名称滚动、项目树选择及提交面板主要交互已有对应实现;完整视觉与交互对齐尚未验证。
  • 已完成范围:项目树会话保护、多选与移动、Commit/Shelf、Stash OID、历史隔离和 Maven 快照投影。
  • 未完成范围:通用插件依赖、归档及虚拟文档浏览;PR 已说明这些边界。
  • 能力复用:复用了 Core/Git 补丁能力及现有 Maven owner;Finding 1 是适配时的基线错误。
  • 两项 Finding 均由本次新增代码引入,属于本次职责范围;未将未恶化的历史问题列入 Findings。

Suggestions

  1. 对应 Finding 1:通过 Core 导出以真实索引为基线、包含未跟踪文件的完整工作区层。补充真实 Git 用例,覆盖暂存删除后保留原路径,以及暂存重命名后重建源路径的搁置与恢复。
  2. 对应 Finding 2:复用现有模块 POM 路径解析,包含 project.relativePath,并覆盖工作区内嵌套 reactor 的导航。
  3. 补充 Project/Dependencies、Commit/Shelf 的截图;已有界面提供前后对比,并覆盖明暗主题及窄面板。当前上下文未提供截图,视觉效果尚未验证。

Summary

整体延续了既有 owner 和平台存储边界,会话隔离与部分失败保护较充分。上述两处有限正确性问题建议修正;现有测试未覆盖对应输入。

Verification

  • 静态检查固定 7121aee6...5bd870df 范围,追踪了项目树选择/拖动/定位、Shelf 捕获与恢复、Core 补丁与 Stash、原生持久化、Git 面板互斥、历史隔离及 Maven 导航,并阅读相关测试、契约、Notes 和 CI 注册。
  • 按完整清单进入审查;资源、二进制、生成文件及锁文件未读取正文。
  • 作者报告:同步前 78c7521d 的 Release 构建通过,合计 426 项测试通过、1 项跳过;同步后另有静态与资源复用检查。这些结果未由本次审查独立验证。
  • 未运行代码、测试、应用或验证脚本,包括 scripts/verify-runtime-bundle-immutability.sh;未访问网络。Finding 的复现路径来自静态推导。
  • 提供的 head 检查仍有排队或运行中的任务,不能视为完整 CI 通过。真实 WebView2、WSL/macOS 对照及安装包运行时哈希验收仍待完成;缺少 UI 截图,需按上述建议补充。

@mirakyux

Copy link
Copy Markdown
Contributor Author

@lithe review

@lithe-code-review

lithe-code-review Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⚠️ 有建议
范围: preview ← codex/fix-project-tree-drag-session · head: 7165900

未发现需要阻塞合并的问题。

Scope Check

  • 关联需求:Issue [Feature] win的项目/提交向mac对齐 #1257 的 Windows 项目树、提交面板和 Shelf/Stash 对齐需求已覆盖;通用插件依赖、归档和虚拟文档浏览仍按 PR 声明保持未实现。
  • 已完成范围:项目树定位、选择与批量移动,会话过期保护,Commit/Shelf/Stash 工作流,本地历史隔离,Maven Dependencies 快照,以及对应共享契约、功能矩阵和 Agent Note 同步。
  • 前一轮审查中的两项 P2 问题已有对应修复线索:Shelf 未跟踪层改用以真实索引为基线的 source: "unstaged" 导出,并增加暂存删除/重命名回归;Dependencies 导航复用包含 reactor 相对目录的 POM 路径解析,并增加嵌套项目测试。
  • 能力复用:改动继续复用 Rust Core 的补丁交换能力和现有 Maven owner,未发现新增第二套项目模型或编译语义实现的高置信证据。
  • 本次没有可列为 Finding 的 P0/P1/P2 问题;未将未被本次改动恶化的历史问题纳入审查。

Suggestions

  1. PR 描述或评论补充受影响界面的截图,至少覆盖 Project/Dependencies、Commit/Shelf 的前后对比、明暗主题和窄面板状态,并展示长文件名横向滚动、拖动和定位交互。当前上下文没有截图,视觉效果尚未验证。
  2. CI 完成后补充最终检查结果,尤其是 Windows 前端与原生测试、Rust Core、macOS 对照以及 scripts/verify-runtime-bundle-immutability.sh 的实际执行结果;当前只提供了作者报告的本地测试和等价静态扫描,审查未独立运行这些检查。

Summary

本次 head 已针对前一轮 Shelf 补丁基线和 Maven 嵌套路径问题补充实现与回归覆盖,项目树会话保护、Git 操作互斥和应用数据存储边界也有相应契约与测试说明。静态审查未发现需要阻塞合并的正确性或安全问题;视觉验收、跨平台实机行为和完整 CI 结果仍需补齐。

Verification

  • 审查范围固定为 7121aee6df79037d1ddffd9d4aee1a3aa0b3f67d...7165900467c488ee99d6989b3bc4804443e8e3d5,按给定语义文件清单检查项目树拖动/选择、Shelf/Stash 捕获与恢复、Core 补丁交换、Maven 导航、原生持久化、共享契约、测试和 CI 注册。
  • 作者提供的验证结果包括 Windows Release 构建通过、329 项测试通过且 1 项跳过、定向 TypeScript lint/Rust 格式/稳定性门禁和契约检查通过;这些结果未由本次审查独立运行。
  • PR 说明的既有项目树专项测试来自 78c7521d,本轮未重复运行;head 的若干 GitHub Check Run 在提供的上下文中仍处于 queued 或 in_progress,不能视为完整 CI 已通过。
  • 本次未启动应用、未运行测试或验证脚本、未访问网络;真实 WebView2、WSL/macOS 对照和安装包运行时哈希验收仍属于未验证范围。

@mirakyux

Copy link
Copy Markdown
Contributor Author

@lithe review

@lithe-code-review

Copy link
Copy Markdown

Lithe Review

⏳ 正在以大型变更模式审查代码,完成或失败后会更新本评论。

@mirakyux

Copy link
Copy Markdown
Contributor Author

@lithe review

@lithe-code-review

lithe-code-review Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⚠️ 有建议
范围: preview ← codex/fix-project-tree-drag-session · head: 594df568

未发现需要阻塞合并的问题。

Findings

  1. [P2] 单选跟随应监听编辑器目标,而非项目树的 activePath
    windows/tauri/src/features/file-explorer/components/file-explorer-tree.tsx:564

    • 实际影响:编辑器已有文件打开时,单击目录名称后,选择和键盘焦点会被改回编辑器文件;随后复制、剪切操作针对文件,而非刚点击的目录。
    • 触发条件:通过实际 FileExplorerPane 使用项目树,编辑器活动文件保持不变,单击另一个目录且没有明确多选。
    • 复现步骤:打开并保持可见的 README.md,单击 src 目录名称,再按 Ctrl+C。目录点击在第 1094 行调用 updateActivePath(src);既有 useFileExplorerSync.ts:33 的 Effect 随后将 sidebar 的 activePath 恢复为编辑器的 README.md。新增 Effect 把这次变化误认为编辑器切换,将选择、锚点及焦点全部改成 README.md,因此复制目标也随之改变。
    • 来源:本次引入。base 在树拥有焦点时保留独立的 focusedPath;新增逻辑无条件跟随共享 activePath。现有“编辑器路径未变时保留目录选择”测试没有传入 updateActivePath,未覆盖实际 store 的往返同步。

Scope Check

  • 关联需求:[Feature] win的项目/提交向mac对齐 #1257 的长名称滚动、项目树多选和提交面板主要工作流已有实现;Finding 1 表明目录单选仍存在回归,完整交互对齐尚未完成验收。
  • 已完成范围:定位与拖动会话保护、Commit/Shelf/Stash、持久化恢复、Maven Dependencies 快照及相关契约、矩阵和 Notes。
  • 前轮两项 P2 已有对应修复:Shelf 使用索引基线导出完整工作区层,并覆盖暂存删除/重命名后的路径重建;Dependencies 导航包含 reactor 相对目录。
  • 未完成范围:通用插件依赖、归档与虚拟文档浏览,以及 PR 已声明的真实界面验收。
  • 能力复用:继续使用 Git/Core 补丁能力和既有 Maven owner,未发现新增第二套项目模型的高置信证据。
  • Finding 1 属于本次新增选择同步逻辑的直接影响,未要求修复无关历史问题。

Suggestions

  1. 对应 Finding 1:以真实编辑器目标路径的变化触发单选跟随,区分树选择与编辑器目标。补充接入真实 sidebar store 和活动 buffer 的组件回归,验证单击目录后选择、复制/剪切目标和范围锚点保持目录,直到编辑器实际切换文件。
  2. 补充 Project/Dependencies、Commit/Shelf 的界面截图;已有界面提供前后对比,覆盖明暗主题和窄面板。当前上下文未提供截图,视觉效果尚未验证。
  3. 补充当前 head 的最终插件及 macOS CI 结果,明确区分前序提交通过、当前跳过和仍在运行的检查。

Summary

恢复数据持久化、不可变 Stash 身份和操作生命周期保护有相应实现与回归覆盖,两处 Swift 修改也仅调整夹具。项目树新增选择模型仍与既有活动路径同步产生冲突,建议修正;未发现明确 P0/P1 问题。

Verification

  • 静态审查固定 7121aee6...594df568,从完整文件清单进入,重点追踪项目树选择/定位/移动、Shelf 捕获与重试、Stash OID、Core 补丁、原生存储、Maven 导航、本地历史及测试与 CI 注册。资源、二进制、生成文件和锁文件未读取正文。
  • 作者报告 Windows Release 构建、329 项测试通过且 1 项跳过;另有较早提交的项目树专项结果。这些结果未由本次审查独立验证。
  • 作者提供的 71659004 CI 包括发行目录只读检查通过;0b427564 的 Swift 测试通过,插件检查为 196 项通过、1 项失败。当前 594df568 修正该失败夹具,但提供的插件 Check Run 仍为 in_progress,不能视为已通过。
  • 当前 head 已观察到 Notes、功能矩阵和 Windows gate 成功;Windows 前端、Windows Rust、真实 JDT 测试为 skipped,不计作重新执行通过。
  • 本次未运行代码、测试、应用或验证脚本,包括 scripts/verify-runtime-bundle-immutability.sh;未修改文件或访问网络。Finding 的复现路径来自静态调用链分析。真实 WebView2、WSL/macOS 对照、UI 截图及安装包运行时哈希验收仍待补齐。

@mirakyux

Copy link
Copy Markdown
Contributor Author

@lithe review

Head: 84008cee93847d2a6d1e5d94b27d8c816b580962

已修复评论 6253345907 的 P2:单选跟随真实编辑器目标,共享 sidebar 路径往返不再覆盖目录选择。回归挂载真实 FileExplorerPane 并连接 sidebar/buffer store,核对目录高亮、复制/剪切、键盘焦点、范围锚点及后续真实文件切换。

本地 Release 构建通过,Project 树 40 项及 Windows 原生 350 项通过、1 项原有跳过;Notes、矩阵和静态门禁通过。前一 head 594df56 的 Swift/插件 CI 已实际通过,新 head 的 CI 等待运行;真实 WebView2/macOS 视觉及截图仍 pending。PR 描述已更新详细验证边界。

@mirakyux

Copy link
Copy Markdown
Contributor Author

@lithe review

@lithe-code-review

lithe-code-review Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⚠️ 有建议
范围: preview ← codex/fix-project-tree-drag-session · head: 84008cee

未发现需要阻塞合并的问题。

Scope Check

  • 关联需求:已找到并核对 Issue [Feature] win的项目/提交向mac对齐 #1257;项目树长文件名滚动、定位、多选与批量移动,以及提交面板和 Shelf/Stash 对齐均属于本次范围。
  • 已完成范围:项目树选择、键盘焦点、定位和拖动会话保护;Commit/Shelf/Stash 工作流;Maven Dependencies 快照与嵌套 reactor 导航;本地历史隔离;相关 Rust Core、共享契约、功能矩阵、Agent Notes 和 Windows CI 注册。
  • 未完成范围:通用插件依赖、归档和虚拟文档浏览仍按 PR 声明保持未实现;真实 WebView2、WSL/macOS 对照和完整视觉验收仍待实机验证。
  • 能力复用:改动继续复用 Rust Core 的 Patch exchange、现有 Maven owner 和既有 Git 工作流,未发现新增第二套项目模型或编译语义实现的高置信证据。
  • 前序审查指出的 Shelf 补丁基线、Maven 嵌套路径和项目树单选同步问题,在当前上下文中均有对应修复说明与回归覆盖;未发现新的 P0/P1/P2 问题。
  • 现有建议均属于本次前端界面和跨平台验收职责范围,不涉及未被本次改动恶化的历史问题。

Suggestions

  1. 在 PR 描述或评论中补充受影响界面的截图,至少覆盖 Project/Dependencies、Commit/Shelf 的前后对比、明暗主题和窄面板,并展示长文件名横向滚动、拖动和定位交互。当前上下文没有截图,视觉效果尚未验证。
  2. CI 完成后补充当前 84008cee 的最终结果,明确区分本次 head 实际执行、跳过和沿用前序提交的 Windows Rust、真实 JDT、Rust Core、macOS、Plugin 与 Release 检查;同时附上 scripts/verify-runtime-bundle-immutability.sh 的实际执行结果或 CI 产物。

Summary

静态审查未发现需要阻塞合并的正确性、安全性、资源清理或架构边界问题。当前实现延续了既有 Core、Git 和 Maven owner,会话隔离、操作互斥及部分失败恢复也有相应契约和测试说明。剩余主要是视觉、真实平台行为和当前 head 完整 CI 的验收缺口。

Verification

  • 审查范围固定为 7121aee6df79037d1ddffd9d4aee1a3aa0b3f67d...84008cee93847d2a6d1e5d94b27d8c816b580962,从给定的完整语义文件清单进入,追踪了项目树选择/定位/拖动、Shelf/Stash 捕获与恢复、Patch exchange、Maven 导航、本地历史、原生持久化、共享契约、Agent Notes、功能矩阵及 CI 注册。
  • 作者提供的验证包括 Windows Release 构建、项目树专项 40 项通过、Windows 原生 350 项通过且 1 项原有跳过,以及前序提交中的 Shelf/Maven/Core 回归;这些结果未由本次审查独立运行。
  • 当前 head 已提供 Windows frontend、Windows/macOS/Database CI gate、Notes 和功能矩阵成功状态;Windows Rust、真实 JDT、Rust Core、Plugin 和 Release 等若干 lane 按范围跳过,不能视为当前 head 的完整重跑通过。
  • 本次未启动应用、未运行代码、测试或验证脚本,未访问网络;因此未独立验证 scripts/verify-runtime-bundle-immutability.sh、真实 WebView2、WSL/macOS 对照或安装包运行时哈希。
  • 仍存在的非阻塞风险是视觉效果、窄面板和拖动交互,以及完整跨平台 CI 结果尚未在当前 head 上全部确认。

@mirakyux

Copy link
Copy Markdown
Contributor Author

已核对当前 84008cee 的两条 Suggestions。

  1. CI 证据已处理:PR 描述已补每个 lane 的当前执行/跳过状态和可点击 job 链接,明确区分前序 head;同时附上 71659004 macOS arm64 打包 job 中原版 verify-runtime-bundle-immutability.sh 的成功时间和原始日志来源。当前 head 的 Release lane 跳过,没有将前序成功表述为重新执行通过。
  2. 视觉证据仍待完成:已准备独立 Git fixture,尝试原生应用验收时发现 Windows 桌面锁屏,已停止 UI 操作,没有取得可用截图。本轮应用及 Java 子进程均已关闭。明暗主题、窄面板、长文件名滚动、拖动/定位,以及 macOS 前后对照继续标记 pending,解锁后再补 Windows 部分。

本轮没有代码改动、push 或重跑 CI;bot 对当前 head 已完成审查且没有 P0/P1/P2 finding,因此不重复请求同一 head 的评审。继续每 30 分钟监控新的意见及必需正式评审,REVIEW_REQUIRED 满足前不合并。

@Mucheen
Mucheen merged commit 5ad533c into 1lck:preview Oct 19, 2026
18 checks passed
kuun993 added a commit to kuun993/Lithe-IDEA that referenced this pull request Oct 19, 2026
rebase 到 preview(含 1lck#1341 多选拖拽重写)后的适配:
- 终端分支改用 dragState.draggedItems,多选时插入全部路径;
- dispatchTerminalPathInsert 现在接收原始路径数组(由终端按自身 shell
  引用),不再在此处预先格式化;
- 移除未使用的 formatPathsForTerminal 导入。
tsc --noEmit、70 项聚焦测试、lint 通过。
@mirakyux
mirakyux deleted the codex/fix-project-tree-drag-session branch October 19, 2026 01:37
1lck pushed a commit that referenced this pull request Oct 19, 2026
* feat(windows): 项目树复制或拖拽文件后可在终端插入路径

Windows 项目树的「复制」只写入应用内文件剪贴板(Rust 侧 state),
外部程序无法读取,因此复制一个文件后在集成终端按 Ctrl+V 不会得到
任何内容;把项目树条目拖到终端时,落点会命中编辑器 pane 分支,
结果是在编辑器里打开文件(拖目录则没有任何反应)。

本次改动把文件条目转换成终端可直接使用的路径,且不改变原有复制语义:

- 复制行为不变:copy/cut 仍只写应用内剪贴板,系统剪贴板不被覆写。
  store 额外只读一次剪贴板文本快照,用于判断「文件复制」是否仍是
  用户最近一次操作。
- 终端粘贴:Ctrl+V / Ctrl+Shift+V / 右键「粘贴」优先插入复制条目的
  路径;若此后用户又复制了文本,则照旧粘贴该文本,避免插入过期路径。
- 终端拖拽:项目树条目拖到终端时插入路径并追加一个空格,且不再
  打开编辑器标签;拖到非终端 pane 仍按原逻辑打开文件。
- 路径规则:工作区内用相对路径并把分隔符统一为 `/`(Windows shell
  从工作区根目录启动且不发送 OSC 7),工作区外条目保持完整路径;
  含空格的路径沿用终端拖放的引号规则,保证保持为一个 shell 参数。

复用终端既有的引号函数 quoteTerminalPaths,避免两套转义规则漂移。
资源管理器的外部拖拽路径解析(要求绝对路径)不受影响。

验证:
- 新增 23 个单元测试(路径格式化、引号、落点判定)全部通过。
- 完整前端套件按基线/含改动两轮对比,失败集合完全一致(各 221),
  未引入新的失败。
- tsc --noEmit、lint、verify-test-stability、verify-windows-boundaries、
  verify-platform-feature-matrix 均通过。
- 真实 Windows 交互验收尚未执行,矩阵中 verificationStatus 标为 pending。

不在本次范围:系统剪贴板写入文件格式(CF_HDROP),因此桌面/资源管理器
粘贴文件仍不支持;该能力需要 Rust 侧原生剪贴板实现。

* fix(windows): 修正终端路径插入的注入风险、相对基准与 CI 覆盖

回应 review 的三项 findings。

[P1] 相对路径缺少 shell 元字符转义
quoteTerminalPath 原先只在路径含空白、引号或反斜杠时才加引号。绝对
Windows 路径必含反斜杠,因此一直命中引用分支;本次新增入口把路径变成
不含反斜杠的工作区相对路径后,`README&whoami` 这类文件名会原样送到
cmd,`&` 被当作命令分隔符执行。

改为安全字符白名单:只有字母、数字和 `._/\:-` 不引用,其余一律加引号,
覆盖 cmd 的 & | < > ^ ( ) 和 PowerShell 的 ; $ ` { } 等。
顺带修正引号内的转义策略:反斜杠在 cmd / PowerShell 的引号内不是转义符,
不再改成 `\`(旧写法会破坏路径);只把内嵌的 `"` 双写。未加引号的安全
路径现在原样返回,不再被无条件加反斜杠。

[P2] 路径应按目标终端目录相对化,而非工作区根
terminal 已有 currentDirectory(含 OSC 7 更新)。改为由终端在插入时按
自身目录相对化:抓取和拖放都只在事件里传绝对路径,终端用
getSession(sessionId).currentDirectory 决定基准;目录未知时保留完整路径,
因为此时相对路径可能指向错误文件。这修掉了在子目录中打开终端后
`src/Main.java` 被解析成 `.../src/src/Main.java` 的问题。

[P2] 新增测试接入 CI
在 ci-windows.yml 增加 "Test explorer file paths inserted into the
terminal" 步骤,用既有有界计时入口运行三个新测试文件,产出 JUnit/HTML
报告。本地已按 CI 写法实跑,36 项通过。

未采纳的 finding:
- [P2] 用文本相等判断剪贴板先后。准确修复需要 Windows
  GetClipboardSequenceNumber,Tauri clipboard-manager 只暴露
  read/writeText|Html|Image,没有变更序号或事件 API,必须新增原生 FFI。
  这与本 PR 明确排除的 CF_HDROP 属同一前置依赖,留作后续独立改动。
  当前启发式已在代码注释说明其局限。

验证:
- 新增/更新 36 个单元测试全部通过,含 10 个 shell 元字符注入回归用例
  和子目录相对化用例。
- 完整前端套件按基线/修复后两轮对比,失败集合完全一致(各 221),
  未引入新的失败。
- tsc --noEmit、lint、verify-test-stability、verify-windows-boundaries、
  verify-platform-feature-matrix 均通过。
- 外部拖放(OS 拖入绝对路径)行为经手工探测确认无回归。

* fix(windows): 按目标 shell 引用终端路径并改用绝对路径

回应第二轮 review。两个 P1/P2 经实测确认成立:

[P1] 双引号在 PowerShell / Git Bash 中仍会求值
PowerShell 实测:文件名 report$(hostname).txt 用双引号包裹后,解释器
把它展开成 reportchenbk7995.txt 而报找不到文件。Git Bash 同理。
上一版还把旧代码对 $ 和反引号的反斜杠转义删掉了,使既有外部拖放
在 Git Bash 下也发生回归。

改为按 shell 家族选择引用方式:terminalShellFamily 从 session.shell
识别 cmd / powershell / pwsh / nu / bash,然后
- PowerShell、Nushell:单引号,内嵌单引号双写;
- Git Bash 等 POSIX:单引号,内嵌单引号用 '\'' 闭合;
- cmd.exe:只能双引号(无字面量语法),但保留 %NAME% 可展开的已知局限。
单引号同时保住反斜杠,修掉了 Git Bash 把 C:\work\proj\a.txt 解析成
C:workproja.txt 的回归。实测单引号可正确读到字面文件。

[P2] 启动目录不能当作可靠的当前目录
cmd 与 PowerShell 都不发送 OSC 7,session.currentDirectory 只是启动
目录,用户 cd 后即过期,?? null 识别不出「非空但过期」。

改为不再相对化:路径一律使用完整绝对路径。绝对路径在任何 cd 之后的
当前位置都成立,也不再需要可靠性判断。顺带修掉 [P2] 目录自身被过滤成
空串的问题——绝对路径不会退化为空。

[P2] 内容相等无法证明先后
Tauri clipboard-manager 没有变更序号 API,正确修复需 GetClipboardSequence
Number 原生 FFI,与本 PR 排除的 CF_HDROP 同一前置依赖。按建议补充
不依赖启发式的显式入口:终端右键菜单新增「粘贴已复制的文件路径」,
直接插入应用内文件剪贴板的路径,不受系统剪贴板内容影响。

验证:
- 32 个单元测试通过,含 PowerShell/Git Bash 的 $() 不求值、反斜杠保留、
  单引号转义、cmd 双引号、OS 拖放按 shell 引用等回归用例。
- 手工实测 PowerShell 双引号确实展开 $()、单引号不展开;Git Bash
  裸路径丢反斜杠、单引号保留。
- 完整前端套件按基线/修复后两轮对比,失败集合完全一致(各 221),
  未引入新的失败。
- CI 步骤按实际写法本地实跑,32 项通过并生成报告。
- tsc --noEmit、lint、verify-test-stability、verify-windows-boundaries、
  verify-platform-feature-matrix 均通过。

* fix(windows): 记录实际启动的 shell 并统一路径引用入口

[P1] shell 分类没有接通实际调用链
初始化时 updateSession 只写入 connectionId、currentDirectory 和
remoteConnectionId,没有保存 shell。而 TerminalSlot 的 shell prop 本身
就来自 terminal.shell,因此 getSession(sessionId)?.shell 恒为 undefined,
上一版按 shell 家族引用路径的修复全部回退到 cmd 双引号——PowerShell 与
Git Bash 下 report$(whoami).txt 这类文件名仍会被求值。

修复两处:
- updateSession 一并保存实际启动的 shell(沿用创建时同一来源
  shell || existingSession?.shell || wslShellId),使 remount、
  重新打开以及其它读取 session 的入口都能拿到;
- 组件内新增 resolveShell(),优先使用 shell prop(初始化完成后权威),
  回退到 session 记录,普通粘贴、显式粘贴、外部拖放和应用内拖放四个
  入口统一走它,不再各自读取。

新增 terminal-session-shell.test.ts,直接覆盖这条断点:shell 能经
updateSession 落到 session、与 connectionId/currentDirectory 共存、
后续省略 shell 的更新不会清除它、初始化前为空。该文件已加入 CI 步骤。

[P2] 普通粘贴的内容误判
未修复,属已知局限:正确做法需要 GetClipboardSequenceNumber 原生 FFI,
Tauri clipboard-manager 不提供变更序号。按上一轮评审建议,显式入口
「粘贴已复制的文件路径」已提供不依赖启发式的选择;普通 Ctrl+V 仍保留
内容比较并在代码注释中写明其局限。

验证:
- 36 个单元测试通过(4 个文件),含新增的 4 个 session shell 用例。
- CI 步骤按实际写法本地实跑,36 项通过并生成报告。
- 完整前端套件按基线/修复后两轮对比,失败集合完全一致(各 221),
  未引入新的失败。
- tsc --noEmit、lint、verify-test-stability、verify-windows-boundaries、
  verify-platform-feature-matrix 均通过。

* docs(windows): 记录终端粘贴优先级的取舍决策

[P2] 内容相等无法区分「重新复制了相同文本」。在没有 OS 变更序号的前提下
无法正确修复,因此把它作为明确决策固定下来,而不是留作开放问题:

保留普通 Ctrl+V 自动插入文件路径。理由是这条判断两侧代价不对称——
插入路径正是复制文件的目的,误判需要「相同内容复制两次且中间夹一次文件
复制」才发生,代价只是一次可见、可撤销的插入;而移除自动分支会给每次
「复制文件→终端粘贴」都增加一步手动操作,且并非本 PR 能正确实现(需要
GetClipboardSequenceNumber 原生 FFI)。

- 函数注释改为记录决策依据与两侧代价,不再只描述局限;
- 新增一个测试固定该取舍,使未来改动必须同步修改注释中的决策。

仅注释与测试变更,无生产逻辑改动。37 个单元测试与 CI 聚焦步骤通过,
tsc 与 lint 通过。

* fix(windows): 覆盖 WSL、MSYS2 与 Cygwin 的 shell 家族

[P1] 原生 shell 目录还提供 msys2、cygwin 和 wsl:<发行版>,三者都运行
bash,但 terminalShellFamily 未覆盖,会回退为 cmd 并生成双引号——
这些 shell 下 report$(whoami).txt 仍会被求值。base 的外部拖放原本会
转义 $ 和反引号,本次替换使该入口同样回归。

补齐家族映射:wsl:* 前缀、msys2、cygwin 归为 posix。并在注释中写明
映射必须与 windows_shells.rs 保持对齐,以及「落到 cmd 就意味着双引号」
这一失效路径。

新增覆盖:4 个真实 shell id 的分类用例,以及从 shell id 到最终插入文本
的端到端用例(wsl:Ubuntu / msys2 / cygwin / bash / powershell / pwsh
六种均产出单引号字面路径)。

验证:44 个单元测试通过(4 文件);CI 聚焦命令本地实跑 44 项通过并
生成报告;完整前端套件基线/修复后两轮对比,失败集合完全一致(各 221),
新增 0;tsc --noEmit 与 lint 通过。

* fix(windows): WSL 会话转换路径命名空间并补充弯引号覆盖

[P2] WSL 会话路径未转换命名空间 — 成立,已修复
分类为 posix 只解决了引用问题,原样保留的 Windows 盘符路径在 WSL 内仍
不可访问:Linux 把 `C:\work\proj\a.txt` 当作含反斜杠的相对文件名。
新增 windowsPathToWslPath,把 `C:\work\proj\a.txt` 转成
`/mnt/c/work/proj/a.txt`(WSL 默认按盘符挂载);已是 Linux 形式的路径
原样保留;UNC 共享没有可预测挂载点,返回 null 并保留原值,避免插入
指向不到的位置。quoteTerminalPaths 在 wsl: 会话下先转换再引用。

[P1] 弯单引号可逃逸 — 复核后不成立,未改代码
按 finding 逐字节验证 PowerShell 5.1:把 U+2018、U+2019、U+201A、
U+201B、U+FF07、U+2032 分别置于单引号字符串内并尝试注入
`$(Write-Host PWNED)`,六种全部未执行,脚本正常解析。说明这些字符
不是 PowerShell 的字符串分隔符,只有 ASCII `'` 需要双写(已实现)。
为免该假设日后静默失效,新增 6 个用例固定「弯引号保持字面值」。

[P2] 相同文本误判 — 维持决策,修正注释
保留自动插入的取舍不变。原注释称「再次粘贴即可撤销」,经复核不成立
(剪贴板文本与应用内文件剪贴板都仍然有效),已改为准确描述恢复方式。

验证:
- 57 个单元测试通过(5 个文件),含 WSL 路径转换 6 例、弯引号 6 例、
  wsl: 端到端 2 例;CI 聚焦命令本地实跑 57 项通过并生成报告。
- 完整前端套件基线/修复后两轮对比,失败集合完全一致(各 221),新增 0。
- tsc --noEmit、lint 0/0 通过。

* fix(windows): WSL 工作区的协议路径转换为发行版内路径

[P2] wsl:// 协议路径未转换 — 成立,已修复
上一轮只把 wsl:* 归为 POSIX,并处理了 Windows 盘符路径;但 explorer 的
WSL 工作区根是用 buildWslPath 生成的 `wsl://Ubuntu/home/me/proj`,该
形态既不是盘符路径也不是 Linux 路径,windowsPathToWslPath 返回 null 后
回退原值,Linux 下无法访问。

新增 pathToWslPath(path, distro):命中 wsl:// 时用 parseWslPath 取出
linuxPath,并校验发行版与目标终端一致(跨发行版不可达,返回 null 让
调用方保留原值而不是插入猜测);否则交给 windowsPathToWslPath 处理
盘符路径与已是 Linux 的路径。quoteTerminalPaths 在 wsl: 会话下改用它。

[P1] 弯单引号 — 仍不成立,但改用更强证据并修正了判定方法
上一轮我用 grep 匹配输出中的 PWNED,这个判定有缺陷:字符串内容本身也会
被打印出来,造成假阳性/假阴性。改用 PowerShell 自身的词法分析器
(System.Management.Automation.Language.Parser)直接查看切分结果:
含 U+2019 的路径被解析为**单个 StringLiteral**,errors=0;同样形状换成
ASCII `'` 则切分为 StringLiteral + 独立语句 + 注释。结论不变:弯引号不是
PowerShell 的字符串分隔符,只有 ASCII `'` 需双写。测试注释已改为记录该
词法证据及其判定方法。

验证:64 个单元测试通过(5 个文件),含 pathToWslPath 6 例;CI 聚焦命令
本地实跑 64 项通过;完整前端套件基线/修复后两轮对比,失败集合完全一致
(各 221),新增 0;tsc 与 lint 通过。

* fix(windows): 双写 PowerShell 认可的四种弯单引号

[P1] 弯单引号可逃逸字符串 — **成立**。此前两轮我判定它不成立,结论是错的。

用无歧义的副作用检测(注入语句创建文件,只检查文件是否出现)在
PowerShell 5.1 上复验:把注入串放在单引号字符串内执行,

  'report<Q>; New-Item ... 'pwned_XXXX.txt' ...; #.txt'

U+2018、U+2019、U+201A、U+201B 四种全部**创建了文件**,与 ASCII `'`
一致;U+FF07、U+2032 未创建。即 PowerShell 收四种弯引号为字符串分隔符,
只双写 ASCII `'` 确实留有命令注入入口。

我此前两次误判的原因,如实记录:第一次用 `grep PWNED` 匹配程序输出,
而字符串内容本身也会被打印,判定不可靠;第二次构造注入串时多插了一个
ASCII 引号,导致引号配对被破坏、结果全部显示为「未执行」。

修复:PowerShell 分支对 `'` 与 U+2018/U+2019/U+201A/U+201B 一并双写。
已用应用真实输出做端到端复验——生成的
`'C:\work\report’’; New-Item ...; #.txt'` 在 PowerShell 中执行后没有
产生文件,且良性文件名往返值保持不变。POSIX 分支不受影响(这些字符在
bash 中不是分隔符,双写反而会破坏路径)。

测试改为按「分隔符 / 非分隔符」两组断言,并注明判据来自真实执行而非
仅比较输出字符串。

验证:65 个单元测试通过(5 文件);CI 聚焦命令本地实跑 65 项通过;
完整前端套件基线/修复后两轮对比,失败集合完全一致(各 221),新增 0;
tsc 与 lint 通过。

* fix(windows): 按 Nushell 语法引用路径并记录 WSL 挂载限制

阻塞已解除(上轮结论为「有建议」)。本轮处理剩余 P2:

[P2] Nushell 不应沿用 PowerShell 的单引号双写 — 成立,已修复
Nushell 的单引号字符串没有 `'` 转义,双写不会还原为一个引号,
`C:\work\it's.txt` 插入后无法访问。改用 Nushell 的 raw string
(反引号),它保留引号、`$` 与 Windows 反斜杠:
- 常规路径与含 `$()`、单引号的路径统一用 raw string;
- 文件名含反引号时 raw string 无法表达,退化到单引号(仅当不含单引号);
- 两者同时出现时没有能保持字面的 Nushell 形式,按原样输出而不是
  生成 Nushell 会读成别的值的转义。
已补 4 个用例覆盖上述分支。

[P2] WSL 自定义 automount root — 不修复,记录限制
`/etc/wsl.conf` 的 `[automount] root` 可改变盘符挂载前缀,正确解析需要
目标发行版的 `wslpath`,前端没有该能力。已在 windowsPathToWslPath 的
注释和功能矩阵验证说明中明确记录这一前提,避免被当作通用事实。

[P2] 相同文本误判 — 非阻塞,决策不变。

验证:69 个单元测试通过(5 文件);CI 聚焦命令本地实跑 69 项通过;
完整前端套件基线/修复后两轮对比,失败集合完全一致(各 221),新增 0;
tsc --noEmit、lint 0/0、verify-platform-feature-matrix 通过。

* fix(windows): Nushell 无法字面表示时丢弃路径而非输出裸文本

[P1] Nushell 回退分支重新引入注入入口 — 成立,是我上一轮引入的。

上一轮为处理「文件名同时含反引号与单引号」,让 quotePathForShell 直接
返回原路径。该分支取消了整条路径的引用:文件名中的 `;` 会被 Nushell
当作命令分隔符,`print C:\work\a; ^whoami; #'`.txt` 会执行 `^whoami`。

修复:quotePathForShell 改为返回 string | null,无法字面表示时返回
null;quoteTerminalPaths 过滤掉 null。即**拒绝插入**而不是输出裸路径。
本次改动前该场景输出裸文本(不安全),改动后不插入任何内容——宁可
不插入,也不能执行文件名里的命令。

同时把上一轮固定「原样输出」的测试改为断言输出为空,并注明原因。

验证:
- 实测 无法表示 -> ""(不再输出裸路径);仅含反引号 -> 单引号形式。
- 37 个终端引用用例通过;CI 聚焦命令 69 项通过。
- 完整前端套件基线/修复后两轮对比,失败集合完全一致(各 221),新增 0。
- tsc --noEmit、lint 0/0 通过。

* fix(windows): 拖放路径被全部丢弃时不再插入孤立空格;显式粘贴项补 disabled

自查发现两处不一致:

1. formatDroppedPathsForTerminal 在引用阶段丢弃全部路径时仍会追加
   尾部空格,向提示符插入一个孤立空格。formatPathsForTerminal 已有
   该保护,两者行为不一致。补同样的空值判断,并加回归用例
   (Nushell 下文件名同时含反引号与单引号时,quote 返回 null)。

2. 终端右键「粘贴已复制的文件路径」缺少 disabled={!connectionId},
   与上方「粘贴」不一致,未连接时仍可点击。补齐。

验证:70 个单元测试通过(5 文件);完整前端套件基线/修复后两轮对比,
失败集合完全一致(各 229),新增 0。注意基线从 221 升至 229 是上游
41 个提交自带的失败,与本改动无关,已用同一轮基线验证。
tsc --noEmit、lint 0/0、verify-test-stability、verify-windows-boundaries 通过。

* fix(windows): 用剪贴板变更序号替代文本比较判断粘贴优先级

[P2] 相同文本会让旧文件路径覆盖最近复制的文本 — 已从「记录取舍」改为
真正修复。

原实现比较系统剪贴板的**文本内容**来判断「文件复制是否仍是最近一次
操作」。该判据在「复制文本 → 复制文件 → 再复制同一段文本」时失效:
内容相同,函数仍选文件路径,且重复复制该文本也不会恢复。

Windows 提供 GetClipboardSequenceNumber(),每次任何进程写剪贴板都会
递增,是内容比较无法替代的变更身份。新增 tauri 命令
clipboard_sequence_number(cfg(windows) 下调用该 API,其它平台返回 0):
- copy/cut 时读取序号存为 sourceClipboardSequence;
- 粘贴时再读一次,序号相同才插入文件路径,不同则说明用户此后复制过
  其它内容,照旧粘贴剪贴板文本;
- 任一侧读不到序号时回退为粘贴文本,不猜测。

新增 windows-sys 的 Win32_System_DataExchange feature。该 feature
不需要更新 Cargo.lock:src-tauri 的 lock 目前与上游 #1267 引入的
lithe-agent-host 依赖不同步(缺 tungstenite 等),cargo check 会自动
补写;那属于上游 lock 维护问题,不在本 PR 范围内,故未提交 lock 变更。

顺带更新功能矩阵:补充 Rust 侧证据路径,并把验证说明改为覆盖「复制相同
文本」这一纯文本比较无法区分的场景。

验证:
- 70 个单元测试通过(5 文件),新增 4 个序号语义用例(含回归:
  相同文本二次复制现在正确让位)。
- 实测 GetClipboardSequenceNumber 连续读取稳定(16738/16738),只读、
  未扰动剪贴板。
- cargo check --target x86_64-pc-windows-msvc 通过。
- 完整前端套件基线/修复后两轮对比,失败集合完全一致(各 229),新增 0。
- tsc --noEmit、lint 0/0、verify-platform-feature-matrix(172 项)通过。

* fix(windows): 适配上游多选拖拽接口并复用终端路径通道

rebase 到 preview(含 #1341 多选拖拽重写)后的适配:
- 终端分支改用 dragState.draggedItems,多选时插入全部路径;
- dispatchTerminalPathInsert 现在接收原始路径数组(由终端按自身 shell
  引用),不再在此处预先格式化;
- 移除未使用的 formatPathsForTerminal 导入。
tsc --noEmit、70 项聚焦测试、lint 通过。

* fix(windows): 剪贴板变更序号随文件条目一同广播

[P2] 跨窗口时条目与序号不同步 — 成立,是我上一轮引入的。

上一轮把序号存在前端 store 的独立字段里,而跨窗口同步只广播条目
(`file-clipboard-changed` 事件与 `clipboard_get` 都只含条目),
于是 `setClipboard` 保留了**接收窗口自己的旧序号**。后果:
- 窗口 B 从未复制过文件时序号为 null,粘贴永远回退到系统剪贴板文本;
- B 复制过文件时,新条目会与旧操作的序号配对,判断依据错位。

修复:把序号移入 `FileClipboardState` 本身。
- 后端 `clipboard_set` 用 `clipboard_change_counter()` 取得序号并写入
  state,随 `file-clipboard-changed` 一起广播;`clipboard_set` 改为返回
  该 state,前端直接使用,不再单独读取;
- 前端删除独立的 `sourceClipboardSequence` 字段,`setClipboard` 整体
  替换 state,条目与序号始终成对;
- 粘贴时从 `clipboard.source_sequence` 读取基准序号。

新增回归用例:同一组条目分别用「随条目而来的序号」与「本窗口旧序号」
判定,前者插入路径、后者让位,固定住这个 bug 的形态。

验证:71 个单元测试通过(5 文件);tsc --noEmit、cargo check
(x86_64-pc-windows-msvc)、lint、verify-test-stability、
verify-windows-boundaries 通过;完整前端套件基线/修复后两轮对比,
失败集合完全一致(各 317),新增 0。

* fix(windows): clipboard_set 返回记录状态;修正 Nushell 引用并让复制容错

两个 P1 均为我方缺陷。

[P1] clipboard_set 仍返回 Result<(), String>,前端按 FileClipboardState 接收
成功时序列化为 null,`set({ clipboard: state })` 会清空当前窗口条目,
导致复制后立即粘贴看不到文件。CI 的 "Test Project tree selection" 门禁
正是这样红的(5 个用例失败,报 clipboard.entries 为 undefined)。
- host.rs: clipboard_set 改为返回记录下来的 FileClipboardState,并在
  emit 时用引用,避免返回值与广播内容分叉;
- store: 先本地记录条目,再尽力采纳后端返回的 state。后端调用失败或返回
  意外值都不会清空用户刚复制的条目——终端是从这个 store 读条目的。

[P1] Nushell 分支把反引号当作 raw string 定界符
Nushell 的反引号是 quoted identifier,不是字符串字面量,用它包裹路径
会改变含义而非保护路径。改为只用单引号;单引号内无法转义,故文件名含
`'` 时返回 null(丢弃),既不改写路径也不输出裸文本。

验证(均为本地实跑):
- CI 门禁 "Test Project tree selection, reveal, and move lifecycle":
  修复前 5 失败 → 修复后 40 项全通过;
- CI 门禁 "Test explorer file paths inserted into the terminal":70 项通过;
- 71 个单元测试、tsc --noEmit、cargo check(x86_64-pc-windows-msvc)、
  lint 0/0 通过。

已知无关失败:完整套件中 file-explorer-selection 的 5 个用例仍失败,
根因是 editor 目录的 svg-editor.test.tsx 用 mock.module 把 buffer.store
替换成无 setState 的裸对象,跨文件污染了后续测试。该测试文件与上游
preview 完全一致,属上游既有缺陷,不在本 PR 范围内。

* fix(windows): 远程工作区条目转换为连接内路径

[P2] remote:// 协议路径被原样交给远程 shell — 成立。
远程工作区在项目树里用前端 URI `remote://<connection>/<path>` 表示路径,
原样插入后远程 shell 得到一个打不开的字符串。

改为在插入前按目标终端解析:
- 命中 `remote://` 时用已有的 parseRemotePath 取出 remotePath;
- **只有当终端连接的就是该 connection 时才转换**,否则返回 null 丢弃——
  把 URI 交给别的 shell 等于插入一个看起来像路径、实际打不开的值,
  丢弃比这更安全;
- 转换逻辑收敛到 shellPath(),与 WSL 转换并列,避免两条路径各写一份。

terminal.tsx 新增 resolveRemoteConnectionId()(prop 优先,回退 session),
粘贴、显式粘贴、应用内拖放、外部拖放四个入口统一使用。

验证:73 个单元测试通过(新增 3 个远程路径用例:同连接去协议、
跨连接丢弃、本地不受影响);两套 CI 门禁本地实跑 40 项与 73 项全通过;
tsc --noEmit、lint 0/0 通过。

* fix(windows): 复制失败时前后端剪贴板保持一致

[P2] 复制失败时前后端分叉 — 成立,是我上一轮修 P1 时引入的新问题。

上一轮为了不让「后端返回意外值」清空条目,改成**先本地写入、再吞掉错误**。
代价是:后端拒绝 `clipboard_set`(源文件消失等)时,前端仍持有新条目,
而后端保留旧条目。于是终端显式粘贴得到 B、项目树粘贴却操作后端保留的 A;
若旧操作是剪切,项目树还会去移动旧文件。

改回以后端为准:`invoke` 成功才写入,失败时抛出、前端保持原状,前后端
一致。唯一的兜底是「返回体不含条目」——那只能来自未实现该命令的宿主或
测试替身(早期 `clipboard_set` 返回空),此时保留本次复制条目,避免把
剪贴板清空。

验证:两套 CI 门禁本地实跑 40 项与 73 项全通过;tsc --noEmit、lint 通过。

* fix(windows): 把剪贴板序号 0 当作未知而不是有效身份

[P2] 序号 0 被当作有效值 — 成立。

host.rs 在平台没有变更计数器时返回 0(`cfg(not(windows))` 分支与
GetClipboardSequenceNumber 的失败语义都用 0 表示未知),而判断函数只拒绝
null。于是两侧都是 0 时被判为「未变化」,粘贴仍插入旧文件路径,没有按设计
回退到文本——正是序号机制要解决的失效。

改为 `!sourceSequence || !currentSequence` 一律回退,并补回归用例
(0/0 与 0/42 均让位)。doc comment 同步说明 0 表示未知。

顺带按 review 建议更新 PR 描述中两处过时内容:剪贴板判断已从「文本快照」
改为变更序号;Nushell 已改为单引号并过滤含单引号路径(不再使用反引号
raw string)。并补充远程协议路径的转换规则。

验证:CI 门禁「explorer file paths…」74 项、门禁「Project tree selection…」
40 项本地实跑全通过;tsc --noEmit、lint 0/0 通过。
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.

2 participants