From 2ce3c07702d033d29765c9c68211083e368a25d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=88=E6=84=BFQiii?= <2297740147@qq.com> Date: Thu, 22 Oct 2026 11:35:48 +0800 Subject: [PATCH 1/2] feat(web): collect inline diff feedback in the chat draft Preserve the original diff source and existing draft while reviewing multiple lines. Keep IME cancellation inside the inline editor. --- .../2026-10-22-diff-inline-feedback/README.md | 39 +++++++++ .../after-desktop.png | Bin 0 -> 111229 bytes .../after-narrow.png | Bin 0 -> 56724 bytes .../before-desktop.png | Bin 0 -> 117959 bytes .../before-narrow.png | Bin 0 -> 40855 bytes .../observations.json | 31 +++++++ .../conversation-inspector-feedback.test.tsx | 37 ++++++++ web/src/v2/components/conversation.tsx | 14 ++- web/src/v2/components/review-diff-comment.css | 7 ++ web/src/v2/components/review-diff.test.tsx | 72 +++++++++++++++- web/src/v2/components/review-diff.tsx | 80 +++++++++++++++--- web/src/v2/components/task-git.test.tsx | 22 ++++- web/src/v2/components/task-git.tsx | 6 +- web/src/v2/components/task-overview.tsx | 13 +-- web/src/v2/components/task-review.tsx | 24 ++++-- web/src/v2/review-comment-draft.test.tsx | 57 +++++++++++++ web/src/v2/review-comment-draft.ts | 15 ++++ 17 files changed, 389 insertions(+), 28 deletions(-) create mode 100644 docs/acceptance/2026-10-22-diff-inline-feedback/README.md create mode 100644 docs/acceptance/2026-10-22-diff-inline-feedback/after-desktop.png create mode 100644 docs/acceptance/2026-10-22-diff-inline-feedback/after-narrow.png create mode 100644 docs/acceptance/2026-10-22-diff-inline-feedback/before-desktop.png create mode 100644 docs/acceptance/2026-10-22-diff-inline-feedback/before-narrow.png create mode 100644 docs/acceptance/2026-10-22-diff-inline-feedback/observations.json create mode 100644 web/src/v2/components/review-diff-comment.css create mode 100644 web/src/v2/review-comment-draft.test.tsx create mode 100644 web/src/v2/review-comment-draft.ts diff --git a/docs/acceptance/2026-10-22-diff-inline-feedback/README.md b/docs/acceptance/2026-10-22-diff-inline-feedback/README.md new file mode 100644 index 00000000..66e13261 --- /dev/null +++ b/docs/acceptance/2026-10-22-diff-inline-feedback/README.md @@ -0,0 +1,39 @@ +# Diff 行内意见加入聊天草稿 + +本次补的是多处代码审阅时反复退出、重新定位的问题。基线 `ed87d1dbdf3bc04c7935ca9c7b4fabfb67524e0e`(PR #445)已有文件、片段和行引用,但点击后立即关闭审阅窗口;没有在行旁输入意见并继续审阅的入口。核对当前 40 个待审 PR 后,没有发现已覆盖此流程的实现。 + +## 竞品依据与选择 + +对照的是二者公开文档描述的实际产品流程,不推断未公开的内部存储或组件实现。核对日期:2026-10-22。 + +| 产品 | 公开流程 | 本次取舍 | +| --- | --- | --- | +| [Claude Code Desktop](https://code.claude.com/docs/en/desktop#review-changes-with-diff-view) | 在 Diff 行旁输入意见,可连续添加多条,再统一提交给 Claude | 采用就地输入和连续审阅;UC 的 Enter 保留多行输入,Ctrl/⌘+Enter 将当前意见加入草稿 | +| [Codex app](https://learn.chatgpt.com/docs/code-review#inline-comments-for-feedback) | 通过行旁的 + 添加意见,完成后发送聊天消息让 Codex 处理 | 采用显式回到聊天检查并发送;添加意见本身不执行任务 | +| Universal-Code 本次实现 | 行旁 + → 写意见 → 加入现有聊天草稿 → 继续看其它行 → 返回草稿发送 | 复用现有草稿文档、附件和冲突处理;不增加评论数据库、后端接口或新的模型调用 | + +每条意见携带原始来源、旧/新路径、差异片段、原始行文本和该版本的行号。输入时冻结这些来源;刷新后即使差异消失,也不会把意见重新绑定到另一行。原有引用操作和长行换行继续可用。意见加入草稿后可以编辑或删除;尚未点击“加入聊天草稿”的行内输入仅保留在当前审阅视图。 + +## 依赖与兼容 + +本 PR 叠加 #445,目标分支为 `Qiyuanqiii/review-diff-line-wrap`。`onAddComment` 是可选回调,原 `onFeedback` 与服务端合同不变。其它待审 PR 的依赖关系不变。特别核对 #437、#454、#458、#461、#465、#467、#469、#470 的对话、快捷键与草稿交互;#447/#451 使用另一套仓库 Diff 组件。 + +## 真实浏览器验证 + +使用仓库现有 `cmd/workbenchjourney`、真实 Go API 和 SQLite,在临时 Git 项目中通过 workspace import、thread、lifecycle、file-edit-proposal API 创建 `settings.ts` 提案。没有替换浏览器 API 响应。一个本机静态代理依次提供基线与候选生产包,并将 API 请求转发至同一个 Go 进程;两组截图使用同一持久化编辑记录、原始/提案哈希和 Diff。供应商使用现有离线 journey provider,审阅过程未请求模型推理。 + +| 视口 | 变更前 | 变更后 | +| --- | --- | --- | +| 1280 × 900 | [仅可引用](before-desktop.png) | [就地输入意见](after-desktop.png) | +| 390 × 844 | [窄屏 Diff](before-narrow.png) | [继续添加第二条意见](after-narrow.png) | + +[浏览器观测记录](observations.json) 保留浏览器版本、实际 Diff、布局尺寸及操作结果: + +- 两条意见连续加入同一草稿,原有空白和正文保留;审阅窗口保持打开。 +- 每条意见包含正确的源编辑 ID、新文件行号和原始代码;返回后可检查完整草稿,刷新重连后仍保留两条意见。 +- Ctrl+Enter 与 Meta+Enter 添加意见;Escape 只关闭当前行内输入;返回草稿聚焦编辑入口,窄屏折叠入口可展开继续编辑。 +- 窄屏编辑器位于视口内,开启换行后 Diff 无横向溢出。审阅操作期间无 API 写请求,无未捕获浏览器错误。 + +通过:6 个相关测试文件的 68 项测试、`npm run build`(包含 TypeScript 检查)、`npm run check:api`、`go test -count=1 -tags desktop ./web ./internal/webui`。测试覆盖旧引用行为、Git 预览指纹、差异刷新、中文/英文、键盘与输入法事件、草稿冲突、保存失败、附件保留和跨任务隔离。 + +本次真实浏览器为 Linux Chromium。Windows/macOS 原生壳、真实中文输入法及 macOS 硬件键盘未实测;Meta 与组合输入仅验证合成事件。Git 预览由组件测试覆盖,journey harness 未启用任务 Git API。上述结果不代表完整桌面发行验收。 diff --git a/docs/acceptance/2026-10-22-diff-inline-feedback/after-desktop.png b/docs/acceptance/2026-10-22-diff-inline-feedback/after-desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..9e727c36a02cfb64197af0fcf249ab9b850b9020 GIT binary patch literal 111229 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