Skip to content

fix(desktop): make addFileViewerPane respect file open mode setting#1565

Merged
Kitenite merged 1 commit into
mainfrom
kitenite/fix/cmd-p-respect-tab-split-setting
Feb 18, 2026
Merged

fix(desktop): make addFileViewerPane respect file open mode setting#1565
Kitenite merged 1 commit into
mainfrom
kitenite/fix/cmd-p-respect-tab-split-setting

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Feb 18, 2026

Summary

  • CMD+P (and other file-open callers) always opened files as split panes, ignoring the user's "File open mode" setting
  • Centralized the tab-vs-split logic inside addFileViewerPane so it reads user config by default when openInNewTab is not explicitly passed

Changes

  • useFileOpenMode.ts — Added getFileOpenMode() imperative getter backed by a module-level cache, so the Zustand store can read the setting synchronously
  • tabs/store.tsaddFileViewerPane now defaults openInNewTab from user config via getFileOpenMode() when not explicitly set
  • page.tsx (workspace) — Calls useFileOpenMode() to keep the cache warm while a workspace is mounted
  • Removed redundant useFileOpenMode() + openInNewTab passing from FilesView.tsx, useFileLinkClick.ts, RightSidebar/index.tsx

Test Plan

  • Set file open mode to "New tab" in Settings > Features, open a file via CMD+P — should open in a new tab
  • Set file open mode to "Split pane", open a file via CMD+P — should open as a split
  • Verify file clicks in the sidebar and terminal links also respect the setting
  • Verify that explicitly passing openInNewTab still overrides the default

Summary by CodeRabbit

Refactor

  • Enhanced file open mode initialization by moving cache warming earlier in the application lifecycle and implementing module-level caching.
  • Consolidated file opening behavior logic across components, removing redundant hook invocations from multiple UI paths.
  • Simplified dependencies and cleaned up imports in related components.
  • Optimized control flow for file viewer pane operations with improved consistency.

Centralize the file open mode (tab vs split) logic in addFileViewerPane
so CMD+P and all other callers automatically respect the user's setting
without having to pass openInNewTab explicitly.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

This PR introduces a module-level cache for the file open mode preference and refactors component-level hook usage to use a centralized getter function. The cache is initialized when the workspace page loads, and the tabs store now uses the cached value as a fallback when openInNewTab is not explicitly provided.

Changes

Cohort / File(s) Summary
Import Formatting
apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
Reformatted branch utility imports to single-line format; no functional change.
Hook Export & Cache
apps/desktop/src/renderer/hooks/useFileOpenMode/index.ts, apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts
Added getFileOpenMode() getter export and introduced module-level cache initialized to DEFAULT_FILE_OPEN_MODE with cache updates in the hook.
Cache Initialization
apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx
Added useFileOpenMode hook invocation to warm the cache on workspace page load without affecting rendering.
Hook Removal from Components
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx, apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx, apps/desktop/src/renderer/screens/main/components/WorkspaceView/.../Terminal/hooks/useFileLinkClick.ts
Removed useFileOpenMode hook usage and openInNewTab parameter from addFileViewerPane calls, simplifying dependency arrays.
Cache Integration
apps/desktop/src/renderer/stores/tabs/store.ts
Added fallback logic to default openInNewTab to true when undefined and getFileOpenMode() returns "new-tab".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A cache is born, so swift and clean,
No more hooks scattered in between!
The mode now whispers once, then stays,
Guiding opens through the maze.
With panes aligned, we hop with glee,
Fast and cozy, wild and free!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main fix: making addFileViewerPane respect the file open mode setting.
Description check ✅ Passed The PR description follows the template with summary, changes, and test plan; all key required sections are present and comprehensive.

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

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kitenite/fix/cmd-p-respect-tab-split-setting

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.

🧹 Nitpick comments (2)
apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts (2)

5-10: Stale cache window at startup — design trade-off to document

cachedFileOpenMode initializes to DEFAULT_FILE_OPEN_MODE and only updates once useFileOpenMode() renders and the tRPC query resolves. Any addFileViewerPane call fired in that window uses the default mode, ignoring the user's setting.

Since this goes through IPC (Electron main-process local-db read), the gap is small but non-zero. A user with "New tab" configured who opens a file immediately on cold start (e.g., via a notification or restored session) would momentarily see split-pane behavior.

The cache-warming in WorkspacePage mitigates this for the typical flow. Consider adding a brief doc comment to getFileOpenMode acknowledging the startup race so future maintainers don't overlook it.

📝 Suggested doc addition
-/** Non-React getter, kept in sync by useFileOpenMode(). */
+/**
+ * Non-React getter for the current file-open mode, kept in sync by useFileOpenMode().
+ *
+ * Returns DEFAULT_FILE_OPEN_MODE until useFileOpenMode() has been called and
+ * its query has resolved (cache warm-up). Callers should be aware of this
+ * startup window.
+ */
 export function getFileOpenMode(): FileOpenMode {
 	return cachedFileOpenMode;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts` around
lines 5 - 10, Add a short doc comment above cachedFileOpenMode/getFileOpenMode
explaining that cachedFileOpenMode initializes to DEFAULT_FILE_OPEN_MODE and may
be stale until useFileOpenMode() runs and the tRPC IPC read resolves (causing a
small startup race where callers like addFileViewerPane may see the default
mode); note that WorkspacePage cache-warming mitigates the common path and call
out this trade-off for future maintainers so they don’t inadvertently rely on
the getter being fully warm at cold start. Reference cachedFileOpenMode,
DEFAULT_FILE_OPEN_MODE, getFileOpenMode(), useFileOpenMode(), and WorkspacePage
in the comment.

14-16: Render-phase mutation of module-level state — optional useEffect refactor

cachedFileOpenMode = mode is a side effect inside the render body, which violates React's render-purity expectation. In React 19 Concurrent Mode, renders can be discarded or re-invoked (Strict Mode double-render) before commit.

That said, this particular mutation is idempotent (same query state → same mode), so it won't cause observable issues in practice. Be aware of the real trade-off: moving to useEffect makes the code more React-correct but delays the cache update until after the commit phase, meaning getFileOpenMode() can return a stale value for the window between render and effect execution.

♻️ React-idiomatic alternative (with timing caveat)
-export function useFileOpenMode(): FileOpenMode {
-	const { data } = electronTrpc.settings.getFileOpenMode.useQuery();
-	const mode = data ?? DEFAULT_FILE_OPEN_MODE;
-	cachedFileOpenMode = mode;
-	return mode;
-}
+export function useFileOpenMode(): FileOpenMode {
+	const { data } = electronTrpc.settings.getFileOpenMode.useQuery();
+	const mode = data ?? DEFAULT_FILE_OPEN_MODE;
+	useEffect(() => {
+		cachedFileOpenMode = mode;
+	}, [mode]);
+	return mode;
+}

Note: the synchronous assignment in the current code means getFileOpenMode() is accurate immediately after the render that produced the new mode, whereas useEffect would leave a brief window after render where the cache lags. If synchronous accuracy is required (e.g., the store calls getFileOpenMode() in the same tick as a file open action), the current approach is actually preferable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts` around
lines 14 - 16, The render is mutating module-level cachedFileOpenMode inside
useFileOpenMode (the line `cachedFileOpenMode = mode`), which should be moved
out of render; update useFileOpenMode to compute const mode = data ??
DEFAULT_FILE_OPEN_MODE and then perform the assignment inside a useEffect that
runs when mode changes (e.g., useEffect(() => { cachedFileOpenMode = mode },
[mode])); keep the same default fallback (DEFAULT_FILE_OPEN_MODE) and ensure any
callers using getFileOpenMode are aware of the possible micro-timing window
where the cache lags after render.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/desktop/src/renderer/hooks/useFileOpenMode/useFileOpenMode.ts`:
- Around line 5-10: Add a short doc comment above
cachedFileOpenMode/getFileOpenMode explaining that cachedFileOpenMode
initializes to DEFAULT_FILE_OPEN_MODE and may be stale until useFileOpenMode()
runs and the tRPC IPC read resolves (causing a small startup race where callers
like addFileViewerPane may see the default mode); note that WorkspacePage
cache-warming mitigates the common path and call out this trade-off for future
maintainers so they don’t inadvertently rely on the getter being fully warm at
cold start. Reference cachedFileOpenMode, DEFAULT_FILE_OPEN_MODE,
getFileOpenMode(), useFileOpenMode(), and WorkspacePage in the comment.
- Around line 14-16: The render is mutating module-level cachedFileOpenMode
inside useFileOpenMode (the line `cachedFileOpenMode = mode`), which should be
moved out of render; update useFileOpenMode to compute const mode = data ??
DEFAULT_FILE_OPEN_MODE and then perform the assignment inside a useEffect that
runs when mode changes (e.g., useEffect(() => { cachedFileOpenMode = mode },
[mode])); keep the same default fallback (DEFAULT_FILE_OPEN_MODE) and ensure any
callers using getFileOpenMode are aware of the possible micro-timing window
where the cache lags after render.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 18, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app
  • ✅ Streams Fly.io app

Thank you for your contribution! 🎉

@Kitenite Kitenite merged commit 8eac336 into main Feb 18, 2026
15 checks passed
@Kitenite Kitenite deleted the kitenite/fix/cmd-p-respect-tab-split-setting branch February 18, 2026 19:46
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