Skip to content

fix (desktop): make app go brr#659

Merged
AviPeltz merged 1 commit intomainfrom
why-app-slow
Jan 8, 2026
Merged

fix (desktop): make app go brr#659
AviPeltz merged 1 commit intomainfrom
why-app-slow

Conversation

@AviPeltz
Copy link
Copy Markdown
Collaborator

@AviPeltz AviPeltz commented Jan 7, 2026

Description

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes

Summary by CodeRabbit

Release Notes

  • Documentation

    • Added input lag analysis and performance fix documentation.
  • Performance Improvements

    • Optimized component rendering to reduce unnecessary re-renders.
    • Debounced workspace title input for smoother typing responsiveness.
    • Debounced terminal directory navigation updates to improve performance.
  • Chores

    • Removed inactive MCP server configurations from settings.

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

The PR removes configured MCP servers from .mcp.json, adds performance documentation, and refactors desktop UI components to use granular store selectors instead of passing entire pane objects as props. Debouncing is added to workspace title and terminal directory updates to reduce excessive re-renders.

Changes

Cohort / File(s) Summary
Configuration
\.mcp\.json
Removed neon and morph-warp-grep MCP server configurations; mcpServers now empty.
Documentation
apps/desktop/docs/INPUT_LAG_FIXES\.md
New file documenting root causes and four implemented fixes for desktop app input lag (granular selectors, selective prop passing, CWD/title debouncing) with testing steps.
Debouncing optimizations
apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal\.tsx
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal\.tsx
NewWorkspaceModal: added localTitle state with 150ms debounce to title updates; uses localTitle for derived calculations. Terminal: debounced CWD store updates (150ms) and replaced coarse pane access with granular selectors.
Store selector refactoring
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/TabPane\.tsx
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index\.tsx
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index\.tsx
Removed pane prop from components; FileViewerPane and TabPane now derive pane data via granular store selectors (fileViewer, cwd, cwdConfirmed). TabView no longer receives panes prop; derives tabPanes from layout and global store using memoization; passes only paneId to child components. TabsContent simplified to not retrieve/pass panes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Granular selectors hop so spry,
Debounce delays the input sigh,
No panes passed around in props,
Store subscriptions never stop! ✨
Lag fixed with a selective bounds—
Optimized renders, swifter rounds!

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely empty—only the template structure remains with no filled-in sections for description, related issues, type of change, testing, or additional context. Complete the PR description template with clear explanations of changes, link related issues, select the appropriate type of change, document testing steps, and add relevant context.
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.
Title check ❓ Inconclusive The title is vague and uses informal phrasing ('make app go brr') that doesn't clearly convey the specific technical changes made (input lag fixes, debouncing, granular selectors, etc.). Revise the title to be more specific and descriptive, such as 'fix(desktop): optimize performance with debouncing and granular selectors' or similar.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 7, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

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

Thank you for your contribution! 🎉

@AviPeltz AviPeltz marked this pull request as ready for review January 8, 2026 01:09
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: 0

🧹 Nitpick comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx (1)

37-37: Consider more selective store subscription to fully realize performance gains.

While the refactor correctly derives pane data internally, the subscription pattern may limit performance benefits:

Line 37 subscribes to the entire panes object, meaning any pane update in any tab triggers this component to re-render and the tabPanes memo (lines 55-64) to recompute. Additionally, the memo creates a new object each time, so tabPanes will have a new reference even if the content is identical, causing the renderPane callback (line 162) to be recreated and potentially triggering Mosaic re-renders.

For a workspace with many active panes/tabs, this could result in unnecessary work.

Potential optimization approaches

Option 1: Use Zustand's shallow equality or a custom equality function:

+import { shallow } from 'zustand/shallow';

 const tabPanes = useTabsStore(
   (s) => {
     const result: Record<string, { tabId: string; type: string }> = {};
     for (const paneId of layoutPaneIds) {
       const pane = s.panes[paneId];
       if (pane?.tabId === tab.id) {
         result[paneId] = { tabId: pane.tabId, type: pane.type };
       }
     }
     return result;
   },
+  shallow
 );

Note: This approach still subscribes broadly but compares the result to avoid unnecessary updates.

Option 2: Consider a derived selector in the store that pre-computes panes-by-tab to avoid repeated filtering across all tab components.

Also applies to: 49-64, 162-162

📜 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 9152eab.

📒 Files selected for processing (8)
  • .mcp.json
  • apps/desktop/docs/INPUT_LAG_FIXES.md
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • 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/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
🧰 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/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
  • 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/index.tsx
**/*.{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/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
  • 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/index.tsx
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/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
  • 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/index.tsx
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
  • 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/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/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
  • 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/index.tsx
**/*.{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/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
  • 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/index.tsx
🧠 Learnings (2)
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/{ui,ai-elements,react-flow}/*.tsx : Use kebab-case single files for shadcn/ui components (e.g., button.tsx, base-node.tsx) in src/components/ui/, src/components/ai-elements, and src/components/react-flow/

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
🧬 Code graph analysis (3)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx (2)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx (1)
  • TabView (26-194)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/EmptyTabView.tsx (1)
  • EmptyTabView (5-36)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx (1)
apps/desktop/src/renderer/stores/theme/index.ts (1)
  • useTerminalTheme (4-4)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx (2)
apps/desktop/src/renderer/stores/tabs/types.ts (1)
  • Tab (18-20)
apps/desktop/src/renderer/stores/tabs/utils.ts (1)
  • extractPaneIdsFromLayout (54-65)
🔇 Additional comments (12)
apps/desktop/docs/INPUT_LAG_FIXES.md (1)

1-149: Excellent documentation of performance improvements!

This document clearly explains the root causes of input lag and the implemented fixes. The before/after code examples and testing guidance are particularly helpful for understanding the changes.

Minor suggestion: Consider adding metrics or benchmarks (if available) to quantify the improvement, such as "reduced re-renders by X%" or "typing latency improved from Yms to Zms."

apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx (3)

31-32: LGTM! Granular selector reduces unnecessary re-renders.

The change from subscribing to all panes to selecting only the specific pane data is a key optimization that prevents this Terminal from re-rendering when other panes change.


51-54: LGTM! Focused pane selector is now tab-specific.

This granular selector only subscribes to the focused pane ID for this terminal's tab, avoiding re-renders from focus changes in other tabs.


161-183: LGTM! Debounced CWD updates reduce store write frequency.

The implementation correctly:

  1. Debounces CWD updates with a 150ms delay to batch rapid directory changes
  2. Properly cleans up the debounced function on unmount to prevent memory leaks
  3. Maintains the correct behavior while reducing store updates
apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx (3)

66-95: LGTM! Debounced title input with immediate local state.

This implementation provides an excellent user experience:

  1. localTitle updates immediately for responsive typing
  2. debouncedSetTitle batches updates to title (used for expensive derived calculations)
  3. Proper cleanup prevents memory leaks

The 150ms debounce delay is appropriate for this use case.


196-197: Good defensive coding using localTitle for submission.

Using localTitle instead of title ensures that if the user submits the form before the debounce fires (e.g., by pressing Enter quickly), the latest input value is captured. This prevents data loss in edge cases.


293-294: LGTM! UI correctly bound to localTitle for immediate feedback.

All UI elements that display or derive from the title correctly use localTitle, ensuring users see their input immediately without lag.

Also applies to: 297-301, 331-332

apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/index.tsx (1)

39-39: LGTM! Removed panes prop to reduce re-render scope.

This change aligns with the documented performance improvements in INPUT_LAG_FIXES.md. By removing the panes prop, TabsContent no longer re-renders when any pane changes. Instead, TabView internally selects only the specific pane data it needs, significantly reducing unnecessary re-renders.

.mcp.json (1)

2-2: The claim that "neon" and "morph-warp-grep" MCP servers were removed is incorrect. The .mcp.json file was added (not modified) in this commit with an empty mcpServers: {} object. There is no evidence these MCP servers ever existed in the repository—no previous version of the file is in git history, and searching the codebase finds no references to them as MCP configurations. The "neon" references in the codebase are unrelated (they refer to the @neondatabase/serverless database package), and "morph-warp-grep" does not appear anywhere.

If the goal was to initialize .mcp.json as part of this commit, that's unrelated to the performance improvements documented in INPUT_LAG_FIXES.md. No action is required here.

Likely an incorrect or invalid review comment.

apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/TabPane.tsx (1)

59-61: Excellent use of granular selectors for performance.

The refactor from passing the entire pane object to using targeted zustand selectors (s.panes[paneId]?.cwd and s.panes[paneId]?.cwdConfirmed) ensures this component only re-renders when these specific properties change, not when unrelated pane data updates. This aligns well with the PR's performance goals.

Also applies to: 101-102

apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx (1)

58-59: Well-implemented granular selector pattern.

Consistent with the performance improvements in TabPane.tsx, this refactor uses s.panes[paneId]?.fileViewer to subscribe only to the relevant file viewer data rather than the entire pane object. The defensive programming with fallbacks (lines 70-77) and the guard clause (line 139) properly handle edge cases.

Also applies to: 70-77, 139-139

apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx (1)

101-160: Clean pane rendering logic with proper type discrimination.

The renderPane function correctly uses the minimal paneInfo to determine pane type and route to the appropriate component. Both FileViewerPane and TabPane receive only paneId and derive their own data via granular selectors, maintaining the performance optimization pattern established in those components.

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