Skip to content

fix(desktop): fetch latest file content when clicking already-open file#660

Merged
Kitenite merged 5 commits intomainfrom
fetch-latest-file-content-on-click
Jan 8, 2026
Merged

fix(desktop): fetch latest file content when clicking already-open file#660
Kitenite merged 5 commits intomainfrom
fetch-latest-file-content-on-click

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Jan 8, 2026

Summary

  • Fixed issue where clicking on a file in the changes sidebar didn't refresh content if the file was already open
  • Added contentVersion counter to FileViewerState that increments when clicking on an already-open file
  • Updated useFileContent hook to refetch content when contentVersion changes

Test plan

  • Open a file from the changes sidebar
  • Modify the file externally (e.g., in another editor or via terminal)
  • Click on the same file in the changes sidebar again
  • Verify the updated content is displayed

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • File preview panes are now automatically pinned and focused when reopening the same file, improving workspace organization
    • File content is automatically refreshed when files are opened to ensure you're always viewing the latest data
    • Enhanced file pane focus and state management for smoother navigation

✏️ Tip: You can customize this high-level summary in your review settings.

When clicking on a file in the changes sidebar that's already open in a pane,
the content wasn't refreshing because tRPC queries used cached data. Added a
contentVersion counter to FileViewerState that increments when clicking on
an already-open file, triggering a refetch of the file content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 8, 2026

Warning

Rate limit exceeded

@Kitenite has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 32 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 73b9989 and dee1309.

📒 Files selected for processing (4)
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/main/lib/api-client.ts
  • apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
📝 Walkthrough

Walkthrough

The changes enhance file-viewer behavior by automatically pinning reused preview panes when the same file is reopened, and improve sidebar file-open handlers to extract worktreePath from the active workspace and invalidate file content queries upon file access.

Changes

Cohort / File(s) Summary
Tabs Store Enhancement
apps/desktop/src/renderer/stores/tabs/store.ts
When reusing an unpinned preview file-viewer pane for the same file, the pane is now automatically pinned and focused.
Sidebar File Handler Improvements
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
Extracts worktreePath from activeWorkspace; adds trpc cache invalidation helper; updates handleFileOpen and handleFileOpenPinned to require both workspaceId and worktreePath; invalidates file content queries when files are opened; conditionally executes handlers only when worktreePath is available.

Sequence Diagram

sequenceDiagram
    participant User
    participant Sidebar
    participant TRPC as TRPC Utils
    participant TabsStore as Tabs Store

    User->>Sidebar: Click file to open
    Sidebar->>Sidebar: Extract worktreePath
    Sidebar->>TRPC: Invalidate file content cache
    TRPC-->>Sidebar: Cache invalidated
    Sidebar->>TabsStore: Add/reuse file-viewer pane
    alt Pane is reused & unpinned
        TabsStore->>TabsStore: Pin pane & focus
    end
    TabsStore-->>Sidebar: Pane updated
    Sidebar-->>User: File opened & displayed
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

Poem

🐰 A pane that's unpinned when files align,
Now pins itself with a hop so fine!
Cache invalidation—swift and clear,
Files open fresh, no stale data here!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description lacks critical sections from the template: no explicit Related Issues links, no Type of Change checkbox selection, incomplete Testing section, and missing Screenshots and Additional Notes sections. Fill in all required template sections: add GitHub issue links under Related Issues, select appropriate Type of Change checkbox (Bug fix), complete Testing section with actual test results, and add any additional context needed.
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 (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing the issue where clicking an already-open file doesn't fetch latest content.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts:
- Around line 72-85: The effect in useFileContent checks both contentVersion and
prevContentVersionRef.current, so when a new pane starts with contentVersion
undefined the first re-click increments to 1 but prev stays undefined and no
refetch occurs; fix this by initializing the pane's contentVersion to 0 when
creating file viewer panes (update the createFileViewerPane / pane creation
logic in store.ts where contentVersion is set) so subsequent increments produce
defined previous and current values and the existing effect in useFileContent
will trigger refetchRaw/refetchDiff as intended.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccf9cdb and 1ff9dfc.

📒 Files selected for processing (4)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/shared/tabs-types.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/shared/tabs-types.ts
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid using any type - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness

Files:

  • apps/desktop/src/shared/tabs-types.ts
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations - never use raw SQL

Files:

  • apps/desktop/src/shared/tabs-types.ts
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting and linting - run at root level with bun run lint:fix or biome check --write

Files:

  • apps/desktop/src/shared/tabs-types.ts
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Files:

  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

One component per file - do not create multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
🧬 Code graph analysis (1)
apps/desktop/src/renderer/stores/tabs/store.ts (1)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
  • set (162-172)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (6)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx (1)

79-79: LGTM!

The contentVersion is correctly extracted from the file viewer state and passed through to the useFileContent hook. This plumbing enables the content refetch mechanism.

Also applies to: 106-106

apps/desktop/src/shared/tabs-types.ts (1)

103-104: LGTM!

The contentVersion property is correctly defined as optional with clear documentation. The optional type aligns with the implementation where it starts undefined and increments to positive integers when the file is re-opened.

apps/desktop/src/renderer/stores/tabs/store.ts (2)

400-421: LGTM! Content version incremented correctly for pinned panes.

When focusing an already-open pinned file, the contentVersion is correctly incremented to trigger a content refetch. The increment logic (existingFileViewer.contentVersion ?? 0) + 1 properly handles both the initial case (undefined → 1) and subsequent increments.


449-469: LGTM! Content version incremented correctly for preview panes.

When clicking the same file that's already in a preview pane, the pane is correctly pinned and contentVersion is incremented to trigger a content refetch. The increment logic properly handles the undefined → 1 transition and subsequent increments.

apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/hooks/useFileContent/useFileContent.ts (2)

37-46: LGTM! Raw file query correctly separated with refetch capability.

The raw file query is properly separated and exposes the refetchRaw function, which is used by the content version effect.


48-66: LGTM! Diff query correctly separated with refetch capability.

The diff query is properly separated and exposes the refetchDiff function, which is used by the content version effect.

Kitenite and others added 2 commits January 7, 2026 16:46
Replace the contentVersion counter approach with React Query's built-in
cache invalidation mechanism. This is a cleaner pattern that:
- Doesn't pollute the data model with implementation details
- Uses React Query's native cache management
- Provides better separation of concerns

Changes:
- Export queryClient as singleton from TRPCProvider for store access
- Add worktreePath to AddFileViewerPaneOptions for query invalidation
- Remove contentVersion field from FileViewerState
- Remove contentVersion-related logic from useFileContent hook

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use trpc.useUtils() in the Sidebar component instead of exporting
queryClient as a singleton. This is the idiomatic React Query pattern:
- Store handles only state management
- Component handles side effects like query invalidation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
@apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx:
- Around line 14-25: The invalidateFileContent helper currently discards the
promises from trpcUtils.changes.readWorkingFile.invalidate and
trpcUtils.changes.getFileContents.invalidate, which can hide failures; update
invalidateFileContent to await or handle those promises and catch errors,
logging contextual details (including worktreePath and filePath and which
invalidate call failed) instead of using bare void; reference the
invalidateFileContent function and the two invalidate calls so you can wrap each
invalidate in a try/catch or use Promise.all(...).catch(...) and call the
appropriate logger (e.g., console.error or the app logger) with a clear message.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ff9dfc and 73b9989.

📒 Files selected for processing (2)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid using any type - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

One component per file - do not create multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations - never use raw SQL

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting and linting - run at root level with bun run lint:fix or biome check --write

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (4)
apps/desktop/src/renderer/stores/tabs/store.ts (1)

435-454: LGTM! Pinning behavior improves UX.

The logic correctly pins a preview pane when the same file is clicked again. This provides better UX by signaling that repeated clicks indicate intent to keep the file open permanently. The implementation properly updates both the isPinned state and the focused pane.

apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/index.tsx (3)

9-9: LGTM! Proper extraction of worktreePath.

The worktreePath is correctly extracted and used to conditionally enable file operations and cache invalidation.


28-40: LGTM! Proper conditional handler with cache invalidation.

The handler correctly guards against missing workspaceId or worktreePath, opens the file in preview mode, and invalidates the cache to ensure fresh content is fetched. The sequence is correct: state update followed by cache invalidation.


43-55: LGTM! Consistent implementation for pinned file handling.

The handler mirrors the preview mode handler but correctly opens files as pinned (isPinned: true). Cache invalidation ensures fresh content is fetched for both pinned and preview modes.

Kitenite and others added 2 commits January 7, 2026 17:18
Replace bare void with Promise.all().catch() to properly handle
potential failures from invalidate calls. Logs worktreePath, filePath,
and error details for debugging.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Kitenite Kitenite merged commit 320c5d8 into main Jan 8, 2026
5 checks passed
@Kitenite Kitenite deleted the fetch-latest-file-content-on-click branch January 8, 2026 01:24
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 8, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

saddlepaddle pushed a commit that referenced this pull request Jan 10, 2026
…le (#660)

* fix(desktop): fetch latest file content when clicking already-open file

When clicking on a file in the changes sidebar that's already open in a pane,
the content wasn't refreshing because tRPC queries used cached data. Now will invalidate when clicking once again.
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