makecindy / makecindy/cindy

[Bug][Desktop 内置浏览器] 页面评论无法二次编辑,重复点击已标注区域会新建评论

Open
#216 1 comment 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.7k
Forks
395
Avg merge
21h 48m
Merged PRs (30d)
776

Description

## 问题概述

Desktop 右侧栏内置浏览器的页面评论(Comment on the page)目前只有“新增评论”链路,没有 Codex 风格的“重新打开已有评论并二次编辑”能力,也不支持在评论输入气泡里粘贴或拖入图片。

用户对单一组件添加评论后,如果评论文字还没写完:

1. 在评论模式下再次点击原来的评论区域/蓝色 marker,不会打开已有评论;
2. 页面会再次进入元素选择流程,新增一条评论和新的 marker;
3. 如果退出评论模式,左侧输入框中的评论胶囊也没有编辑入口;
4. 评论气泡无法接收剪贴板图片或拖入的图片,图片不能归属于当前评论;
5. 最终只能删除重建,无法继续修改未完成的评论或把视觉参考图随评论交给 AI。

这会导致用户无法安全地把评论整理完整后再交给 AI,且容易产生重复 marker、重复截图和重复评论上下文。

## 平台与仓库

- 平台:Desktop(Electron)
- 功能入口:右侧栏内置浏览器 → 页面评论模式
- 主仓库:makecindy/cindy-temp
- 主要范围:apps/desktop renderer / preload / shared
- 不需要服务端或协议改动
- 建议 assignee:nanaco666
- 建议 label:bug

## 复现步骤

### A. 已有评论无法二次编辑

1. 在 Cindy Desktop 打开右侧栏内置浏览器,进入任意网页。
2. 点击浏览器工具栏的页面评论按钮,进入评论模式。
3. 点击一个组件/元素,打开评论输入气泡。
4. 输入一段不完整的评论,例如“这个按钮需要改成……”,点击“添加评论”。
5. 确认左侧 Composer 出现“1 条注释”,页面保留蓝色 marker。
6. 保持评论模式开启,再次点击原来的组件或蓝色 marker 附近。
7. 观察到:不会加载原评论文本,而是再次打开一个空评论气泡并创建新的 marker。
8. 退出评论模式后,悬浮左侧“1 条注释”预览,只能移除单条或清空全部,没有编辑入口。

### B. 评论输入气泡无法接收图片

1. 按上面步骤打开一条评论输入气泡。
2. 将系统截图/剪贴板图片粘贴到评论输入框,或从 Finder/Explorer、桌面、其它窗口把图片拖到输入框。
3. 观察到:输入框没有图片缩略图或附件状态,图片不会归属于当前评论,也无法在保存后随该条评论发送给 AI。

## 实际行为

- 点击已提交 marker/原评论区域会走新建评论流程。
- 评论数量从 1 变为 2,marker 编号继续递增,可能产生重复截图和重复上下文。
- 新气泡的文本为空,原评论内容不会回填。
- 退出评论模式会销毁 guest overlay;重新进入时也没有从草稿恢复可编辑 marker。
- Composer 的评论预览只支持 remove/clear,无法在评论模式关闭后修订。
- Host 侧的提交逻辑始终创建新 id 并 append,不能原地更新原评论。
- 评论输入气泡只有普通 textarea 的文本 onChange,没有 paste/dragover/drop 事件、图片缓存、缩略图、移除或失败提示。
- BrowserCommentDraftItem 只有系统生成的 marker 截图,没有“归属于某条评论的用户图片”字段;发送链路只会附加 item.screenshot。

## 期望行为

### 1. 在评论模式中编辑已有评论

- 已提交的蓝色 marker、元素标注、区域框或文本标注应当可被识别为已有评论。
- 点击已有 marker/对应标注时,打开“编辑评论”气泡,而不是新建评论。
- 气泡应回填原评论文字、已保存的样式调整/文本调整(如果有)、原 marker 编号和目标摘要。
- 保存后保留原评论 id、marker 编号和目标;更新原 BrowserCommentDraftItem,不增加评论数量。
- 编辑保存应重新生成带最新标注状态的截图,并在新截图成功后清理旧截图缓存。
- 左侧 Composer 只显示更新后的已保存版本;页面仍保持评论模式,可继续添加下一条新评论。
- 取消或按 Esc 时,原评论文字、样式、截图、marker 和 Composer 草稿全部保持不变,不创建新评论。

### 2. 评论模式关闭后仍可编辑

- 左侧 Composer 的评论胶囊逐条预览中增加明确的“编辑”入口,或允许点击评论行进入编辑。
- 若对应网页 tab 仍存在,可重新定位到该 marker 并打开编辑气泡。
- 即使评论模式已关闭,也不能让未发送的评论进入不可编辑状态。
- 编辑保存前不得修改 Composer 中的已保存评论;点击保存后才原子替换草稿条目。
- 主输入框的 Send 行为保持不变:只有用户最终点击发送时,才把最新评论序列化并交给 AI。

### 3. 评论输入气泡支持图片粘贴与拖放

- textarea/评论编辑区域支持从剪贴板粘贴截图或图片。
- 支持从 Finder/Explorer、桌面或其它窗口拖入图片。
- 支持常见图片类型(PNG/JPEG/GIF/WebP);非图片文件应明确拒绝并提示原因。
- 图片在评论未保存前只属于当前 pending/editing 评论,不进入全局 Composer 附件列表。
- 气泡内显示缩略图、文件名/类型(如可得)和移除入口;可处理多张图片。
- 编辑已有评论时回填已有用户图片,可继续添加或删除。
- 取消编辑时丢弃本次新增/删除变化;保存时原子替换该评论的图片集合。
- 左侧评论预览应显示该评论有用户图片,或提供可见的图片数量/缩略图状态。
- 发送时每条评论的用户图片应与该条 marker 截图一起发送,并在序列化文本中明确标识图片与对应评论编号,避免把图片变成无归属的全局附件。
- 删除评论、清空评论、编辑替换图片、发送成功后的所有权转移都必须正确清理或保留缓存,不能泄漏孤儿缓存文件。
- 可复用 Composer 现有图片缓存、校验和拖放语义,但必须保持评论级关联。

## 当前根因与代码位置

### Guest preload

- apps/desktop/src/preload/browserCommentPreload.ts
- buildOverlay():.doc-layer 和 .marker 都是 pointer-events: none(约 368–445 行),已提交 marker 无法接收点击。
- enterMode() 的 onClick(约 920–927 行)只执行 selectElement();没有先命中 committed marker/annotation 的分支。
- OverlayState.committedMarkers(约 331–335 行)只保存 { number, marker },没有关联草稿评论 id、目标类型或可编辑上下文。
- commitPending()(约 1017–1044 行)只把 pending marker 转为常驻,没有暴露 marker activation/edit 事件。

### Shared contract、Host 状态机与草稿

- apps/desktop/src/shared/browserComment.ts
- 当前 guest → host 只有 element-selected、截图完成和退出模式事件,没有“激活已有评论/请求编辑”的 channel 或 payload。
- apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/useBrowserComment.ts
- BrowserCommentMode 只有 off/selecting/pending/submitting,没有 editing 状态或当前编辑评论 id(约 67–79 行)。
- doSubmit()(约 163–268 行)每次都生成新的 crypto.randomUUID() 并调用 appendBrowserCommentToDraft()。
- element-selected 处理(约 295–313 行)无论是原 marker 附近还是新元素,都只进入新 pending。
- 截图缓存流程只创建系统 marker screenshot,没有 comment-level pasted/dropped image 的接线。
- apps/desktop/src/renderer/lib/composerDraftStore.ts
- 目前只有 appendBrowserCommentToDraft()(约 371–386 行),没有按 id replace/update 的草稿操作。
- apps/desktop/src/renderer/lib/browserComments.ts
- BrowserCommentDraftItem 已包含稳定 id、marker 编号、target、comment、screenshot、styleChanges,但没有评论用户图片字段。
- formatBrowserCommentsForSend() 只序列化评论文字/样式,没有用户图片清单或评论级图片引用。

### 评论气泡与 Composer

- apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/BrowserCommentPopover.tsx
- props 没有 editing、initialText、initialStyleChanges、commentImages 或保存/图片处理语义(约 38–50 行)。
- 文本 state 固定初始化为空字符串(约 113–118 行)。
- textarea(约 249–263 行)没有 onPaste、onDragEnter、onDragOver、onDrop。
- 提交按钮文案固定为“添加评论”(约 363–375 行),没有编辑态保存语义。
- apps/desktop/src/renderer/components/new-chat/ChatInput.tsx
- 评论胶囊预览(约 4743–4833 行)只有移除单条和清空全部,没有编辑动作,也没有评论图片状态。
- 通用 Composer 已有图片 paste/drop 逻辑:Tiptap handlePaste(约 1549–1607 行)、Composer onDrop(约 4632–4725 行)和 useAttachments 的 cacheImageFromBuffer 逻辑;这些可以复用,但不能把评论图片直接混入无归属的全局 attachments。
- 发送链路(约 3347–3353 行)只把每条评论的 item.screenshot 加入 filesToSend。

## 建议修复方向

1. 为 shared contract 增加 activate existing marker / edit comment 事件。
2. Guest overlay 为 marker、region、text annotation 建立可点击的命中层;点击时优先返回已有 marker,不要让 blocker 的新建 onClick 抢先处理。
3. committedMarkers 保存 marker 与草稿 id 的映射,必要时保存目标类型/坐标;host 侧通过当前 draft 找回完整 BrowserCommentDraftItem。
4. 扩展 useBrowserComment:增加 editing 状态和 editingCommentId;支持 startEdit(item)、updateBrowserComment(id, patch);新建走 append,编辑走 replace/upsert;截图生成、图片缓存和草稿替换保持事务语义,失败时保留原条目。
5. 扩展 BrowserCommentPopover 支持初始值、编辑态标题/按钮、保存和取消。
6. 在 BrowserCommentPopover 增加图片 paste/drop 处理:解析 ClipboardEvent/DragEvent 的 image items,缓存成 AttachedFile,显示缩略图并允许删除;复用 Composer 的类型校验和缓存失败提示。
7. 为 BrowserCommentDraftItem 增加评论级用户图片集合;更新 formatBrowserCommentsForSend 与 ChatInput filesToSend,使每条评论的用户图片和 marker screenshot 有明确编号关联。
8. Composer 逐条评论预览增加 Edit 入口和图片状态,作为退出评论模式后的兜底编辑路径。
9. 编辑样式反馈时,继续维护 removeBrowserCommentAndRepairChains() 依赖的 previousValue 链,避免修改早期评论后破坏后续同元素样式注解。
10. 保留现有 Cmd/Ctrl+click 立即添加、Shift 拖拽区域评论、文本选择评论、页面导航/刷新取消失效 guest 状态和发送时统一序列化行为。

## 验收标准

- [ ] 新建一条评论后,点击同一已提交 marker 会打开原评论编辑气泡,而不是新增评论。
- [ ] 编辑气泡会回填原文字和已有样式调整。
- [ ] 保存编辑后评论总数、评论 id、marker 编号不变,左侧 Composer 显示最新内容。
- [ ] 编辑保存会使用最新内容生成截图;旧截图只在新截图成功后清理。
- [ ] 取消编辑不会改变原评论、截图、样式或 marker。
- [ ] 评论模式关闭后,可从 Composer 评论预览进入编辑,不会出现只能删除不能修改的死路。
- [ ] 点击未标注区域仍能正常创建新评论,marker 编号不重复。
- [ ] 同时存在多条评论时,只更新被编辑的那一条。
- [ ] 评论气泡可以粘贴剪贴板图片,并显示缩略图和移除入口。
- [ ] 评论气泡可以拖入 PNG/JPEG/GIF/WebP 图片;非图片文件有明确错误提示。
- [ ] 编辑已有评论时已有图片会回填,新增/删除图片只在保存后生效。
- [ ] 取消编辑不会把图片写入 Composer 或留下孤儿缓存。
- [ ] 左侧评论预览能显示评论图片状态。
- [ ] 发送后每条评论的最新文字、样式、用户图片和 marker 截图均能被 AI 收到,且图片与评论编号对应。
- [ ] 删除/清空/替换/发送后的缓存所有权和清理行为正确。
- [ ] 发送结果只包含最新一次保存的评论内容,不产生重复评论块或重复图片。
- [ ] 页面导航、刷新、切换 tab、草稿恢复和重新进入评论模式不会留下孤儿 marker 或错误关联。
- [ ] 覆盖 Light/Dark 主题以及中英文/中文输入法(Enter/Esc composing guard)回归。
- [ ] 补充 guest marker 命中、hook editing 状态、popover 回填/取消/保存、图片 paste/drop、草稿 replace/upsert、序列化和缓存清理的自动化测试。

## 关联检查

截至 2026-07-23,已检查 makecindy/cindy-temp 的 open issues 和 open PRs,没有发现覆盖上述评论二次编辑或评论内图片输入的未合并事项。

Contributor guide

Open the contributing guide

Research direction

Start by reading apps/desktop/src/preload/browserCommentPreload.ts, the shared contract, and useBrowserComment.ts to trace marker activation and draft updates. Then inspect BrowserCommentPopover.tsx, composerDraftStore.ts, browserComments.ts, and ChatInput.tsx, including the existing Composer image-cache paths. Done means the listed edit, cancel, image paste/drop, serialization, cache-cleanup, and regression-test acceptance criteria pass without duplicate comments.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron, typescript
Domain
desktop, frontend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.