Summary
After PR #103 (VS Code session support), tab switching feels slightly slower than before, especially Sessions → Projects. Confirmed by comparing yarn start (PR branch) vs installed v1.0.66 (pre-PR) after a clean macOS restart.
Symptoms
- Sessions → Projects tab switch: noticeably slower (most visible)
- Projects → Sessions: possibly slower but less certain
- Overall UI feels slightly less responsive during session tab interactions
Root Cause Analysis
1. Increased React re-renders per fetchClaudeSessions() call
Priority: High | Confidence: High
Each fetchClaudeSessions() triggers ~12-15 state updates (vs ~8 pre-PR):
| State update |
Pre-PR |
Post-PR |
Source |
setAllSessions (Step 1) |
1 |
1 |
Initial load |
setAssistantResponses (Step 2) |
1 |
1 |
loadLastAssistantResponses |
setAllSessions + setSessions (Step 3) |
1 |
1 |
Active session merge |
setTerminalApps (Step 3) |
1 |
1 |
Terminal detection |
setAssistantResponses (Step 3 VS Code) |
0 |
1 |
Pre-loaded VS Code responses |
setAllSessions + setSessions (Step 5) |
0 |
1 |
Closed VS Code merge |
setAssistantResponses (Step 5) |
0 |
1 |
Pre-loaded closed responses |
setCustomTitles + setBranches + setPrLinks (Step 4) |
1 |
1 |
CLI enrichment |
setCustomTitles + setBranches + setPrLinks (VS Code enrichment) |
0 |
1 |
VS Code enrichment |
Each state update triggers a re-render of ~100 session items.
Possible fixes:
- Batch state updates using
React.unstable_batchedUpdates or flushSync
- Combine multiple state variables into a single state object (reduces render count)
- Use
React.memo on session item components to skip unchanged items
- Move VS Code session state (assistantResponses for VS Code) into the same update as the merge
2. Background async tasks still running when switching tabs
Priority: Medium | Confidence: Medium
When the user switches Sessions → Projects, background tasks from fetchClaudeSessions() may still be completing:
loadLastAssistantResponses (100 sessions × tail -n 100 | grep) — ~150ms
scanClosedVSCodeSessions (218 files × 4KB read + VS Code sessions × head/tail/grep) — ~100ms
loadSessionEnrichment (100+ sessions × 4 grep each) — ~200ms
These spawn child processes that consume CPU even after leaving the Sessions tab.
Possible fixes:
- Cancel pending async tasks on tab switch (abort controller pattern)
- Lower priority for enrichment loads (requestIdleCallback)
- Reduce process spawning by reading files in Node.js instead of shell commands
3. Closed VS Code JSONL scan (synchronous file reads)
Priority: Medium | Confidence: Medium
scanClosedVSCodeSessions() synchronously reads first 4KB of each JSONL file to check entrypoint:
for (const f of files) {
const fd = fs.openSync(filePath, 'r');
fs.readSync(fd, buf, 0, 4096, 0);
fs.closeSync(fd);
}
This blocks the main Electron process for ~50ms (218 files). While short, it blocks IPC and window management during that time.
Possible fixes:
- Use async file reads (
fs.promises.open + read)
- Move to a worker thread
- Increase cache TTL from 30s to 60-120s (reduce scan frequency)
4. fs.watch debounce may be too short
Priority: Low | Confidence: Medium
Current debounce is 50ms. Log analysis showed events still firing 3-4 times per status change. Each fire reads all status files + sends IPC.
Possible fixes:
- Increase debounce to 100-200ms
- Track last-processed timestamp to skip redundant reads
5. Session item rendering complexity
Priority: Low | Confidence: Low
Each session item now renders:
Highlighter component for project name, custom title, branch, terminal badge, PR badge (5 instances even with empty search)
- PR badge with URL match detection
- Status dot with animation
Pre-PR each item had fewer Highlighter instances and no PR URL matching.
Possible fixes:
- Skip Highlighter instantiation when search is empty (return plain text)
- Memoize session items with
React.memo
- Virtualize the list (only render visible items)
Quantified Performance: Shell Exec vs Native Reads
Current approach spawns ~530 child processes per fetchClaudeSessions() call. Replacing exec('tail | grep') with direct file reads eliminates process spawn overhead.
| Operation |
Shell exec (current) |
Node.js fs.readFile |
Rust native module |
| 100 sessions assistant msg |
~150ms, 100 processes |
~25ms, 0 processes |
~5ms |
| 100 sessions enrichment |
~200ms, 400 processes |
~30ms, 0 processes |
~8ms |
| VS Code active (10 sessions) |
~50ms, 30 processes |
~10ms, 0 processes |
~2ms |
| JSONL scan (218 files) |
~50ms, 0 processes |
~50ms (same) |
~15ms |
| Closed VS Code reads |
~30ms, 30 processes |
~5ms, 0 processes |
~1ms |
| refreshSessionPreview (idle) |
~10ms, 1-2 processes |
~2ms |
~0.5ms |
| Total per refresh |
~490ms, ~530 processes |
~120ms, 0 processes |
~30ms |
| Improvement |
baseline |
~4x faster |
~16x faster |
Recommendation: Node.js native reads first (4x, low effort), Rust later if needed (16x, high effort + napi-rs build toolchain).
Recommended Fix Order
- Replace shell exec with Node.js file reads — highest impact, moderate effort (~4x faster, eliminates ~530 process spawns)
- Batch React state updates — high impact, moderate effort
- Skip Highlighter when no search — low effort, minor impact
- Increase fs.watch debounce to 150ms — trivial change
- Increase closed scan cache TTL — trivial change
- Cancel async tasks on tab switch — moderate effort, medium impact
- Virtual list — high effort, high impact for large lists (future)
- Rust native module — high effort, highest impact (future, if Node.js reads insufficient)
Related
🤖 On behalf of @grimmerk — generated with Claude Code
Summary
After PR #103 (VS Code session support), tab switching feels slightly slower than before, especially Sessions → Projects. Confirmed by comparing
yarn start(PR branch) vs installed v1.0.66 (pre-PR) after a clean macOS restart.Symptoms
Root Cause Analysis
1. Increased React re-renders per
fetchClaudeSessions()callPriority: High | Confidence: High
Each
fetchClaudeSessions()triggers ~12-15 state updates (vs ~8 pre-PR):setAllSessions(Step 1)setAssistantResponses(Step 2)setAllSessions+setSessions(Step 3)setTerminalApps(Step 3)setAssistantResponses(Step 3 VS Code)setAllSessions+setSessions(Step 5)setAssistantResponses(Step 5)setCustomTitles+setBranches+setPrLinks(Step 4)setCustomTitles+setBranches+setPrLinks(VS Code enrichment)Each state update triggers a re-render of ~100 session items.
Possible fixes:
React.unstable_batchedUpdatesorflushSyncReact.memoon session item components to skip unchanged items2. Background async tasks still running when switching tabs
Priority: Medium | Confidence: Medium
When the user switches Sessions → Projects, background tasks from
fetchClaudeSessions()may still be completing:loadLastAssistantResponses(100 sessions ×tail -n 100 | grep) — ~150msscanClosedVSCodeSessions(218 files × 4KB read + VS Code sessions × head/tail/grep) — ~100msloadSessionEnrichment(100+ sessions × 4 grep each) — ~200msThese spawn child processes that consume CPU even after leaving the Sessions tab.
Possible fixes:
3. Closed VS Code JSONL scan (synchronous file reads)
Priority: Medium | Confidence: Medium
scanClosedVSCodeSessions()synchronously reads first 4KB of each JSONL file to check entrypoint:This blocks the main Electron process for ~50ms (218 files). While short, it blocks IPC and window management during that time.
Possible fixes:
fs.promises.open+read)4. fs.watch debounce may be too short
Priority: Low | Confidence: Medium
Current debounce is 50ms. Log analysis showed events still firing 3-4 times per status change. Each fire reads all status files + sends IPC.
Possible fixes:
5. Session item rendering complexity
Priority: Low | Confidence: Low
Each session item now renders:
Highlightercomponent for project name, custom title, branch, terminal badge, PR badge (5 instances even with empty search)Pre-PR each item had fewer Highlighter instances and no PR URL matching.
Possible fixes:
React.memoQuantified Performance: Shell Exec vs Native Reads
Current approach spawns ~530 child processes per
fetchClaudeSessions()call. Replacingexec('tail | grep')with direct file reads eliminates process spawn overhead.Recommendation: Node.js native reads first (4x, low effort), Rust later if needed (16x, high effort + napi-rs build toolchain).
Recommended Fix Order
Related
🤖 On behalf of @grimmerk — generated with Claude Code