Skip to content

polish: UI refinements, file preview enhancements, and Command K search#336

Merged
reachrazamair merged 9 commits into
mainfrom
0.15.0-polish
Feb 11, 2026
Merged

polish: UI refinements, file preview enhancements, and Command K search#336
reachrazamair merged 9 commits into
mainfrom
0.15.0-polish

Conversation

@pedramamini
Copy link
Copy Markdown
Collaborator

@pedramamini pedramamini commented Feb 10, 2026

Summary

A collection of UI polish and feature enhancements for the 0.15.0 release:

  • File Preview Enhancements — "Open in Default App" button, Cmd+Shift+Up/Down file selection in the Right Bar, and automatic file change detection with a reload banner
  • Command K Search Actions — Search functionality added to Quick Actions modal for all panels (Left Bar, Main Window, Right Bar)
  • Auto Run Kill Button — Kill pill button with confirmation modal during Auto Run stopping state
  • Raw Markdown Mode FixCmd+E now correctly shows source markdown text instead of stripping formatting
  • File Search Filter Fix — Visible Files filter in fuzzy file search now respects the expanded folder state
  • Settings Modal Resize — Increased dimensions by 20% (650×600 → 780×720) for better readability
  • Test Updates — Updated tests for file preview polling behavior and batch processor recount logic

Changes

Area Files Description
File Preview FilePreview.tsx, MainPanel.tsx, RightPanel.tsx Open in Default App, Cmd+Shift+Up/Down selection, file change detection with reload banner
Command K QuickActionsModal.tsx, App.tsx Search actions for Left Bar, Main Window, and Right Bar panels
Auto Run AppModals.tsx, App.tsx Kill pill button with confirmation modal in stopping state
Markdown TerminalOutput.tsx Raw mode (Cmd+E) now preserves source text
File Search FileSearchModal.tsx Visible Files filter respects folder expansion state
Settings SettingsModal.tsx Modal dimensions increased 20%
Keyboard useMainKeyboardHandler.ts Cmd+Shift+Up/Down for file selection
State uiStore.ts Search focus actions for each panel
Tests 10 test files New + updated tests covering all changes

Commits

  • 776da736 feat: add file preview enhancements — Open in Default App button, Cmd+Shift+Up/Down selection, and file change detection with reload banner
  • 30eb565f fix: fuzzy file search Visible Files filter now respects expanded folder state
  • 972ba899 feat: add Search actions to Command K menu for all panels
  • 5231be0a feat: add Kill pill button to Auto Run stopping state with confirmation modal
  • 38273de2 fix: raw markdown mode (⌘E) now shows source text instead of stripping formatting
  • 1e7803b9 fix: update tests for file preview polling and batch processor recount behavior
  • d523273c fix: increase settings modal dimensions by 20% (650×600 → 780×720)

Test Plan

  • TypeScript lint passes (npm run lint)
  • ESLint passes (npm run lint:eslint)
  • All 18,621 tests pass (npm run test) — 434 test files, 0 failures
  • Manual: Verify Cmd+E shows raw markdown source in AI Terminal
  • Manual: Verify Cmd+Shift+Up/Down navigates file preview tabs
  • Manual: Verify Command K search actions focus correct panel search
  • Manual: Verify Kill button appears during Auto Run stopping state
  • Manual: Verify file change detection shows reload banner in file preview

@claude
Copy link
Copy Markdown

claude Bot commented Feb 10, 2026

Code Review - PR #336

This PR adds three useful file preview enhancements. The implementation is solid overall, with good test coverage and adherence to the codebase patterns. Here's my detailed review:


✅ Strengths

1. Excellent Test Coverage

  • 7 new tests for file change detection covering all critical paths (polling, reload, dismiss, unsaved edits warning)
  • Tests properly use vi.useFakeTimers() for polling verification
  • Good edge case coverage (no-poll guard when lastModified is undefined)

2. Clean Prop Threading

The new lastModified and onReloadFile props follow the established pattern through the component hierarchy:

App.tsx → useMainPanelProps → MainPanel → FilePreview

3. User Experience Considerations

  • Dismissible banner with clear Reload/Dismiss actions
  • Warning when unsaved edits exist
  • Polling stops when banner is shown (avoids repeated alerts)

⚠️ Issues & Recommendations

1. Polling Interval Memory Leak Risk (Minor)

Location: FilePreview.tsx:723-739

The polling effect has proper cleanup, but there's a subtle issue: when fileChangedOnDisk becomes true, the effect re-runs and creates a new interval that immediately returns. This is fine, but the dependency on fileChangedOnDisk could be removed since the early return prevents unnecessary work:

// Current
useEffect(() => {
  if (!file?.path || !lastModified || fileChangedOnDisk) return;
  // ... polling logic
}, [file?.path, lastModified, sshRemoteId, fileChangedOnDisk]);

// Suggestion: Remove fileChangedOnDisk from deps
useEffect(() => {
  if (!file?.path || !lastModified) return;
  
  const interval = setInterval(async () => {
    if (fileChangedOnDisk) return; // Check inside interval instead
    // ... rest of polling logic
  }, 3000);
  
  return () => clearInterval(interval);
}, [file?.path, lastModified, sshRemoteId]); // fileChangedOnDisk removed

However, the current approach works correctly and is more explicit about stopping the interval entirely. This is a style preference rather than a bug.


2. Error Handling in handleReloadFileTab (Minor)

Location: App.tsx:5483

The function silently catches errors with only a debug log:

} catch (error) {
  console.debug('[handleReloadFileTab] Failed to reload:', error);
}

Recommendation: Consider showing a user-facing error message when reload fails (e.g., file deleted, permissions changed). Users clicking "Reload" expect feedback:

} catch (error) {
  console.error('[handleReloadFileTab] Failed to reload:', error);
  // TODO: Show toast/notification to user that reload failed
  // E.g., "Failed to reload file: <reason>"
}

3. Race Condition in Polling (Very Minor)

Location: FilePreview.tsx:728

If the file is modified rapidly (within the 3s polling window), the banner might not appear until the next interval. This is expected behavior for polling, but worth noting in case users report "delayed" change detection.

Mitigation (optional): Could add file watchers via fs.watch() for instant detection, but that adds complexity. The 3-second polling is a reasonable tradeoff for simplicity.


4. shell.openExternal Security Consideration (Low Risk)

Location: FilePreview.tsx:1885

onClick={() => window.maestro?.shell?.openExternal(`file://${file.path}`)}

Concern: If file.path ever contains user-controlled input without validation, this could be a security risk. However, in this codebase, file paths come from trusted sources (file picker, session state), so this is low risk.

Best Practice: Consider URL encoding the path to handle special characters:

onClick={() => {
  const encodedPath = encodeURI(file.path);
  window.maestro?.shell?.openExternal(`file://${encodedPath}`);
}}

This prevents issues with paths containing spaces, special chars, etc.


5. Keyboard Shortcut Tests Missing Implementation Verification

Location: useMainKeyboardHandler.test.ts:1248-1383

The new tests for Cmd+Shift+Up/Down are testing the existing Cmd+E markdown toggle behavior, not the new selection shortcuts. The test suite title says "Cmd+E markdown toggle" but the PR description says these tests are for "Cmd+Shift+Up/Down selection".

Issue: I don't see the actual implementation of Cmd+Shift+Up/Down in useMainKeyboardHandler.ts. The only change in that file is removing the hasActiveFileTab check for Cmd+E.

Action Required: Either:

  1. The PR description is incorrect and these tests are for the Cmd+E fix (likely case)
  2. The Cmd+Shift+Up/Down implementation is missing from the diff

Looking at the diff again, I believe the PR description has a documentation error. The new tests are for the Cmd+E behavior change (allowing markdown toggle even when file tabs exist), not for new selection shortcuts.


🔍 Code Quality & Best Practices

✅ Follows Codebase Conventions

  • Uses tabs for indentation (verified in FilePreview.tsx)
  • Consistent with existing patterns (props threading, useCallback usage)
  • Good variable naming (fileChangedOnDisk, lastModifiedRef)

✅ Performance Considerations

  • Proper use of useCallback for event handlers
  • useRef for values that don't need to trigger re-renders
  • Polling interval is reasonable (3s) and cleanup is handled

✅ Accessibility

  • Buttons have proper title attributes
  • Icons have semantic meaning (ExternalLink, RefreshCw, X)

📋 Suggestions for Follow-up

  1. Add user-facing error handling for reload failures
  2. Clarify PR description - the keyboard shortcut tests appear to be for Cmd+E behavior, not Cmd+Shift+Up/Down
  3. Consider URL encoding for file paths in shell.openExternal
  4. Optional: Add integration test verifying the full reload flow (stat → banner → reload → content update)

🎯 Summary

Overall Assessment:Approved with minor suggestions

The implementation is well-tested, follows established patterns, and provides useful functionality. The issues identified are minor and don't block merging:

  • No critical bugs detected
  • Good test coverage (320 new test lines)
  • Security concerns are low-risk
  • Performance impact is minimal (3s polling is negligible)

Recommendation: Merge after addressing the PR description clarification (Cmd+Shift+Up/Down vs Cmd+E tests).


Great work on this feature! The file change detection will be particularly useful for users editing files in external editors.

@pedramamini pedramamini changed the title feat: file preview enhancements — open in default app, editor selection, disk change detection polish: UI refinements, file preview enhancements, and Command K search Feb 11, 2026
…+Shift+Up/Down selection, and file change detection with reload banner

- Add "Open in Default App" button to FilePreview toolbar using ExternalLink icon (consistent with tab context menu)
- Add Cmd+Shift+Up/Down keyboard shortcuts for selection in file editor mode
- Add file change detection: polls fs.stat every 3s for the active file tab, shows a dismissible banner with Reload button when file changes on disk
- Banner warns about unsaved edits when user has pending changes
- Wire handleReloadFileTab through App.tsx → useMainPanelProps → MainPanel → FilePreview
- Reload clears edit content and refreshes from disk with updated lastModified timestamp
…der state

Previously, "Visible Files" only excluded dotfiles, making the count nearly
identical to "All Files." Now it uses fileExplorerExpanded to show only files
in expanded folders, matching the file tree's actual visible state.
Add four new search-focused actions to the Quick Actions modal:
- Search: Agents — opens and focuses the sidebar session filter
- Search: Message History — opens the main panel output search
- Search: Files — opens the file explorer filter in the right panel
- Search: History — opens the history panel search in the right panel

Lifts sessionFilterOpen (SessionList) and searchFilterOpen (HistoryPanel)
from local component state to the Zustand uiStore so the Command K modal
can trigger them. Uses setTimeout(50ms) to ensure the modal's focus trap
releases before search inputs attempt to grab focus via autoFocus.
…on modal

When Auto Run is in the stopping state, a red "Kill" pill appears next to
the "STOPPING..." label. Clicking it opens a confirmation modal that warns
about mid-execution termination before calling process.kill on the session.
…g formatting

Previously, toggling to plain text mode called stripMarkdown() which
removed URLs from links, heading markers, bold/italic syntax, and code
fences — defeating the purpose of raw mode for copy-pasting.
…t behavior

Align test expectations with actual runtime behavior — FilePreview polling
test now accounts for initial mount stat call, and batch processor test
adjusts doc2 read threshold for the recount-all-documents phase. Also adds
missing Group type import in App.tsx.
@claude
Copy link
Copy Markdown

claude Bot commented Feb 11, 2026

Pull Request Review: UI Polish and Feature Enhancements

This is a well-crafted PR with comprehensive test coverage and thoughtful implementation. Here's my detailed feedback:


✅ Strengths

Code Quality

  • Excellent test coverage: All new features have thorough test cases (195+ lines for FilePreview tests alone)
  • Clean separation of concerns: State management properly lifted to Zustand store for cross-component coordination
  • Proper cleanup: Components like HistoryPanel correctly reset store state on unmount to prevent stale state
  • Consistent patterns: Follows existing codebase conventions (tabs for indentation, proper TypeScript usage)

Implementation Highlights

  1. File change detection (FilePreview.tsx:722-740): Smart polling implementation with proper cleanup and edge case handling
  2. Search actions architecture (QuickActionsModal.tsx): 50ms setTimeout to ensure modal's focus trap releases before search inputs grab focus - good UX detail
  3. Visible Files filter fix (FileSearchModal.tsx:168-182): Now correctly respects folder expansion state instead of just hiding dotfiles - much more intuitive

🔍 Issues & Concerns

1. Performance: File Change Detection Polling

Location: FilePreview.tsx:722-740

Issue: Every open file tab polls fs.stat every 3 seconds unconditionally.

Concerns:

  • Multiple file tabs = multiple concurrent polling intervals
  • No throttling/debouncing for rapid tab switching
  • Could cause performance issues with many tabs or on slow filesystems (especially SSH remote)
  • Polling continues even when the app is in the background

Recommendation:

// Consider:
// 1. Only poll when tab is visible/active
// 2. Use a global interval that checks all tabs once (not per-tab intervals)
// 3. Increase interval when app is backgrounded (via Page Visibility API)
// 4. Add debouncing for stat calls on slow remotes
if (\!file?.path || \!lastModified || fileChangedOnDisk || \!isTabMode || \!isActive) return;

2. Missing Type Import

Location: App.tsx:1070

The Group type import was added in the test fix commit but isn't used in the actual code - likely a merge artifact. Verify if this is needed.

3. Raw Markdown Mode: Terminology Confusion

Location: TerminalOutput.tsx:697-709

Issue: The fix changes behavior from "strip markdown" to "show raw source", but the variable is still named chatRawTextMode. This is confusing because:

  • "Raw text" implies stripped formatting (plain text)
  • Actual behavior now shows markdown syntax intact (raw source)

Recommendation:

// Consider renaming for clarity:
chatRawTextMode  chatShowMarkdownSource
// or
chatRawTextMode  chatRawMarkdownMode

4. Potential Race Condition in Search Actions

Location: QuickActionsModal.tsx:1527-1529

Issue: The 50ms setTimeout is hardcoded and might not be sufficient on slower systems or under heavy load.

Recommendation:

// Use requestAnimationFrame + setTimeout for more reliability:
requestAnimationFrame(() => {
  setTimeout(() => storeSetSessionFilterOpen(true), 50);
});
// Or use a slightly longer delay (100ms) to be safer

5. File Reload: Potential Data Loss

Location: FilePreview.tsx:2290-2294

Issue: When file has unsaved edits, the warning message is shown but reload is not prevented. Users might still accidentally click "Reload" and lose work.

Recommendation: Add an additional confirmation modal when hasChanges is true:

const handleReloadFile = useCallback(() => {
  if (hasChanges) {
    // Show ConfirmModal before reloading
    setShowReloadConfirm(true);
  } else {
    setFileChangedOnDisk(false);
    onReloadFile?.();
  }
}, [hasChanges, onReloadFile]);

6. Settings Modal Resize: Magic Numbers

Location: SettingsModal.tsx:1677

Issue: Dimensions changed from 650×600 to 780×720 (exactly 20% increase) but these are hardcoded magic numbers.

Recommendation: Extract to constants or use CSS variables for easier future adjustments:

const SETTINGS_MODAL_WIDTH = 780;
const SETTINGS_MODAL_HEIGHT = 720;

🔐 Security & Best Practices

1. Path Injection Risk (Low)

Location: FilePreview.tsx:1867

window.maestro?.shell?.openExternal(`file://${file.path}`)

Issue: If file.path contains malicious characters or URL schemes, this could potentially be exploited.

Recommendation: Validate/sanitize the path or use a dedicated IPC handler that validates paths server-side:

// In main process (IPC handler):
const resolvedPath = path.resolve(filePath);
if (\!resolvedPath.startsWith(expectedBasePath)) {
  throw new Error('Invalid file path');
}
shell.openExternal(`file://${resolvedPath}`);

2. Error Handling: Silent Failures

Location: FilePreview.tsx:734-736, App.tsx:1109-1111

Multiple catch blocks silently ignore errors with just a comment. Consider:

  • Logging to console.debug for debugging (already done in App.tsx)
  • Potentially showing a subtle notification for network errors on SSH remotes
  • Using Sentry integration mentioned in CLAUDE.md for unexpected errors

🎯 Performance Considerations

File Search Modal

Location: FileSearchModal.tsx:264-290

The visibleFiles memo depends on expandedFolders array, which might trigger unnecessary recomputation. Consider:

const expandedSet = useMemo(() => 
  expandedFolders ? new Set(expandedFolders) : null, 
  [expandedFolders]
);
const visibleFiles = useMemo(() => {
  if (\!expandedSet) return allFiles;
  return flattenPreviewableFiles(fileTree, '', 0, expandedSet);
}, [fileTree, expandedSet, allFiles]);

📝 Minor Suggestions

  1. Kill Button Accessibility: Consider adding keyboard support (Enter/Space) for the Kill pill button beyond just click
  2. File Change Banner: Could use window.requestIdleCallback for the polling interval to reduce impact during heavy user interaction
  3. Test Export: flattenPreviewableFiles is exported from FileSearchModal.tsx for testing - consider moving to a utility file for cleaner separation
  4. Cmd+Shift+Up/Down: Not implemented in this PR despite being mentioned in the PR description - is this intentional or overlooked?

✨ Positive Callouts

  1. Excellent commit messages: Clear, descriptive, properly formatted
  2. Test-driven approach: Tests updated alongside implementation
  3. Edge case handling: File deletion, network failures, permission issues all considered
  4. Documentation: PR description is thorough and well-organized
  5. Backward compatibility: Changes are purely additive, no breaking changes

🎯 Summary

Recommendation: Approve with minor fixes

This is high-quality work that significantly improves the user experience. The main concern is the file change detection polling performance - I'd recommend addressing that before merging, especially the multi-tab scenario. The other issues are nice-to-haves that could be addressed in follow-up PRs.

Priority Fixes:

  1. ✅ File change polling optimization (prevent multiple concurrent intervals)
  2. ⚠️ File reload confirmation when unsaved changes exist
  3. 💡 Consider adding telemetry for the new search actions to understand usage

Blocking: None - all issues are addressable post-merge if needed

Great work overall! 🎉

@claude
Copy link
Copy Markdown

claude Bot commented Feb 11, 2026

PR Review: UI Refinements & File Preview Enhancements

Summary

Overall, this is a well-executed polish PR with strong attention to detail. The code quality is high, test coverage is comprehensive, and the implementation follows the codebase's established patterns. All 18,621 tests passing is excellent.


✅ Strengths

Code Quality

  • Excellent test coverage: Every feature has comprehensive tests with edge cases (SSH sessions, error states, confirmation flows)
  • Consistent patterns: Uses established codebase patterns (uiStore for cross-component state, layer stack integration, proper cleanup)
  • Clean separation of concerns: File change detection polling lives in FilePreview, reload logic in App.tsx
  • Proper React hooks usage: Correct dependency arrays, cleanup in useEffect returns, refs for mutable values

Implementation Highlights

1. File Change Detection (FilePreview.tsx:712-746)

Good:

  • ✅ Proper cleanup of interval
  • ✅ Silent error handling (files can be deleted/inaccessible during polling)
  • ✅ Stops polling once change detected (avoids unnecessary work)
  • ✅ Uses ref for lastModified to avoid stale closure issues
  • ✅ SSH remote support via sshRemoteId

Consideration: 3-second polling interval is reasonable for file change detection, but if many file tabs are open simultaneously, this could create significant IPC traffic. Consider:

  • Adding a global debounce/throttle if multiple tabs are polling the same file
  • Using file watchers (fs.watch) instead of polling (more efficient, though SSH support would need investigation)

2. File Search Filter Fix (FileSearchModal.tsx:168-195)

The "Visible Files" filter now correctly respects folder expansion state. This is a significant UX improvement.

Before: "Visible Files" meant "not hidden" (files not starting with .)
After: "Visible Files" means "in expanded folders" (matches file explorer state)

Good:

  • ✅ Exported function for testing (excellent testability)
  • ✅ Backward compatible (expandedSet is optional)
  • ✅ Comprehensive test coverage in FileSearchModal.test.ts

3. Raw Markdown Mode Fix (TerminalOutput.tsx:740)

The fix correctly removes the stripMarkdown() call, showing raw source instead of processed text.

Before: stripMarkdown(filteredText) → "Heading" (# removed)
After: filteredText → "# Heading" (raw markdown visible)

This aligns with the feature's intent: Cmd+E should toggle between rendered and source view, not rendered and "stripped" view.

4. Command K Search Actions (QuickActionsModal.tsx:1034-1078)

Clean implementation using uiStore to coordinate across panels without prop drilling.

Good:

  • ✅ 50ms delay allows panel transitions to complete before focusing search
  • ✅ Consistent pattern across all 4 search actions
  • ✅ Test coverage includes timing behavior

Minor suggestion: The 50ms magic number appears 4 times. Consider extracting to a constant like PANEL_TRANSITION_DELAY_MS = 50

5. Auto Run Kill Button (RightPanel.tsx:617-630)

Proper confirmation flow with skull icon and destructive styling.

Good:

  • ✅ Only shown during isStopping state (prevents confusion)
  • ✅ Clear warning about incomplete changes
  • ✅ Destructive confirmation pattern (consistent with other dangerous operations)
  • ✅ Comprehensive test coverage (modal appearance, confirmation, cancellation)

🔍 Observations & Suggestions

1. Settings Modal Resize (SettingsModal.tsx:1690)

20% size increase (650×600 → 780×720)

Observation: Consider testing on 1366×768 displays (common laptop resolution). 720px height + window chrome could overflow on smaller screens.

2. File Preview Polling Performance

Current: Each open file tab polls independently every 3 seconds.

Potential optimization for future: Centralized file watcher service that shares polling/watching across all tabs viewing the same file. Not critical for this PR, but worth considering if users report performance issues.

3. Open in Default App Button (FilePreview.tsx:1877-1885)

Security consideration: File paths should be URL-encoded to handle special characters correctly:

Example edge case: /path/with spaces/file#name.txt → malformed URL without encoding

Also: Consider Windows path handling (C:\path\to\file.txt → file:///C:/path/to/file.txt)

4. Test Hygiene

The test cleanup pattern is excellent - resetting uiStore state in beforeEach and cleanup in useEffect returns. This prevents test pollution. Well done.


🐛 Potential Issues

1. File Reload Race Condition (App.tsx:5493)

Between reading the file and reading the stat, the file could be modified again. The stat.modifiedAt might be newer than the content you just read, leading to an immediate "file changed" notification after reload.

Suggested Fix: Read stat first, then content, then stat again to verify:

  • If mtimes differ, the file changed during read - retry or warn user

This is a rare edge case but could cause user confusion.

2. Missing Keyboard Handler Cleanup

In useMainKeyboardHandler.test.ts, verify that renderHook from @testing-library/react automatically cleans up event listeners between tests.


🔒 Security

File Path Injection

The shell.openExternal() call with user-controlled file paths is generally safe since it's from the local file tree, but:

  1. URL encoding needed (as mentioned above)
  2. Path validation recommended: Ensure file.path is a real absolute path
  3. Consider allowlisting schemes: Only allow file:// protocol

📊 Performance

Good Practices Observed:

  • ✅ useMemo for expensive computations (file flattening, filtering)
  • ✅ useCallback to prevent unnecessary re-renders
  • ✅ Polling stops when banner is dismissed or change detected
  • ✅ Tests use fake timers to avoid real delays

Considerations:

  • Poll interval (3s) is reasonable but could be configurable
  • Multiple file tabs polling simultaneously could add up
  • Consider using requestIdleCallback for non-critical polling

🎯 Test Coverage

Excellent coverage across the board:

  • FilePreview: 6 new test cases (polling, reload, dismiss, SSH exclusion, unsaved edits)
  • FileSearchModal: 8 new test cases (expansion logic, depth calculation)
  • QuickActionsModal: 6 new test cases (all 4 search actions + filtering)
  • RightPanel: 6 new test cases (kill button flow)
  • TerminalOutput: 8 updated test cases (raw markdown expectations)
  • useMainKeyboardHandler: 5 new test cases (Cmd+E with various states)

All tests properly:

  • Use fake timers for polling/delays
  • Mock Lucide icons to avoid render complexity
  • Reset store state between tests
  • Test both happy paths and edge cases

📝 Minor Code Quality Notes

  1. Consistent commenting: Good inline explanations (e.g., "Silently ignore — file may have been deleted")
  2. Type safety: All new functions have proper TypeScript types
  3. Accessibility: Buttons have proper title attributes for screen readers
  4. Naming: Clear, descriptive names (handleReloadFileTab, fileChangedOnDisk)
  5. DRY principle: Search action pattern could be slightly more DRY, but current duplication is acceptable for clarity

🎨 Adherence to CLAUDE.md Guidelines

Tabs for indentation - Maintained throughout
No unnecessary documentation - No README/MD files added
Scope discipline - Only touched relevant files, no drive-by refactoring
Error handling - Proper try/catch with silent handling where appropriate
Testing - Comprehensive coverage as required
No over-engineering - Simple, focused solutions
Platform awareness - SSH support properly handled (Open in Default App hidden for SSH)


🚀 Recommendations

Must Fix (before merge):

  1. URL encode file paths in "Open in Default App" button (security + correctness)
  2. Consider file reload race condition - add note or fix if deemed important

Should Consider (follow-up):

  1. Extract PANEL_TRANSITION_DELAY_MS = 50 constant
  2. Test Settings modal on 1366×768 displays
  3. Monitor file polling performance with many tabs (add metrics?)
  4. Consider file watchers instead of polling (follow-up optimization)

Nice to Have:

  1. Make poll interval configurable in settings
  2. Add keyboard shortcut hint to Kill button tooltip
  3. Consider visual indicator (icon/badge) on file tabs with detected changes

✨ Final Verdict

Approved with minor suggestions. This is high-quality work that demonstrates strong understanding of React best practices, the codebase architecture, and testing requirements. The features are well-implemented and the test coverage is exemplary.

Estimated Risk: Low - comprehensive tests, conservative changes, proper error handling

Recommendation: Merge after addressing the URL encoding concern in "Open in Default App". The reload race condition is worth noting but likely acceptable as-is for this release.

Great work! 🎉

…fileExplorerStore

Merge origin/main into 0.15.0-polish, resolving App.tsx import conflict
(removed unused Group and GroupChatMessage type imports). Migrated
QuickActionsModal's fileTreeFilterOpen usage from deprecated uiStore
to fileExplorerStore to align with main's refactored architecture.
@claude
Copy link
Copy Markdown

claude Bot commented Feb 11, 2026

Code Review: PR #336 - UI Polish & Feature Enhancements for 0.15.0

Thank you for this comprehensive set of UI improvements! Overall, this is a well-structured PR with excellent test coverage. Here's my detailed feedback:


Strengths

  1. Excellent Test Coverage

    • All new features have corresponding tests (FilePreview, QuickActionsModal, RightPanel, etc.)
    • Tests are well-structured and cover edge cases (SSH remote, error states, modal interactions)
    • Test descriptions are clear and match the actual behavior
  2. Clean Architecture

    • Proper use of Zustand stores (uiStore, fileExplorerStore) instead of threading props through multiple components
    • Good separation of concerns (e.g., flattenPreviewableFiles exported for testability)
    • Proper cleanup in useEffect hooks (HistoryPanel unmount cleanup)
  3. User Experience

    • File change detection with polling is a great feature
    • Kill button for stuck processes addresses a real pain point
    • Search actions in Command K improve discoverability
    • Raw markdown mode fix aligns with user expectations
  4. Code Quality

    • Consistent code style matching the project conventions (tabs for indentation ✓)
    • Good use of TypeScript typing
    • Proper keyboard event handling with stopPropagation

🔍 Issues & Concerns

1. Performance: File Polling Interval (Medium Priority)

Problem: 3-second polling for every active file tab can create performance issues:

  • Multiple file tabs = multiple simultaneous polling intervals
  • SSH remote calls are expensive (network latency)
  • No debouncing or throttling if multiple tabs exist

Location: src/renderer/components/FilePreview.tsx:1244

Recommendations:

  • Use a single shared polling service that all FilePreview instances subscribe to
  • Consider using filesystem watchers (via fs.watch) instead of polling for local files
  • For SSH remote files, increase interval to 5-10 seconds or make it configurable
  • Add a global setting to disable polling entirely

2. Error Handling: Silent Failures (Medium Priority)

Multiple locations swallow errors without user feedback at FilePreview.tsx:1251 and App.tsx:3799

Problem:

  • Users won't know why file reload failed (permissions, network issues, file deletion)
  • console.debug may not be visible in production builds
  • No way to recover from transient errors

Recommendations:

  • Show a toast notification on reload failures with the specific error
  • For polling errors, track consecutive failures and show a persistent warning after 3+ failures
  • Use captureException from Sentry utils for unexpected errors per CLAUDE.md guidelines

3. Race Condition: Reload During Edit (High Priority)

Location: src/renderer/App.tsx:3804

Problem:

  • editContent: undefined unconditionally clears edits, even if the user dismisses the banner
  • If reload happens while user is typing, their changes are lost
  • No confirmation modal despite the warning text

Recommendation:

  • Only clear editContent if the user explicitly clicks "Reload" (not when banner is dismissed)
  • Consider showing a confirmation modal when hasChanges is true (similar to the Kill confirmation modal)
  • Alternatively, add an "Apply Changes" button to merge user edits with disk changes

4. Memory Leak Potential: Polling Not Stopped (Low Priority)

Location: src/renderer/components/FilePreview.tsx:1244

Analysis:

  • If fileChangedOnDisk becomes true, the effect cleanup runs and stops polling
  • But if the user dismisses the banner, polling doesn't restart
  • This is actually correct behavior (polling stops after detection), but it's subtle and worth documenting

Recommendation:

  • Add a comment explaining this intentional behavior
  • Consider restarting polling after banner dismissal if users want continuous monitoring

5. UX Issue: "Open in Default App" for SSH (Low Priority)

Good: You correctly hide this button for SSH sessions (commit 0874a96).

Suggestion:

  • Consider showing a tooltip explaining why it's hidden: "Not available for remote files"
  • Or provide an alternative action like "Download & Open" that fetches the file first

6. Type Safety: Optional Chaining Overuse (Low Priority)

Location: src/renderer/components/FilePreview.tsx:1275

Observation:

  • If window.maestro.shell.openExternal is missing, the button click silently does nothing
  • User gets no feedback

Recommendation: Add error handling or disable the button if the API is unavailable


7. Zustand Store Cleanup Pattern (Low Priority)

Good: You correctly clean up store state on unmount in HistoryPanel.tsx:1452

Concern:

  • SessionList doesn't have similar cleanup (though it may not need it)
  • Consider documenting this pattern in CLAUDE-PATTERNS.md for consistency

8. Command K Search: setTimeout Magic Number (Low Priority)

Location: src/renderer/components/QuickActionsModal.tsx:1550

Problem:

  • 50ms delay is hardcoded without explanation
  • May be too short on slower devices or too long on fast ones

Recommendation:

  • Add a comment explaining why the delay is needed ("Wait for modal focus trap to release")
  • Or extract to a named constant: FOCUS_TRAP_RELEASE_DELAY = 50

🎯 Code Style & Conventions

Adheres to CLAUDE.md:

  • Uses tabs for indentation
  • Proper error handling patterns (mostly)
  • Good use of captureException would improve it further
  • Follows SSH remote execution awareness (hides button correctly)

Test Quality:

  • Tests are comprehensive and well-named
  • Good use of vi.useFakeTimers() for time-dependent tests
  • Proper cleanup with vi.useRealTimers()

🚀 Suggestions for Follow-up

  1. Add performance monitoring for file polling (track how many active pollers exist)
  2. Add user setting for polling interval or disable entirely
  3. Add Sentry error tracking for file reload failures
  4. Document the file change detection in CLAUDE-FEATURES.md
  5. Consider adding keyboard shortcut for "Reload file" action (e.g., Cmd+R when file tab is focused)

📊 Summary

Category Rating Notes
Code Quality ⭐⭐⭐⭐☆ Clean, well-structured, minor issues
Test Coverage ⭐⭐⭐⭐⭐ Excellent - all features tested
Performance ⭐⭐⭐☆☆ Polling approach needs optimization
Security ⭐⭐⭐⭐⭐ No concerns identified
UX ⭐⭐⭐⭐☆ Great features, minor edge cases
Adherence to Standards ⭐⭐⭐⭐☆ Follows CLAUDE.md well

Recommendation: Approve with Minor Revisions

This PR is well-executed and ready to merge with minor improvements:

Must Fix Before Merge:

  • Add error handling for file reload failures (show user-facing error messages)
  • Document the file polling behavior (why it stops after detection)

Nice to Have (can be follow-up PRs):

  • Optimize file polling (shared service or fs.watch)
  • Add confirmation modal for reload when edits exist
  • Extract magic numbers to named constants
  • Add Sentry error tracking

Great work on this feature set! The file change detection and Kill button are valuable additions that will improve the user experience significantly.


Reviewed by: Claude Code Agent
Review Date: 2026-02-11
Commits Reviewed: 776da73 through de372c6 (9 commits)

@reachrazamair reachrazamair merged commit d90b433 into main Feb 11, 2026
2 checks passed
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.

2 participants