Skip to content

feat: improve renderer interaction quality - #1976

Merged
zhangmo8 merged 6 commits into
devfrom
feat/renderer-interaction-quality
Jul 15, 2026
Merged

feat: improve renderer interaction quality#1976
zhangmo8 merged 6 commits into
devfrom
feat/renderer-interaction-quality

Conversation

@zhangmo8

@zhangmo8 zhangmo8 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a typed MCP batch installation-status route and make marketplace pagination query only each newly loaded batch
  • expose pending, duplicate-prevention, and retry states for MCP installs, API-key saves, and settings navigation
  • replace layout-bound side-panel width motion with surface motion, and make message disclosures/toolbars keyboard-visible
  • add SDD documentation plus contract, accessibility, interaction, pagination, and performance coverage

BEFORE

MCP page
  fetch page
    +-- card A -> isInstalled IPC
    +-- card B -> isInstalled IPC
    `-- card N -> isInstalled IPC
  load failure -> pull beyond the bottom to retry

Chat
  [content width animates] [side-panel width animates]
  tool/error row <div> -> details scale and shift independently
  toolbar -> hover only

Settings
  click row -> no immediate feedback -> lazy chunk -> route

AFTER

MCP page
  fetch page -> one batch status IPC -> ready cards
                                      +-- pending install state
                                      `-- duplicate click blocked
  load failure -> [Retry]

Chat
  [content width commits once] [side-panel surface slides/fades]
  [tool/error button >] -> synchronized height, opacity, and chevron
  toolbar -> hover | keyboard focus | coarse pointer

Settings
  hover/focus -> prefetch chunk
  click row -> spinner/aria-busy -> route success or cleared failure

Validation

  • pnpm run format
  • pnpm run i18n
  • pnpm run lint
  • pnpm run typecheck
  • affected suites: 12 files, 202 tests passed
  • performance smoke: 160 production tool blocks mounted in about 255ms
  • full suite: 5,398 tests passed; four combined-run environment failures all passed in isolated reruns
  • architecture baseline isolated rerun: 2/2 passed
  • loopback remote-control isolated rerun outside the restricted sandbox: 22/22 passed

Risks

  • the batch contract is capped at 100 IDs; the marketplace currently requests 20 per page
  • no stored-data migration or dependency change is included
  • validation ran on Node 26.0.0 while the repository declares Node 24.14.x; all checks above passed

Summary by CodeRabbit

  • New Features
    • Marketplace installation status now resolves in batches with incremental pagination and a dedicated refresh/retry flow on failures.
    • Settings navigation now preloads destinations and shows a pending/loading state while switching sections.
  • Accessibility
    • Message error and tool-call details now use proper disclosure controls (aria-expanded/aria-controls) with improved expand/collapse behavior.
    • Message toolbar copy/image actions now support keyboard interactions and shortcut hints.
  • Bug Fixes
    • Improved detection for dropped workspace folders.
    • Corrected settings window close-button styling and localized accessibility labels.
  • Performance
    • Refined reduced-motion and side-panel/chat layout transitions to avoid width-transition jank during resizing.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f6e1fb0-3785-4ddb-b2c7-22672a9836a5

📥 Commits

Reviewing files that changed from the base of the PR and between d845603 and 4184372.

📒 Files selected for processing (2)
  • test/renderer/composables/useMessageWindow.test.ts
  • test/renderer/performance/chatRendering.perf.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/renderer/composables/useMessageWindow.test.ts
  • test/renderer/performance/chatRendering.perf.test.ts

📝 Walkthrough

Walkthrough

The PR adds batched MCP installation-status lookup, incremental marketplace loading with retry and concurrency states, settings route prefetch and pending feedback, accessible message disclosures and toolbar actions, layout and performance updates, tests, and architecture documentation.

Changes

Renderer interaction performance

Layer / File(s) Summary
MCP installed-status route and client flow
src/shared/contracts/..., src/main/..., src/renderer/api/McpClient.ts, test/main/..., test/renderer/api/clients.test.ts
Adds a typed batched installation-status route spanning the MCP presenter, dispatcher, renderer client, and tests.
Marketplace pagination and installation states
src/renderer/settings/components/McpBuiltinMarket.vue, test/renderer/components/McpBuiltinMarket.test.ts
Batches status checks for new pages, adds explicit loading/error/retry states, and prevents duplicate saves and installs.
Settings route prefetch and pending feedback
src/renderer/settings/App.vue, src/renderer/settings/main.ts, src/renderer/settings/settingsRouteComponents.ts, test/renderer/components/SettingsApp.test.ts
Extracts route loaders, preloads routes on hover or focus, and exposes asynchronous navigation with pending indicators.
Accessible message disclosures and toolbar actions
src/renderer/src/components/message/*, test/renderer/components/message/*, test/renderer/performance/*, test/renderer/composables/useMessageWindow.test.ts
Adds ARIA-controlled message disclosures, delayed tool-detail unmounting, keyboard toolbar actions, timer cleanup, and rendering performance coverage.
Layout transition and interaction-performance specification
src/renderer/src/components/sidepanel/*, src/renderer/src/views/ChatTabView.vue, docs/architecture/renderer-interaction-performance/*, test/renderer/components/ChatSidePanel.test.ts
Removes shell-level width transitions, improves dropped-path fallback handling, and documents the implementation and validation checklist.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant McpBuiltinMarket
  participant McpClient
  participant dispatchDeepchatRoute
  participant McpPresenter
  McpBuiltinMarket->>McpClient: listInstalledServerIds(source, sourceIds)
  McpClient->>dispatchDeepchatRoute: invoke MCP route
  dispatchDeepchatRoute->>McpPresenter: resolve installed IDs
  McpPresenter-->>dispatchDeepchatRoute: installedSourceIds
  dispatchDeepchatRoute-->>McpBuiltinMarket: merge installed status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main theme of renderer interaction and accessibility improvements.
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 feat/renderer-interaction-quality

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: 1

🧹 Nitpick comments (2)
src/renderer/src/components/message/MessageToolbar.vue (2)

117-118: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Keyboard activation only reaches the "copy" action, not "copy from top."

handleCopyImageStart/handleCopyImageEnd distinguish a short tap (copyImage) from a long press (copyImageFromTop), but the new keydown.enter/keydown.space binding always calls handleCopyImageKeyboard, which only emits copyImage. Keyboard-only users have no way to trigger the "copy from top" variant that mouse users get via long-press.

Consider adding a keyboard-accessible path to the long-press variant (e.g. a modifier like Shift+Enter), or documenting that it is mouse-only if intentional.

Also applies to: 299-317

🤖 Prompt for 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.

In `@src/renderer/src/components/message/MessageToolbar.vue` around lines 117 -
118, Extend the keyboard activation handling in MessageToolbar so keyboard users
can trigger both copyImage and copyImageFromTop, matching the existing
handleCopyImageStart/handleCopyImageEnd behavior. Add a clearly defined keyboard
gesture such as Shift+Enter (and an equivalent space-key gesture if supported)
that routes to the long-press variant, while preserving the current Enter/Space
short-copy behavior.

263-274: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate tip-timer logic across four call sites.

handleCopy, the long-press branch of handleCopyImageStart, handleCopyImageEnd, and the new handleCopyImageKeyboard all repeat the same "show tip → clear stale timer → auto-hide after 2000ms → null out timer" block. handleCopyImageEnd (Lines 299-304) and handleCopyImageKeyboard (Lines 310-315) are now identical in body. Extracting a small helper would remove the duplication and guarantee the four tips stay behaviorally consistent.

♻️ Example helper extraction
+const flashTip = (
+  tipRef: import('vue').Ref<boolean>,
+  getTimer: () => number | null,
+  setTimer: (id: number | null) => void
+) => {
+  tipRef.value = true
+  const existing = getTimer()
+  if (existing !== null) window.clearTimeout(existing)
+  setTimer(
+    window.setTimeout(() => {
+      tipRef.value = false
+      setTimer(null)
+    }, 2000)
+  )
+}
+
 const handleCopyImageKeyboard = (event: KeyboardEvent) => {
   if (event.repeat || props.isCapturingImage) return
   emit('copyImage')
-  showCopyImageTip.value = true
-  if (copyImageTipTimer !== null) window.clearTimeout(copyImageTipTimer)
-  copyImageTipTimer = window.setTimeout(() => {
-    showCopyImageTip.value = false
-    copyImageTipTimer = null
-  }, 2000)
+  flashTip(showCopyImageTip, () => copyImageTipTimer, (id) => (copyImageTipTimer = id))
 }

Also applies to: 283-286, 299-317

🤖 Prompt for 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.

In `@src/renderer/src/components/message/MessageToolbar.vue` around lines 263 -
274, Extract the repeated tip-display and timer-reset logic from handleCopy,
handleCopyImageStart, handleCopyImageEnd, and handleCopyImageKeyboard into a
shared helper. Have the helper show the relevant tip, clear any existing timer,
schedule the 2000ms auto-hide, and null the timer afterward; update all four
call sites to use it while preserving their existing tip state and timer
variables.
🤖 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/components/message/MessageToolbar.vue`:
- Around line 375-389: The scoped .relative rule in MessageToolbar is currently
unused. Add the relative class to the button containing the absolutely
positioned tip spans so they anchor correctly, or remove the rule if no
anchoring is needed; prefer updating that button to preserve the intended
tooltip positioning.

---

Nitpick comments:
In `@src/renderer/src/components/message/MessageToolbar.vue`:
- Around line 117-118: Extend the keyboard activation handling in MessageToolbar
so keyboard users can trigger both copyImage and copyImageFromTop, matching the
existing handleCopyImageStart/handleCopyImageEnd behavior. Add a clearly defined
keyboard gesture such as Shift+Enter (and an equivalent space-key gesture if
supported) that routes to the long-press variant, while preserving the current
Enter/Space short-copy behavior.
- Around line 263-274: Extract the repeated tip-display and timer-reset logic
from handleCopy, handleCopyImageStart, handleCopyImageEnd, and
handleCopyImageKeyboard into a shared helper. Have the helper show the relevant
tip, clear any existing timer, schedule the 2000ms auto-hide, and null the timer
afterward; update all four call sites to use it while preserving their existing
tip state and timer variables.
🪄 Autofix (Beta)

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

Run ID: 5327b07f-8866-4b64-8804-6d69f0269b5b

📥 Commits

Reviewing files that changed from the base of the PR and between e042478 and 42bc630.

📒 Files selected for processing (31)
  • docs/architecture/renderer-interaction-performance/plan.md
  • docs/architecture/renderer-interaction-performance/spec.md
  • docs/architecture/renderer-interaction-performance/tasks.md
  • src/main/presenter/mcpPresenter/index.ts
  • src/main/routes/index.ts
  • src/renderer/api/McpClient.ts
  • src/renderer/settings/App.vue
  • src/renderer/settings/components/McpBuiltinMarket.vue
  • src/renderer/settings/main.ts
  • src/renderer/settings/settingsRouteComponents.ts
  • src/renderer/src/components/message/MessageBlockError.vue
  • src/renderer/src/components/message/MessageBlockToolCall.vue
  • src/renderer/src/components/message/MessageToolbar.vue
  • src/renderer/src/components/sidepanel/ChatSidePanel.vue
  • src/renderer/src/components/sidepanel/WorkspacePanel.vue
  • src/renderer/src/views/ChatTabView.vue
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/mcp.routes.ts
  • src/shared/types/presenters/core.presenter.d.ts
  • test/main/presenter/mcpPresenter.test.ts
  • test/main/routes/contracts.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/renderer/api/clients.test.ts
  • test/renderer/components/ChatSidePanel.test.ts
  • test/renderer/components/McpBuiltinMarket.test.ts
  • test/renderer/components/SettingsApp.test.ts
  • test/renderer/components/message/MessageBlockBasics.test.ts
  • test/renderer/components/message/MessageBlockToolCall.test.ts
  • test/renderer/components/message/MessageToolbar.trace.test.ts
  • test/renderer/composables/useMessageWindow.test.ts
  • test/renderer/performance/chatRendering.perf.test.ts
💤 Files with no reviewable changes (2)
  • src/renderer/src/components/sidepanel/ChatSidePanel.vue
  • src/renderer/src/components/sidepanel/WorkspacePanel.vue

Comment thread src/renderer/src/components/message/MessageToolbar.vue
@zhangmo8

Copy link
Copy Markdown
Collaborator Author

Addressed the CodeRabbit follow-ups in d845603f9:

  • anchored both absolutely positioned success tips to their copy buttons
  • added Enter/Space for current-message capture and Shift+Enter/Shift+Space for capture from top, with aria-keyshortcuts
  • consolidated repeated tip visibility/timer handling while preserving unmount cleanup

Validation passed: component tests (6/6), web typecheck, format, i18n, full lint, and targeted oxlint.

The generic docstring-coverage warning was not applied: these are private, self-explanatory <script setup> handlers rather than public APIs, and adding JSDoc would not match the repository's concise SFC style.

…ion-quality

# Conflicts:
#	test/renderer/composables/useMessageWindow.test.ts
@zhangmo8
zhangmo8 merged commit 8c1e5a4 into dev Jul 15, 2026
4 checks passed
@zhangmo8
zhangmo8 deleted the feat/renderer-interaction-quality branch July 15, 2026 08:31
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.

1 participant