Skip to content

Issue 50

ShaoClean edited this page Sep 19, 2026 · 1 revision

Issue #50:[功能] 支持 Diff 中的图片预览

Issue #50 · PR #57 · 实现方案与验收记录

背景与问题

在查看包含图片文件变更的 Diff 时,图片只被标记为「二进制文件,无法显示文本差异」,无法直接查看图片内容,也无法比较修改前后的视觉差异。提交或审查 PNG、JPEG、GIF、WebP 等资源时,必须离开应用才能确认改动内容。

代码核对基线:development@6eaf6f0getDiffNoticeapps/web/src/components/diff-lines.ts)在补丁匹配 Binary filesGIT binary patch 时统一返回二进制提示,检查器不区分图片与其他二进制文件。

目标与范围

在 Diff 中识别常见图片文件并提供可直接查看的预览;修改过的图片可同时查看变更前与变更后版本。无法预览的格式继续使用明确的二进制文件提示。

本次包含

  • 新增图片显示变更后版本,删除图片显示变更前版本,修改图片并排显示「变更前 / 变更后」。
  • 显示文件名、格式、像素尺寸与文件大小。
  • 工作区改动与提交历史两个入口使用同一套预览。
  • 无法解码、超出大小限制、格式不受支持时显示明确提示,并回落到既有二进制文件展示方式。

本次不包含:像素级差异计算、图片标注编辑、复杂图像处理、叠加滑块对比模式。

方案与取舍

判定条件

只有「单文件补丁 + 纯二进制补丁 + 可渲染扩展名」三者同时成立才进入图片预览(getImageDiffKind)。文本 hunk、多文件整提交补丁、空新增文件、.svg / .bmp 等一律保持原有行为,因此文本 Diff 与其他二进制文件展示不受影响。

补丁头中的 new file mode / deleted file mode 用于区分新增、删除与修改三种预览模式。

两侧版本解析

新增 DiffImagespackages/ssh-client/src/diff-images.ts),按比较类型解析两侧对应的 Git 对象,与 git diff 的语义保持一致:

场景 变更前 变更后
未暂存 暂存区 :0:file 远端工作区磁盘内容(SFTP 读取)
已暂存 HEAD:file 暂存区 :0:file
提交 父提交(或显式 parentCommit 该提交

已确认的设计取舍

  1. base64 随 JSON 返回,而非新增二进制端点。桌面端 CSP 已是 img-src 'self' data:apps/desktop/src/main.cjs),使用 data URL 渲染无需放宽任何安全策略。此结论在实现前核对了现有 CSP 配置。
  2. 某一侧不存在时返回 404,而非 400。新增与删除图片天然缺少一侧,属正常情况而非错误;页面据此显示「此版本不存在该图片」,不会误报为读取失败。
  3. 大小校验前置到传输之前。先用 cat-file -s(Git 对象)或 lstat(工作区文件)判断体积,超过上限直接拒绝,避免传输超限内容。
  4. 二进制读取不经过文本解码器execCommand 新增 binary 模式返回 stdoutBytes,图片字节不会被 UTF-8 解码破坏。

限制与失败处理

  • 单张图片上限 5 MiBDIFF_IMAGE_MAX_BYTES)。
  • 不支持的格式、无法解码、超限、缺少读取权限均回落到既有二进制文件提示,不影响同一 Diff 中的其他内容。
  • 预览为只读操作,不写入文件或暂存区。
  • Windows 仓库沿用既有路径校验,拒绝反斜杠路径与 NTFS 备用数据流(如 icon.png:stream),且在建立连接前即拒绝。

待确认项

  • 动图(GIF)预览当前由浏览器默认自动播放,是否需要提供暂停控制尚未确认。
  • 修改场景首期仅提供并排对比;是否追加叠加滑块模式待评估。

验收标准

  • 新增的常见图片文件在 Diff 中可直接预览。
  • 删除的常见图片文件在 Diff 中可查看删除前的图片。
  • 修改的图片可同时查看变更前和变更后的版本,并有清晰标识。
  • 图片保持正确宽高比,超出可视区域时可自适应缩放(CSS object-fit: containmax-height,已在生产构建中包含;真机视觉确认待补充)。
  • 不支持、无法解码或超出限制的图片会显示明确提示,不影响其他 Diff 内容。
  • 文本文件及其他二进制文件的现有 Diff 行为不受影响。

验证记录

代码提交 7ab4eff,环境 macOS (darwin arm64) + Node.js 22。

日期 代码提交 / 环境 命令或场景 结果与证据
2026-09-20 7ab4eff / macOS arm64 node --test tests/*.test.cjs(packages/ssh-client) 通过:70 项,0 失败,2 项 Windows-only 跳过
2026-09-20 7ab4eff / macOS arm64 新增 tests/diff-images.test.cjs 通过:4 项,覆盖三种比较场景、只读性、降级与路径穿越拒绝
2026-09-20 7ab4eff / macOS arm64 npm test(apps/web) 通过:79 项,0 失败,含新增 3 项
2026-09-20 7ab4eff / macOS arm64 npx jest src/repository(apps/server) 通过:12 项,0 失败
2026-09-20 7ab4eff / macOS arm64 tsc -b(apps/web) 通过:0 错误
2026-09-20 7ab4eff / macOS arm64 npm run build(apps/server,nest build) 通过
2026-09-20 7ab4eff / macOS arm64 npm run build -w web(vite 生产构建) 通过
2026-09-20 7ab4eff / macOS arm64 apps/server 全量 npm test 失败 4 项,属既有问题(见下)
真机 UI 视觉验收(四种格式、动图播放、窄屏单列) 未运行:需在桌面端实际打开观察,截图待补充至 assets/issue-50/
Windows / Linux 平台回归 未运行:仅静态校验逻辑有测试覆盖,未在真实平台执行

集成测试覆盖的场景

packages/ssh-client/tests/diff-images.test.cjs 使用真实 Git 仓库与回环 SSH 服务:

  • 提交比较返回修改图片的两侧字节,且与写入的 PNG 字节完全一致;首个提交无父版本、删除后无新版本均按「缺失」处理。
  • 工作区与暂存区比较各自停留在正确的一侧;断言操作前后 .git/index 与工作区文件字节完全未变,证明预览只读。
  • 不支持的扩展名、超过 5 MiB、目标为目录、路径不存在、非法 side 取值,以及 ../outside.png/etc/icon.png.git/config.png 等穿越路径均被拒绝。
  • 超限的暂存 blob 在传输前即被拒绝,且 SSH 连接保持可用。

既有失败说明

apps/server 全量测试中,FileService / ConnectionController / FileController / GitController 的 "should be defined" 共 4 项失败。已用 git stash -u未改动的干净基线上复现出完全相同的 4 项失败,与本次变更无关,本次未做处理。

风险、未决事项与回退

  • 未验证平台:Windows 与 Linux 未实际运行;相关路径校验逻辑有单元测试覆盖,但无真机记录。不得据此将这两个平台标记为已验收。
  • 未验证交互:真机 UI 视觉效果、GIF 自动播放行为、窄屏(≤820px)单列布局均未实际观察。
  • 内存与传输:单张上限 5 MiB 且传输前校验;base64 带来约 1.33 倍膨胀,最坏约 6.7 MiB 单次响应。大量图片同时打开的压力未做专门测量。
  • SSH 生命周期:新增读取复用既有连接与 withSftp 超时约定;超限时仅关闭当前命令通道,不影响同一连接上的其他工作。
  • 既有环境问题(非本次引入)packages/shared 下嵌套的 typescript@5.9.3 无法识别 ignoreDeprecations: "6.0",导致 npm run build -w @remote-git/shared 失败。该问题在未改动的主工作树上同样复现,本次改用仓库根部的 TypeScript 6.0.3 编译通过。建议另开 Issue 清理该嵌套依赖。
  • CI 覆盖不足:当前工作流仅运行 commitlint,不执行单元测试或构建。PR 的绿标只代表提交信息合规,功能证据来自上表的本地执行记录。
  • 回退方式:改动集中在新增模块(diff-images.tsImageDiffView.tsx)与判定函数 getImageDiffKindgit revert 本次提交即可恢复原有二进制提示,无持久化状态残留。

关联资料

  • Issue:https://github.com/ShaoClean/remote-git/issues/50
  • PR:https://github.com/ShaoClean/remote-git/pull/57
  • 相关设计:Issue-18(新增文件的差异预览,共用 DiffViewerdiff-lines 判定链路)
  • 索引:功能与优化设计
  • 代码入口(基线 7ab4eff):
    • packages/ssh-client/src/diff-images.ts — 两侧版本解析与读取
    • packages/shared/src/types/repository.ts — 图片类型、大小上限与扩展名映射
    • apps/server/src/repository/repository.controller.tsGET :id/diff-image
    • apps/web/src/components/ImageDiffView.tsx — 并排预览组件
    • apps/web/src/components/diff-lines.tsgetImageDiffKind 判定
  • 用户文档:docs/workspace.md「文件差异」章节已补充预览范围与限制。

Clone this wiki locally