Skip to content

fix: address #2131 review follow-ups - #2133

Merged
zerob13 merged 2 commits into
devfrom
fix/copy-button-draft-followups
Aug 10, 2026
Merged

fix: address #2131 review follow-ups#2133
zerob13 merged 2 commits into
devfrom
fix/copy-button-draft-followups

Conversation

@zhangmo8

@zhangmo8 zhangmo8 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

概述

#2131 合并时仍有 4 条未处理的 review 评论(另有 1 条已 resolve 但修复代码未随之落地),本 PR 作为 follow-up 逐条处理。

[P1] 构建时恢复持久化草稿(useComposerSubmit)

loadComposerDraftFromStorage() 此前只能经 switchComposerSession() 触达,而 ChatPage.vue 的 immediate session watcher 在 composable 构建前运行(拿到的是初始 no-op),ChatTabView 又按 session ID 重建 ChatPage,导致重开会话时草稿从不恢复,且下一次 dispose() 可能用空的 live state 覆盖并删除已存草稿。

现在在 composable 构建期间直接从 storage 初始化 message / files / fingerprints;input handle 尚未就绪时经 pendingHandleRestoreSessionId 延迟恢复 document 与 skills(captureDraftForPersistence / captureLiveDraft 在该窗口内也改从恢复的 sessionDrafts 取值,不会把空状态 flush 回去)。测试按真实 remount 路径覆盖(不手动调 switchComposerSession),并断言 dispose() 后 storage 草稿仍在。

[P1] localStorage 只存有界的附件描述符(composerDraftPersistence)

ComposerSessionDraft.files 含完整 MessageFile(图片的 base64 content + thumbnail、文本附件全文),同步写入 localStorage 可能超配额或卡渲染进程,catch 会静默丢弃整个草稿。现持久化前经 toPersistableFile() 剥离 content / thumbnail / resolvedRepresentation,只保留 name / path / mimeType 等描述符(isMessageFile 校验仍通过)。回归测试断言序列化结果不含 base64 payload。

[P2] DcCopyButton 恢复 label 可见文字 fallback

重构成 render 函数时丢了原模板的 <slot>{{ label }}</slot> 兜底,CodeArtifact / McpJsonViewer / McpServers / TraceDialog 这类只传 label 的消费方从「图标 + 文字」退化成纯图标。现以 fallbackSlots 恢复兜底:无默认插槽时渲染 label 文字。

BEFORE(回归后,icon-only):      AFTER(恢复文字):
┌────┐                           ┌────────────┐
│ ⧉  │                           │ ⧉ Copy     │
└────┘                           └────────────┘

[P2] 移除组件级 icon key,保持键盘焦点

key: icon.value 使 copied 切换时整个 DcButton 卸载重建,键盘触发复制后 <button> 节点被移除、焦点掉回 <body>,tooltip 聚焦态同时丢失。DcButton 内部 Icon 自带元素级 :key="icon",zoom 动画不依赖组件级 key;移除后按钮 DOM 全程稳定。测试补了 document.activeElement 断言(attachTo: document.body,覆盖成功态与 1200ms 回落后)。

[style] MessageToolbar 补齐 icon-size="3"

对应已 resolve 的「Preserve the toolbar's 12 px icon contract」评论,修复当时未随 #2131 落地,一并带上并在 trace 测试中断言。

验证

  • Vitest(DcCopyButton / MessageToolbar.trace / useComposerSubmit / composerDraftPersistence):54/54 通过
  • format:check / oxlint / typecheck / i18n 全部通过
  • 注:本地 Node 26 需 NODE_OPTIONS=--no-experimental-webstorage 才能让 jsdom 提供 localStorage(engines 要求 node >=24.18 <25,CI 不受影响)

Relates to #2131.

Summary by CodeRabbit

  • New Features
    • Restored saved message drafts, attachments, skills, and editor content when reopening an active chat.
    • Preserved draft attachment details while reducing unnecessary stored data.
    • Improved copy button labels and retained focus after copying.
  • Improvements
    • Standardized icon sizing across message toolbar actions.
    • Improved button fallback text when no custom label is provided.

Render the label attr as the default-slot fallback again so
text consumers (CodeArtifact, McpJsonViewer, McpServers,
TraceDialog) keep their visible caption, and drop the
component-level icon key so the success icon swap no longer
recreates the button and steals keyboard focus.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds fallback label rendering to DcCopyButton, standardizes message-toolbar icon sizing, and restores persisted composer drafts during initial session setup. Draft storage now filters attachment data before serialization.

Changes

Composer and toolbar updates

Layer / File(s) Summary
Copy fallback and toolbar icon sizing
src/dc-ui/components/button/DcCopyButton.vue, src/renderer/src/components/message/MessageToolbar.vue, test/renderer/components/DcCopyButton.test.ts, test/renderer/components/message/MessageToolbar.trace.test.ts
DcCopyButton preserves provided slots and renders its label as fallback text. Message-toolbar buttons use icon-size="3". Tests cover label visibility, focus retention, and icon sizing.
Storage-safe composer drafts
src/renderer/src/features/chat-page/model/composerDraftPersistence.ts, test/renderer/features/chat-page/model/composerDraftPersistence.test.ts
Draft serialization stores selected attachment metadata and cloned skills instead of full file objects. Tests verify that raw content and thumbnails are excluded.
Initial composer draft restoration
src/renderer/src/features/chat-page/composables/useComposerSubmit.ts, test/renderer/features/chat-page/composables/useComposerSubmit.test.ts
Composer initialization loads the active session draft and restores its message, attachments, pending skills, and editor document. Tests verify restoration and continued persistence.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ActiveSession
  participant composerDraftPersistence
  participant useComposerSubmit
  participant InputHandle
  ActiveSession->>composerDraftPersistence: loadComposerDraftFromStorage
  composerDraftPersistence-->>useComposerSubmit: persisted draft
  useComposerSubmit->>useComposerSubmit: initialize draft tracking
  useComposerSubmit->>InputHandle: restore message, files, skills, and document
Loading

Possibly related PRs

Suggested reviewers: yyhhyyyyyy, zerob13

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title identifies review follow-ups for issue #2131 but does not describe the primary changes, such as draft restoration or attachment persistence limits. Use a specific title that names the main change, such as "fix: restore composer drafts and limit persisted attachment data".
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/copy-button-draft-followups

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/renderer/src/features/chat-page/composables/useComposerSubmit.ts`:
- Around line 215-222: Keep revisions monotonic in nextRevision by using the
current session revision from draftRevisions as the lower bound for
nextDraftRevision before incrementing. Update the draft submission flow around
nextRevision and add a regression test that restores revision 2, edits the
draft, and verifies the persisted revision becomes 3.

In `@src/renderer/src/features/chat-page/model/composerDraftPersistence.ts`:
- Around line 100-101: Update toPersistableFile so it does not persist arbitrary
file.metadata; either omit metadata or construct it from an explicit schema of
bounded fields with appropriate size limits. Tighten isMessageFile to validate
the same bounded metadata shape, and add a regression test proving content-like
or large nested metadata is not persisted.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 682dacaf-6f14-4b5f-a717-2e6313b35fe1

📥 Commits

Reviewing files that changed from the base of the PR and between 2b357b9 and 2c1e1e5.

📒 Files selected for processing (8)
  • src/dc-ui/components/button/DcCopyButton.vue
  • src/renderer/src/components/message/MessageToolbar.vue
  • src/renderer/src/features/chat-page/composables/useComposerSubmit.ts
  • src/renderer/src/features/chat-page/model/composerDraftPersistence.ts
  • test/renderer/components/DcCopyButton.test.ts
  • test/renderer/components/message/MessageToolbar.trace.test.ts
  • test/renderer/features/chat-page/composables/useComposerSubmit.test.ts
  • test/renderer/features/chat-page/model/composerDraftPersistence.test.ts

Load the persisted draft while useComposerSubmit is
constructed: the ChatPage session watcher fires before the
composable exists and each session switch remounts ChatPage,
so switchComposerSession alone never restored a stored draft
and dispose could flush empty state over it. Strip base64
content, thumbnails and resolved representations from
persisted files so localStorage holds bounded descriptors
instead of raw attachment payloads.
@zhangmo8
zhangmo8 force-pushed the fix/copy-button-draft-followups branch from 2c1e1e5 to 3da3364 Compare August 10, 2026 10:10
@zerob13
zerob13 merged commit 6e9facf into dev Aug 10, 2026
6 of 7 checks passed
@zhangmo8
zhangmo8 deleted the fix/copy-button-draft-followups branch August 10, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants