Skip to content

refactor(renderer): split scopes and fix review-confirmed races#2000

Open
zhangmo8 wants to merge 11 commits into
devfrom
refactor/renderer-scope-optimization
Open

refactor(renderer): split scopes and fix review-confirmed races#2000
zhangmo8 wants to merge 11 commits into
devfrom
refactor/renderer-scope-optimization

Conversation

@zhangmo8

@zhangmo8 zhangmo8 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Renderer scope-boundary restructuring per docs/architecture/renderer-scope-optimization (spec/plan/tasks included), plus fixes for every issue confirmed by a final four-boundary independent review (stores/foundation, chat search, message window/virtualization, page shell/router).

Structure

  • Move ChatTabView into apps/chat-main; remove unused views/SettingsTabView (settings owned by the dedicated settings renderer)
  • Extract foundation/appearance/documentAppearance shared by chat-main, settings, and floating entries (theme / font size / locale / direction projection)
  • Session store: cursor pagination, tombstoned deletes, targeted activate/deactivate IPC

Review-confirmed fixes

  • Chat search: same-query re-apply no longer drops the highlight observer's pending rows (streamed/edited rows lost highlights permanently); user messages with both text and rendered content blocks are no longer double-counted (match counter desynced from DOM marks, causing dead Enter-navigation slots)
  • Sidebar search: auto-fill pagination keeps running while a search query is active, so the client-side filter can match every session instead of only already-loaded pages ([BUG] 对话历史无法加载 #1762 behavior preserved); collapsed-group guards still apply when not searching
  • Message window: the stable-tail fast path now returns a fresh array — previously it mutated in place, so totalHeight / window range never updated and the pending-assistant placeholder row was invisible; batch measurements no longer trigger a full layout rebuild per row
  • Scroll arbitration: a session-restore bottom scroll that lands after the user already jumped via search/spotlight is now rejected (state machine gained hasExplicitNavigation)
  • Session store: invalidating an in-flight first page on a delete broadcast now refetches, instead of leaving the sidebar with no skeleton, no rows, and no error
  • Actions: plan snapshots are cleared for deleted messages even when the view switched while the confirm dialog was open; sessionIpc targeted-update payload narrowed explicitly
  • Behavior restore: thread-level isInGeneratingThread (fork gating, variant auto-switch) split from new row-level isStreamingMessage (activity grouping only), restoring fork-disabled-while-generating; time-group workspace sessions sort by recency again

Tests

  • New suites: useChatSearch, useMessageVirtualization, documentAppearance, languageStore, themeStore
  • Regression tests for the observer-drain and fast-path-propagation bugs (verified red without the fix)
  • Fixed test-infra gaps: fake-timer rAF advancement, i18n mock locale, global restoreAllMocks stripping listener-mock implementations, full-page restore mocks

typecheck / lint / format / i18n clean; renderer suite 179 files / 1410 tests green.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added pending-state Stop/Steer controls with accessibility updates and safeguards against duplicate actions.
    • Improved streaming message highlighting and screenshot/capture parent targeting.
    • Added a sticky “failed to load history” state with retry in chat.
  • Bug Fixes
    • Hardened deeplink and session-restore concurrency so stale updates can’t overwrite newer UI.
    • Refined sidebar session ordering and pagination auto-fill rules.
    • Improved chat search highlighting/navigation (debounced, reduced flicker) and virtualization/layout updates.
    • Synchronized theme, language, and text direction across renderer views via shared appearance handling.
  • Documentation
    • Updated renderer scope/optimization plans and specs, plus chat interrupt/actions stall guidance.

Restructure renderer scope boundaries per docs/architecture/renderer-scope-optimization:

- Move ChatTabView into apps/chat-main; drop unused views/SettingsTabView
- Extract foundation/appearance/documentAppearance shared by all renderer entries
- Session store: paginated list with cursor, tombstoned deletes, targeted
  activate/deactivate IPC; refetch when a pending first page is invalidated
- Chat search: canonical debounced query, incremental per-row highlighting
  with a MutationObserver; drain pending observer work on same-query re-apply
  and count only rendered user-message content
- Message window fast path returns a fresh array so totalHeight and the
  window range react to tail growth; batch measures no longer force per-row
  full layout rebuilds
- Scroll state machine rejects a late session-restore scroll after the user
  explicitly navigated (search/spotlight)
- Split row-level isStreamingMessage (activity grouping) from thread-level
  isInGeneratingThread (fork gating, variant auto-switch)
- Always clear plan snapshots for deleted messages even after switching views
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Renderer changes add race-safe session, project, deeplink, composer, search, streaming, appearance, and route-host behavior, alongside Markstream integration, metadata refreshes, architecture documentation, and regression tests.

Changes

Renderer optimization and reliability

Layer / File(s) Summary
Session and async-state concurrency
src/renderer/src/stores/ui/session.ts, src/renderer/src/stores/ui/project.ts, src/renderer/src/stores/ui/sessionIpc.ts, src/renderer/src/features/chat-page/composables/*
Revision, epoch, version, tombstone, and request-scoped write guards protect session, project, restore, pagination, and message-action updates.
Chat startup and controls
src/renderer/src/apps/chat-main/*, src/renderer/src/pages/NewThreadPage.vue, src/renderer/src/features/chat-page/ChatPage.vue, src/renderer/src/components/chat/ChatInputToolbar.vue
Deeplink activation, draft submission, steering, stopping, history retry, and chat hydration gain token and pending-state handling.
Streaming, search, and virtualized layout
src/renderer/src/composables/message/*, src/renderer/src/features/chat-page/composables/*, src/renderer/src/lib/chatSearch.ts, src/renderer/src/components/chat/*, src/renderer/src/components/WindowSideBar.vue
Streaming rows, append-only layout segments, debounced search highlighting, capture-parent lookup, sidebar ordering, and pagination auto-fill are updated.
Appearance, routing, and Markstream integration
src/renderer/src/foundation/appearance/*, src/renderer/src/stores/{language,theme}.ts, src/renderer/src/components/markdown/*, src/renderer/src/router/index.ts, package.json
Appearance projection is centralized, language/theme initialization is guarded, the chat route uses the new shell, and Markstream rendering uses normalized fences and built-in renderer configuration.
Documentation, data, and regression validation
docs/architecture/*, docs/issues/*, resources/*, test/renderer/**/*, test/main/**/*
Architecture and issue specifications, model and agent metadata, and tests document and exercise the updated behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: zerob13

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.96% 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 accurately captures the main renderer scope split and race-condition fixes reflected across the changeset.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/renderer-scope-optimization

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.

zhangmo8 added 3 commits July 20, 2026 14:03
Sidebar search is a pure client-side filter, so pagination must keep
auto-filling while a query is active or matches are limited to already
loaded pages. Collapsed-group guards still apply when not searching.
Preserve targeted session updates across stale page loads and fence environment reorder rollbacks. Align search indexing with activatable DOM content and refresh the renderer architecture baseline.
@zhangmo8
zhangmo8 marked this pull request as ready for review July 20, 2026 07:49

@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 (5)
test/renderer/features/chat-page/composables/useComposerSubmit.test.ts (1)

5-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract a shared deferred-promise test helper.

Both new test files define their own local "deferred" utility with the same shape (a promise plus externally-controlled resolve); this mirrors a pattern that already exists in useToolInteraction.test.ts. Consolidating into one shared helper (e.g. test/renderer/utils/deferred.ts) avoids drift between the two implementations (only one currently exposes reject) and reduces duplication introduced in this PR.

  • test/renderer/features/chat-page/composables/useComposerSubmit.test.ts#L5-L11: replace local createDeferred with the shared helper (which should include reject support for parity).
  • test/renderer/features/chat-page/composables/useMessageActions.test.ts#L5-L14: replace local deferred with the same shared helper.
🤖 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 `@test/renderer/features/chat-page/composables/useComposerSubmit.test.ts`
around lines 5 - 11, Extract the duplicated deferred-promise utilities into a
shared helper at test/renderer/utils/deferred.ts, including externally
controlled resolve and reject functions. In
test/renderer/features/chat-page/composables/useComposerSubmit.test.ts:5-11,
replace createDeferred with the shared helper; in
test/renderer/features/chat-page/composables/useMessageActions.test.ts:5-14,
replace deferred with the same helper and update usages as needed.
src/renderer/src/stores/ui/session.ts (2)

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

isNewerSessionUpdate name is inverted from its actual semantics.

The function returns true when the update is not newer (i.e., should be skipped), but its name reads as "is this update newer". mergeSessions relies on this inverted polarity (if (existing && isNewerSessionUpdate(existing, update)) continue). Given this logic guards cross-window causal ordering, a future edit that "fixes" the name without updating the call site (or vice versa) could silently invert the merge behavior.

♻️ Suggested rename for clarity
-function isNewerSessionUpdate(existing: UISession, update: UISession): boolean {
+function isStaleOrSameSessionUpdate(existing: UISession, update: UISession): boolean {
   const existingUpdatedAt = existing.updatedAt
   const updateUpdatedAt = update.updatedAt
   // ...
   return (
     Number.isFinite(existingUpdatedAt) &&
     Number.isFinite(updateUpdatedAt) &&
     updateUpdatedAt <= existingUpdatedAt
   )
 }

 function mergeSessions(current: UISession[], updates: UISession[]): UISession[] {
   const next = new Map(current.map((session) => [session.id, session]))

   for (const update of updates) {
     const existing = next.get(update.id)
-    if (existing && isNewerSessionUpdate(existing, update)) {
+    if (existing && isStaleOrSameSessionUpdate(existing, update)) {
       continue
     }
🤖 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/stores/ui/session.ts` around lines 250 - 274, Rename
isNewerSessionUpdate to a predicate that clearly indicates the update is stale
or should be skipped, and update the corresponding guard in mergeSessions to use
the renamed symbol without changing its existing behavior: equal or older finite
timestamps must continue to be ignored.

394-452: 🚀 Performance & Scalability | 🔵 Trivial

Tombstone/revision maps grow unbounded for the store's lifetime.

removedSessionIds, sessionByIdRefreshRevisions, and targetedSessionCommitRevisions entries are added on every delete/refresh/targeted-commit but never pruned (only observedSessionStatuses.delete(sessionId) is cleaned up in removeSessions). The in-code comment explicitly accepts this as a lifetime tradeoff for correctness, but in a long-running renderer session with many created/deleted sessions over days, these collections will grow indefinitely.

Worth confirming this is an accepted tradeoff for the renderer's expected session lifetime, or considering a bounded eviction (e.g., cap size / evict oldest tombstones once no in-flight request could plausibly reference them).

🤖 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/stores/ui/session.ts` around lines 394 - 452, Bound the
lifetime of removedSessionIds, sessionByIdRefreshRevisions, and
targetedSessionCommitRevisions in the session store. Add pruning that evicts the
oldest entries once no in-flight list, per-ID refresh, or targeted-commit
operation can still reference them, while preserving tombstone and revision
checks for active requests and keeping removeSessions cleanup correct.
test/renderer/composables/useMessageVirtualization.test.ts (1)

164-185: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for layoutSegments.value resolving to null at runtime.

Test 3 only covers the "option omitted" fallback. useDisplayMessages.ts's layoutSegments computed can also emit null at runtime while the option is present (inline-stream reinsertion case) — that specific transition isn't exercised here, even though it's the most fragile new fallback path introduced by this PR.

🤖 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 `@test/renderer/composables/useMessageVirtualization.test.ts` around lines 164
- 185, Extend the useMessageVirtualization test suite to cover a provided
layoutSegments option whose computed value resolves to null at runtime,
mirroring the inline-stream reinsertion transition. Assert that
visibleDisplayMessages.value falls back to the complete displayMessages list,
while preserving the existing omitted-option fallback test.
test/renderer/components/SettingsApp.test.ts (1)

1483-1489: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Reset document.documentElement.lang/dir after this test.

The test mutates document.documentElement.lang/dir but doesn't restore them, unlike the sibling documentAppearance.test.ts which resets these in afterEach. If any later test in this file relies on default lang/dir, this could leak state across tests.

Suggested cleanup
     expect(configClientMock.getLanguage).not.toHaveBeenCalled()

     wrapper.unmount()
+    document.documentElement.lang = ''
+    document.documentElement.dir = ''
   })
🤖 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 `@test/renderer/components/SettingsApp.test.ts` around lines 1483 - 1489,
Restore document.documentElement.lang and dir to their default values after the
test covering the fa-IR locale, matching the cleanup pattern used by sibling
tests. Add the cleanup alongside the existing wrapper.unmount() flow without
changing the assertions.
🤖 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 `@test/renderer/components/ChatInputToolbar.test.ts`:
- Around line 131-146: Update the “renders progress and blocks repeated steer
clicks while pending” test for ChatInputToolbar to trigger a click on the steer
button and assert that no steer event is emitted, while preserving its existing
disabled, aria-busy, status, and icon assertions.

---

Nitpick comments:
In `@src/renderer/src/stores/ui/session.ts`:
- Around line 250-274: Rename isNewerSessionUpdate to a predicate that clearly
indicates the update is stale or should be skipped, and update the corresponding
guard in mergeSessions to use the renamed symbol without changing its existing
behavior: equal or older finite timestamps must continue to be ignored.
- Around line 394-452: Bound the lifetime of removedSessionIds,
sessionByIdRefreshRevisions, and targetedSessionCommitRevisions in the session
store. Add pruning that evicts the oldest entries once no in-flight list, per-ID
refresh, or targeted-commit operation can still reference them, while preserving
tombstone and revision checks for active requests and keeping removeSessions
cleanup correct.

In `@test/renderer/components/SettingsApp.test.ts`:
- Around line 1483-1489: Restore document.documentElement.lang and dir to their
default values after the test covering the fa-IR locale, matching the cleanup
pattern used by sibling tests. Add the cleanup alongside the existing
wrapper.unmount() flow without changing the assertions.

In `@test/renderer/composables/useMessageVirtualization.test.ts`:
- Around line 164-185: Extend the useMessageVirtualization test suite to cover a
provided layoutSegments option whose computed value resolves to null at runtime,
mirroring the inline-stream reinsertion transition. Assert that
visibleDisplayMessages.value falls back to the complete displayMessages list,
while preserving the existing omitted-option fallback test.

In `@test/renderer/features/chat-page/composables/useComposerSubmit.test.ts`:
- Around line 5-11: Extract the duplicated deferred-promise utilities into a
shared helper at test/renderer/utils/deferred.ts, including externally
controlled resolve and reject functions. In
test/renderer/features/chat-page/composables/useComposerSubmit.test.ts:5-11,
replace createDeferred with the shared helper; in
test/renderer/features/chat-page/composables/useMessageActions.test.ts:5-14,
replace deferred with the same helper and update usages as needed.
🪄 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: c616963f-ab08-4b28-96e9-c59a5b09a9f8

📥 Commits

Reviewing files that changed from the base of the PR and between 3464d91 and eef1e09.

📒 Files selected for processing (59)
  • docs/architecture/baselines/renderer-application-boundaries-baseline.json
  • docs/architecture/renderer-scope-optimization/plan.md
  • docs/architecture/renderer-scope-optimization/spec.md
  • docs/architecture/renderer-scope-optimization/tasks.md
  • docs/issues/chat-interrupt-actions-stall/spec.md
  • src/renderer/floating/main.ts
  • src/renderer/settings/App.vue
  • src/renderer/src/apps/chat-main/ChatMainApp.vue
  • src/renderer/src/apps/chat-main/ChatTabView.vue
  • src/renderer/src/components/WindowSideBar.vue
  • src/renderer/src/components/chat/ChatInputToolbar.vue
  • src/renderer/src/components/chat/MessageList.vue
  • src/renderer/src/components/chat/MessageListRow.vue
  • src/renderer/src/components/message/MessageItemAssistant.vue
  • src/renderer/src/composables/chat/chatScrollState.ts
  • src/renderer/src/composables/message/useMessageWindow.ts
  • src/renderer/src/features/chat-page/ChatPage.vue
  • src/renderer/src/features/chat-page/composables/useChatSearch.ts
  • src/renderer/src/features/chat-page/composables/useComposerSubmit.ts
  • src/renderer/src/features/chat-page/composables/useDisplayMessages.ts
  • src/renderer/src/features/chat-page/composables/useMessageActions.ts
  • src/renderer/src/features/chat-page/composables/useMessageVirtualization.ts
  • src/renderer/src/features/chat-page/composables/useToolInteraction.ts
  • src/renderer/src/foundation/appearance/documentAppearance.ts
  • src/renderer/src/lib/chatSearch.ts
  • src/renderer/src/pages/NewThreadPage.vue
  • src/renderer/src/router/index.ts
  • src/renderer/src/stores/language.ts
  • src/renderer/src/stores/theme.ts
  • src/renderer/src/stores/ui/draft.ts
  • src/renderer/src/stores/ui/message.ts
  • src/renderer/src/stores/ui/project.ts
  • src/renderer/src/stores/ui/session.ts
  • src/renderer/src/stores/ui/sessionIpc.ts
  • src/renderer/src/views/SettingsTabView.vue
  • test/renderer/components/App.startup.test.ts
  • test/renderer/components/ChatInputToolbar.test.ts
  • test/renderer/components/ChatPage.test.ts
  • test/renderer/components/ChatTabView.test.ts
  • test/renderer/components/MessageList.test.ts
  • test/renderer/components/SettingsApp.test.ts
  • test/renderer/components/WindowSideBar.test.ts
  • test/renderer/components/message/MessageItemAssistant.test.ts
  • test/renderer/composables/chat/chatScrollState.test.ts
  • test/renderer/composables/useMessageVirtualization.test.ts
  • test/renderer/composables/useMessageWindow.test.ts
  • test/renderer/features/chat-page/composables/useChatSearch.test.ts
  • test/renderer/features/chat-page/composables/useComposerSubmit.test.ts
  • test/renderer/features/chat-page/composables/useMessageActions.test.ts
  • test/renderer/features/chat-page/composables/useToolInteraction.test.ts
  • test/renderer/foundation/appearance/documentAppearance.test.ts
  • test/renderer/lib/chatSearch.test.ts
  • test/renderer/pages/NewThreadPage.test.ts
  • test/renderer/stores/languageStore.test.ts
  • test/renderer/stores/memoryActivityStore.test.ts
  • test/renderer/stores/messageStore.test.ts
  • test/renderer/stores/projectStore.test.ts
  • test/renderer/stores/sessionStore.test.ts
  • test/renderer/stores/themeStore.test.ts
💤 Files with no reviewable changes (2)
  • src/renderer/src/views/SettingsTabView.vue
  • src/renderer/src/stores/ui/draft.ts

Comment thread test/renderer/components/ChatInputToolbar.test.ts
…e-optimization

Resolve language/i18n conflicts to preserve this PR's scope-boundary race
fixes (languageInitialization promise cache, listener-before-snapshot
registration, documentAppearance abstraction) alongside the lazy renderer
locale loading (#2003) and dashboard CPU reduction (#2004) perf wins from dev.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/renderer/src/stores/language.ts (2)

59-67: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clear initialization cache when locale application fails.

applyLanguageState catches its own errors and returns a boolean rather than throwing. If loading locale messages fails, it returns false, which bypasses the catch block here. As a result, languageInitialization incorrectly retains a successfully resolved promise, preventing any future initialization retries.

Check the return value of applyLanguageState to accurately clear the initialization cache upon failure.

🐛 Proposed fix
     const initialization = (async () => {
       try {
         const languageState = await configClient.getLanguageState()
-        await applyLanguageState(languageState, revision)
+        const success = await applyLanguageState(languageState, revision)
+        if (!success) {
+          languageInitialization = null
+        }
       } catch (error) {
         languageInitialization = null
         console.error('初始化语言失败:', error)
       }
     })()
🤖 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/stores/language.ts` around lines 59 - 67, Update the
initialization flow around applyLanguageState to inspect its boolean result and
set languageInitialization to null when it returns false, while retaining the
existing catch handling for thrown errors and successful initialization
behavior.

83-85: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Avoid using component lifecycle hooks in Pinia setup stores.

Vue lifecycle hooks like onMounted are bound to the component that first invokes useLanguageStore(). If the store is first instantiated outside a component context (such as in a router navigation guard or a background service module), the onMounted hook will be silently ignored, and initLanguage will never execute.

Trigger initialization directly in the store setup to ensure reliable execution regardless of where the store is first used.

🐛 Proposed fix
-  onMounted(async () => {
-    await initLanguage()
-  })
+  void initLanguage()

(You can also remove onMounted from the Vue imports at the top of the file if it is no longer used).

🤖 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/stores/language.ts` around lines 83 - 85, Remove the
onMounted wrapper from the language store setup and invoke initLanguage directly
so initialization runs regardless of where useLanguageStore is first
instantiated. Remove the now-unused onMounted import while preserving the
existing async initialization behavior.
🧹 Nitpick comments (1)
src/renderer/src/stores/language.ts (1)

9-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared appearance helper to resolve document direction.

The RTL_LIST duplicates the logic already provided by resolveDocumentDirection. To fully align with the PR objective of using a shared appearance projection, remove RTL_LIST and use the exported resolveDocumentDirection helper.

♻️ Proposed refactor

Add the import at the top of the file:

import { resolveDocumentDirection } from '`@/foundation/appearance/documentAppearance`'

And update the code accordingly:

-const RTL_LIST = ['fa-IR', 'he-IL']
-
 export const useLanguageStore = defineStore('language', () => {
   const { locale, setLocaleMessage } = useI18n({ useScope: 'global' })
   const language = shallowRef<string>('system')
   const configClient = createConfigClient()
   const initialLocale = resolveSupportedLocale(locale.value)
-  const dir = shallowRef<'auto' | 'rtl'>(RTL_LIST.includes(initialLocale) ? 'rtl' : 'auto')
+  const dir = shallowRef<'auto' | 'rtl'>(resolveDocumentDirection(initialLocale))
   let transitionRevision = 0
   let updateRequestRevision = 0
   let removeLanguageListener: (() => void) | undefined
   let languageInitialization: Promise<void> | null = null
 
   const applyLanguageState = async (state: RendererLanguageState, revision: number) => {
     const resolvedLocale = resolveSupportedLocale(state.locale)
 
     try {
       const messages = await loadLocaleMessages(resolvedLocale)
       if (revision !== transitionRevision) return false
 
       setLocaleMessage(resolvedLocale, messages)
       locale.value = resolvedLocale
       language.value = state.requestedLanguage || 'system'
-      dir.value = state.direction === 'rtl' || RTL_LIST.includes(resolvedLocale) ? 'rtl' : 'auto'
+      dir.value = state.direction === 'rtl' || resolveDocumentDirection(resolvedLocale) === 'rtl' ? 'rtl' : 'auto'
       return true
🤖 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/stores/language.ts` around lines 9 - 33, Replace the local
RTL_LIST-based direction logic in useLanguageStore with the shared
resolveDocumentDirection helper, importing it from the appearance module. Use
the helper for both initial dir setup and the direction assignment in
applyLanguageState, then remove RTL_LIST while preserving the existing
resolved-locale and explicit state.direction behavior.
🤖 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.

Outside diff comments:
In `@src/renderer/src/stores/language.ts`:
- Around line 59-67: Update the initialization flow around applyLanguageState to
inspect its boolean result and set languageInitialization to null when it
returns false, while retaining the existing catch handling for thrown errors and
successful initialization behavior.
- Around line 83-85: Remove the onMounted wrapper from the language store setup
and invoke initLanguage directly so initialization runs regardless of where
useLanguageStore is first instantiated. Remove the now-unused onMounted import
while preserving the existing async initialization behavior.

---

Nitpick comments:
In `@src/renderer/src/stores/language.ts`:
- Around line 9-33: Replace the local RTL_LIST-based direction logic in
useLanguageStore with the shared resolveDocumentDirection helper, importing it
from the appearance module. Use the helper for both initial dir setup and the
direction assignment in applyLanguageState, then remove RTL_LIST while
preserving the existing resolved-locale and explicit state.direction behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d81600b2-b27f-4246-a3fa-6bcf33882046

📥 Commits

Reviewing files that changed from the base of the PR and between eef1e09 and ea824b3.

📒 Files selected for processing (3)
  • docs/architecture/baselines/renderer-application-boundaries-baseline.json
  • src/renderer/floating/main.ts
  • src/renderer/src/stores/language.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/architecture/baselines/renderer-application-boundaries-baseline.json

zhangmo8 added 2 commits July 20, 2026 20:38
Address coderabbit review comments on PR #2000:
- ChatInputToolbar.test: trigger steer click and assert no emission while
  pending (was only checking static attributes)
- session store: rename isNewerSessionUpdate -> isStaleOrSameSessionUpdate
  so the predicate name matches its "skip stale/equal update" semantics
- extract shared createDeferred helper (with reject) to test/renderer/utils,
  replacing three local copies in useComposerSubmit/useMessageActions/
  useToolInteraction tests
- useMessageVirtualization.test: cover layoutSegments provided but returning
  null at runtime (inline-stream reinsertion fallback)
- SettingsApp.test: reset document.documentElement lang/dir in afterEach to
  match sibling documentAppearance cleanup

@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 (1)
test/renderer/stores/floatingButtonStore.test.ts (1)

25-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate deferred helper — reuse the shared test utility.

This appears to duplicate test/renderer/utils/deferred.ts, which other renderer store tests in this PR already use for the identical pattern.

♻️ Suggested fix
-function deferred<T>() {
-  let resolve!: (value: T) => void
-  const promise = new Promise<T>((resolvePromise) => {
-    resolve = resolvePromise
-  })
-  return { promise, resolve }
-}
+import { deferred } from '../utils/deferred'
#!/bin/bash
# Confirm the shared deferred utility exists and matches this local implementation
fd deferred.ts test/renderer
cat -n test/renderer/utils/deferred.ts 2>/dev/null
🤖 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 `@test/renderer/stores/floatingButtonStore.test.ts` around lines 25 - 31,
Remove the local deferred helper from the floating button store test and import
and reuse the shared deferred utility from test/renderer/utils/deferred.ts,
updating its call sites as needed while preserving the existing test behavior.
🤖 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 `@docs/architecture/renderer-scope-optimization/spec.md`:
- Line 66: Update the Chinese sentence in the renderer-scope optimization
specification to use the clearer wording “不得覆盖用户在等待期间手动选择的新项目”, preserving the
original meaning.

---

Nitpick comments:
In `@test/renderer/stores/floatingButtonStore.test.ts`:
- Around line 25-31: Remove the local deferred helper from the floating button
store test and import and reuse the shared deferred utility from
test/renderer/utils/deferred.ts, updating its call sites as needed while
preserving the existing test behavior.
🪄 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: 754cdb1f-3a65-4a7c-8265-fc4939045818

📥 Commits

Reviewing files that changed from the base of the PR and between 1582106 and 2c6c685.

📒 Files selected for processing (10)
  • docs/architecture/renderer-scope-optimization/spec.md
  • docs/architecture/renderer-scope-optimization/tasks.md
  • src/renderer/src/apps/chat-main/ChatMainApp.vue
  • src/renderer/src/pages/NewThreadPage.vue
  • src/renderer/src/stores/floatingButton.ts
  • src/renderer/src/stores/language.ts
  • test/renderer/pages/NewThreadPage.test.ts
  • test/renderer/stores/floatingButtonStore.test.ts
  • test/renderer/stores/languageStore.test.ts
  • test/renderer/stores/projectStore.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/renderer/stores/projectStore.test.ts
  • src/renderer/src/stores/language.ts
  • test/renderer/pages/NewThreadPage.test.ts
  • src/renderer/src/apps/chat-main/ChatMainApp.vue

Comment thread docs/architecture/renderer-scope-optimization/spec.md Outdated
@zhangmo8
zhangmo8 marked this pull request as draft July 21, 2026 03:33
@zhangmo8
zhangmo8 marked this pull request as ready for review July 21, 2026 10:46

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/src/components/markdown/MarkdownRenderer.vue (1)

195-198: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Clear the cached search-results promise on failure

searchResultsPromise stays stuck on a rejected promise until customRendererId changes, so a transient failure can leave reference hover/click handling broken for the rest of the render cycle. Reset the cache on rejection and add a .catch() to the .then() chains so the rejection doesn’t bubble as unhandled.

🤖 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/markdown/MarkdownRenderer.vue` around lines 195 -
198, Update getSearchResults so a rejected sessionClient.getSearchResults
promise clears searchResultsPromise, allowing a later request to retry. Add
rejection handling to every .then() chain consuming this promise so failures are
handled and do not surface as unhandled rejections, while preserving successful
result processing.
🤖 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 `@resources/model-db/providers.json`:
- Around line 258751-258754: Update the meituan/longcat-2.0 provider entry’s
limit.context value from 1048756 to 1048576, while leaving its output limit
unchanged.

---

Outside diff comments:
In `@src/renderer/src/components/markdown/MarkdownRenderer.vue`:
- Around line 195-198: Update getSearchResults so a rejected
sessionClient.getSearchResults promise clears searchResultsPromise, allowing a
later request to retry. Add rejection handling to every .then() chain consuming
this promise so failures are handled and do not surface as unhandled rejections,
while preserving successful result processing.
🪄 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: 766311ad-8b4e-4ea4-bb6d-0dad93275785

📥 Commits

Reviewing files that changed from the base of the PR and between 2c6c685 and 0e53714.

📒 Files selected for processing (20)
  • docs/architecture/chat-markstream-rendering-pipeline/plan.md
  • docs/architecture/chat-markstream-rendering-pipeline/spec.md
  • docs/architecture/chat-markstream-rendering-pipeline/tasks.md
  • docs/issues/markstream-code-block-rendering/spec.md
  • package.json
  • resources/acp-registry/registry.json
  • resources/model-db/providers.json
  • src/renderer/src/components/markdown/MarkdownRenderer.vue
  • src/renderer/src/components/message/MessageBlockContent.vue
  • src/renderer/src/components/message/MessageItemAssistant.vue
  • src/renderer/src/features/chat-page/composables/useDisplayMessages.ts
  • src/renderer/src/stores/ui/message.ts
  • src/renderer/src/stores/ui/stream.ts
  • test/main/agent/deepchat/runtime/echo.test.ts
  • test/renderer/components/MarkdownRenderer.test.ts
  • test/renderer/components/MarkstreamDomContract.test.ts
  • test/renderer/components/message/MessageBlockContent.test.ts
  • test/renderer/components/message/MessageItemAssistant.test.ts
  • test/renderer/features/chat-page/composables/useDisplayMessages.test.ts
  • test/renderer/stores/messageStore.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/renderer/src/components/message/MessageItemAssistant.vue
  • src/renderer/src/features/chat-page/composables/useDisplayMessages.ts

Comment thread resources/model-db/providers.json
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