Conversation
Phase 1 of file preview tabs feature: - Add FilePreviewTab interface with id, path, name, extension, scrollTop, searchQuery, editMode, editContent, and createdAt fields - Add UnifiedTabRef type for tab ordering across types - Add filePreviewTabs, activeFileTabId, and unifiedTabOrder to Session - Update all 13 session creation locations with new fields - Update session restoration to hydrate new fields from persistence This establishes the data model for coexisting AI tabs, file preview tabs, and future terminal tabs in a unified tab bar.
Creates a memoized unifiedTabs array that combines aiTabs and filePreviewTabs according to the session's unifiedTabOrder. Uses a discriminated union type to allow components to render the appropriate content for each tab type (AI or file preview).
Implements the handler to open file preview tabs in the unified tab system: - Checks if a tab with the same file path already exists and selects it - Creates new FilePreviewTab with proper extension parsing - Adds tab to both filePreviewTabs and unifiedTabOrder arrays - Sets activeFileTabId to activate the new file tab
Adds handleSelectFileTab handler that sets the activeFileTabId to select a file preview tab. The activeTabId is preserved (not nullified) following the established pattern from handleOpenFileTab, which tracks the last active AI tab for when the user switches back.
Modify setActiveTab function to deselect any active file preview tab when an AI tab is selected. This ensures only one tab type (AI or file) is active at a time. Updated early-return condition to also check if activeFileTabId is null before returning unchanged session.
Implements unified tab reorder functionality that operates on the unifiedTabOrder array, allowing both AI and file preview tabs to be reordered relative to each other. This supplements the existing handleTabReorder (AI-only) for the unified tab system. Changes: - Added handleUnifiedTabReorder in App.tsx with index validation - Propagated through useMainPanelProps.ts, MainPanel.tsx, TabBar.tsx
Updated handleCloseOtherTabs, handleCloseTabsLeft, and handleCloseTabsRight to support both AI and file preview tabs based on unifiedTabOrder position: - Determine active tab from activeFileTabId or activeTabId - Use unifiedTabOrder to determine which tabs are left/right/other - Close AI tabs using closeTab helper (with wizard history handling) - Close file tabs by removing from filePreviewTabs and unifiedTabOrder
…b support Implements handleCloseCurrentTab that: - Determines which tab is active (file tab via activeFileTabId first, then AI tab via activeTabId) - Closes file tabs immediately without wizard confirmation - For AI tabs, returns info to allow keyboard handler to show wizard confirmation - Prevents closing the last AI tab (keeps at least one AI tab) Updated keyboard handler (Cmd+W) to use handleCloseCurrentTab for unified tab support.
- Add UnifiedTab discriminated union type to types/index.ts - Add new props to TabBarProps: unifiedTabs, activeFileTabId, onFileTabSelect, onFileTabClose - Props are optional for backwards compatibility during transition - Phase 3 of file preview tabs implementation
- Create FileTabProps interface with file tab specific props - Implement FileTab memoized component with: - Filename without extension as label - Color-coded extension badge (blue for TS/JS, green for MD, etc.) - Unsaved edits indicator (pencil icon) - Middle-click to close support - Drag and drop handlers - Same active/inactive styling as AI tabs - Add getExtensionColor() helper for extension badge coloring
Verified that the FileTab extension badge implementation is complete: - Positioned after filename - 10px font size (text-[10px]) - Rounded corners (rounded class) - Color-coding by file type via getExtensionColor() helper - Tests to be added after Task 4 (unified tab rendering) is complete
- Add displayedUnifiedTabs computed value with unread filter support - Update render loop to conditionally render from unified tabs when provided - Check unified tab type to render either Tab (AI) or FileTab (file) - Update handleDrop, handleMoveToFirst, handleMoveToLast for unified tabs - Update overflow check and empty state to consider unified tabs - Maintain backwards compatibility with legacy AI-only tab rendering
Implements file-specific overlay menu for FileTab component: - Add shell:showItemInFolder IPC handler for "Reveal in Finder" action - Add showItemInFolder to shell preload API and global.d.ts types - Extend FileTabProps with position and close action callbacks - Implement full overlay menu with file actions: - Copy File Path/Name to clipboard - Open in Default App via file:// URL - Reveal in Finder via new showItemInFolder API - Move to First/Last Position - Close Tab/Other/Left/Right actions - Add 10 tests for file tab overlay menu - Add 3 tests for shell:showItemInFolder handler
Add 12 comprehensive tests for unified tabs drag-and-drop reordering: - AI tab to file tab drag-and-drop calls onUnifiedTabReorder - File tab to AI tab drag-and-drop calls onUnifiedTabReorder - File tab to file tab drag-and-drop works correctly - Dropping on same tab does not trigger reorder - Drag over visual feedback (ring-2 class) on target tab - Falls back to legacy onTabReorder when unifiedTabs not provided - Move to First/Last shown for file tabs not at edges - Move to First hidden for first tab - Move to Last hidden for last tab - Move to First click calls onUnifiedTabReorder - Move to Last click calls onUnifiedTabReorder - Middle-click closes file tabs
Verify that both AI tabs and file tabs use identical active indicator styling (bright background connecting to content area). The visual difference is correctly the extension badge, not the active state. Added 3 tests: - applies same active styling to both AI tabs and file tabs - applies same inactive styling to both AI tabs and file tabs - file tab displays extension badge with file extension text
- Verified FileTab's handleMouseDown correctly handles button === 1 (middle-click) - Added test: left-click does NOT close file tab - Added test: right-click does NOT close file tab - Added test: middle-click on AI tab still works in unified mode - All 115 TabBar tests pass
Connect FilePreviewTab rendering to the unified tab system: - Add unified tab props to MainPanel (unifiedTabs, activeFileTabId, etc.) - Remove !previewFile condition so TabBar always renders in AI mode - Update content area to prioritize activeFileTabId over legacy previewFile - Add showCloseButton prop to FilePreview (false when rendered as tab) - Add activeFileTab computation in App.tsx - Connect handlers through useMainPanelProps hook
Store file content directly on FilePreviewTab (Option A) for simplicity. File previews are typically small, and we already store larger AI logs. Updated handleOpenFileTab to populate content when opening file tabs.
- Fix MainPanel to use activeFileTab.content as source (was using empty editContent) - Add sshRemoteId and isLoading fields to FilePreviewTab interface - Update handleOpenFileTab to accept optional sshRemoteId parameter - Add handleOpenFileTabAsync for SSH files with async loading: - Creates tab immediately with loading state - Fetches content asynchronously - Updates tab when content loaded (or removes on error) - Add loading state UI in MainPanel for file tabs - Add tests for file tab content storage and SSH loading support This enables proper file content management for the unified tab system, with support for SSH remote files that need async loading.
- Add `lastModified: number` field to FilePreviewTab interface to track when content was loaded from disk - Add `fileTabAutoRefreshEnabled` setting (default: disabled) to control whether file tabs auto-refresh when switched to - Update handleOpenFileTab to accept and store lastModified - Update handleOpenFileTabAsync to fetch file stat and set lastModified from the file's actual modification time - Modify handleSelectFileTab to check if file changed on disk when auto-refresh is enabled: - Compares current file mtime with stored lastModified - Refreshes content if file was modified since last load - Skips refresh if tab has unsaved edits to prevent data loss - Update all FilePreviewTab test fixtures with lastModified field
- Renamed handleCloseFileTab to forceCloseFileTab (performs unconditional close) - Added new handleCloseFileTab wrapper that checks for unsaved changes - When tab.editContent !== undefined, shows confirmation modal before closing - Uses existing showConfirmation pattern with setConfirmModal* state - Modal displays: "<filename>" has unsaved changes. Are you sure you want to close it? - On confirm, calls forceCloseFileTab to perform actual close
- Added initialScrollTop and onScrollPositionChange props to FilePreview - Implemented throttled scroll position reporting (200ms) in content scroll handler - Added scroll position restoration with requestAnimationFrame for DOM readiness - Created handleFileTabScrollPositionChange handler in App.tsx - Wired handler through useMainPanelProps to MainPanel - Added 4 tests for scroll position persistence behavior
- Add initialSearchQuery and onSearchQueryChange props to FilePreview - Auto-open search panel when initialSearchQuery is provided and non-empty - Add handleFileTabSearchQueryChange handler in App.tsx - Wire search state through useMainPanelProps and MainPanel - Add 4 tests for search state persistence in FilePreview.test.tsx
Update keyboard shortcuts to navigate through unified tab order (both AI and file tabs) instead of just AI tabs: - Add navigateToUnifiedTabByIndex() for Cmd+1-9 tab jumping - Add navigateToLastUnifiedTab() for Cmd+0 (last tab) - Functions handle AI tabs (clear activeFileTabId) and file tabs (preserve activeTabId for easy switching back) - Update useMainKeyboardHandler to use new unified functions - Add comprehensive test coverage (18 new tests)
Add unified closed tab history system that tracks both AI and file preview tabs, enabling Cmd+Shift+T to restore any recently closed tab: - Add ClosedTabEntry discriminated union type for AI/file tabs - Add unifiedClosedTabHistory to Session (runtime-only, not persisted) - Add closeFileTab() function with unified history support - Add addAiTabToUnifiedHistory() for AI tab close tracking - Add reopenUnifiedClosedTab() with duplicate detection for both types - Update closeTab() to also update unifiedTabOrder - Update forceCloseFileTab() to use helper and add to history - Update performTabClose() to track AI tabs in unified history - Add comprehensive test coverage (12 new tests)
- Create navigateToNextUnifiedTab() and navigateToPrevUnifiedTab() functions that cycle through unifiedTabOrder (both AI and file tabs) - Update keyboard handler to use new unified navigation functions - Support showUnreadOnly filter (AI tabs filtered, file tabs always navigable) - Add comprehensive test coverage (20 new tests)
Updated TabSwitcherModal to display both AI and file preview tabs: - Added fileTabs, activeFileTabId, and onFileTabSelect props - File tabs show in "Open Tabs" mode with AI tabs, sorted alphabetically - Each file tab displays: filename, extension badge with type-specific coloring, file path, "File" indicator, active/unsaved indicators - Search works across file names, extensions, and paths - Updated AppModals and App.tsx to pass file tabs and handlers - Added 9 new tests (total 85 tests pass)
Added 17 new tests for keyboard shortcuts working with unified tab order: - Cmd+W: Tests file tab closing when active, AI tab closing when no file tab active, and last AI tab protection - Cmd+Shift+[/]: Tests navigation through unified tab order including file tabs, with showUnreadOnly filter support - Cmd+1-9: Tests jumping to specific positions in unified tab order, including file tabs, and showUnreadOnly filter blocking - Cmd+0: Tests jumping to last tab in unified order - Cmd+Shift+T: Tests reopening from unified closed tab history - Tests tab shortcuts are disabled in group chat and terminal mode All 36 tests in useMainKeyboardHandler.test.ts pass (was 19).
Updated handleFileClick in useAppHandlers.ts to use the new tab-based file preview system. When onOpenFileTab callback is provided, file clicks now open tabs instead of using the legacy setPreviewFile overlay. Changes: - Added FileTabInfo interface and onOpenFileTab optional callback to UseAppHandlersDeps - Modified handleFileClick to fetch file stat for lastModified timestamp - handleFileClick now calls onOpenFileTab when provided, falling back to legacy setPreviewFile behavior otherwise - Moved handleOpenFileTab definition in App.tsx to before useAppHandlers call and wired it as the onOpenFileTab callback
- Created comprehensive SessionContext.test.tsx with 15 unit tests - Verifies each session maintains independent file preview tabs - Tests session switching updates visible file tabs correctly - Verifies switching back restores scroll position, search query, edit mode - Tests active file tab ID is tracked per-session - Verifies rapid session switching preserves state
Verify that the file tab system correctly allows the same file to be open in multiple sessions simultaneously. Each session maintains its own independent file tab state (scroll position, search query, edit mode) even when viewing the same file path. Tests added: - allows the same file path to be open in different sessions simultaneously - each session maintains independent state for the same file - sessions can have different number of tabs for the same files
…sion badges - Fix max-width truncation inconsistency: file tabs now use max-w-[120px] to match AI tabs - Add theme-aware hover background: dark mode (rgba(255,255,255,0.08)), light mode (rgba(0,0,0,0.06)) - Enhance getExtensionColor() with theme-aware colors for both light and dark modes: - TypeScript/JavaScript, Markdown, JSON, CSS, HTML with contrast-appropriate colors - Add support for Python (teal), Rust (red), Go (cyan), Shell (gray) - Unknown extensions fall back to theme border/textDim colors - Add 10 unit tests verifying extension badge colors across themes
Use Wong's colorblind-safe palette for file extension badges when colorBlindMode is enabled. Colors are distinguishable across protanopia, deuteranopia, and tritanopia color vision deficiencies. - Add COLORBLIND_EXTENSION_PALETTE to colorblindPalettes.ts - Update getExtensionColor() in TabBar and TabSwitcherModal - Pass colorBlindMode through App → MainPanel → TabBar - Add 11 unit tests for colorblind extension badge colors
- Add comprehensive "File Preview Tab System" section to ARCHITECTURE.md documenting features, interfaces, unified tab system, session fields, behavior, extension color mapping, and key files - Update CLAUDE-SESSION.md with FilePreviewTab interface and session fields - Add file preview tab modification entry to CLAUDE.md Key Files table
Added 7 comprehensive performance-focused unit tests for the file tab system: - Renders 15 file tabs without performance issues - Renders 30 file tabs with mixed AI tabs (interleaved) - Selects file tab correctly among many tabs - Closes file tab correctly among many tabs - Supports drag and drop reorder with many file tabs - Renders file tabs with different extensions correctly - Maintains active tab styling among many tabs Verified existing performance optimizations: - React.memo() wrapping on Tab and FileTab components - useMemo() for computed values (display names, styles, extension colors) - useCallback() for all event handlers - Large file content truncation (100KB limit for syntax highlighting)
- Add isGeneratingName property to AITab interface - Show Loader2 spinner in tab while name generation is in progress - Set isGeneratingName true before API call, false on completion/error - Spinner only shows when automatic tab naming is enabled
Implements file-specific overlay menu for FileTab component: - Add shell:showItemInFolder IPC handler for "Reveal in Finder" action - Add showItemInFolder to shell preload API and global.d.ts types - Extend FileTabProps with position and close action callbacks - Implement full overlay menu with file actions: - Copy File Path/Name to clipboard - Open in Default App via file:// URL - Reveal in Finder via new showItemInFolder API - Move to First/Last Position - Close Tab/Other/Left/Right actions - Add 10 tests for file tab overlay menu - Add 3 tests for shell:showItemInFolder handler
- Fix dual border issue where both AI tab and file tab showed active styling when file tab was selected. AI tabs now only show border when activeFileTabId is null. - Update extension badge styling: remove leading dot, uppercase letters, smaller font (9px), reduced padding for minimal height impact. - Update tests to expect new uppercase extension badge format (e.g., 'TS' instead of '.ts').
- Fix new tab creation (Cmd+T) not appearing in unified tab bar by adding new AI tabs to unifiedTabOrder in createTab() - Fix new AI tab not being displayed when file tab was active by clearing activeFileTabId in createTab() - Fix AI tab isActive check to handle undefined activeFileTabId (use !activeFileTabId instead of === null) - Remove redundant filename/path header from file tab context menu (info already shown in tab and file preview subheading) - Use JavaScript toUpperCase() for extension badges instead of CSS text-transform to ensure DOM content matches displayed text - Update tests to use 'Copy File Path' selector instead of removed file-text-icon for identifying file tab overlays
Relocate the log level setting to be more appropriately placed in General settings, positioned above the GitHub CLI Path setting.
The issue was caused by legacy overlay behavior in FilePreview component: - useClickOutside hook was calling onClose when clicking outside the preview - Layer registration was blocking lower layers and capturing focus Added isTabMode prop to FilePreview to distinguish tab-based rendering: - When isTabMode=true: disable click-outside-to-close behavior - When isTabMode=true: don't block lower layers or aggressively capture focus - When isTabMode=true: set focusTrap to 'none' and allowClickOutside to true File preview tabs now persist until explicitly closed by: - Clicking the X button on the tab - Using Cmd+W keyboard shortcut - Middle-clicking the tab - Using context menu "Close Tab" Updated tests to verify click-outside is disabled in tab mode.
Use theme colors to create vibrant, readable diagrams instead of washed-out defaults. Nodes now have tinted backgrounds with prominent accent-colored borders, and different node types use distinct colors (accent, success, warning) for visual variety. - Add blendColors() and transparentize() helpers for color mixing - Use accent color for primary nodes, success for secondary, warning for tertiary - Connection lines use accent color instead of dim text - Enhanced styling for flowcharts, sequence diagrams, Gantt charts, pie charts, ER diagrams, git graphs, quadrant charts, and Sankey diagrams - Better edge label backgrounds for readability - Extended pie chart palette to 12 distinct colors
Tab management shortcuts (Cmd+T new tab, Cmd+W close tab) now work when viewing a file preview tab. Previously these shortcuts were blocked because file preview registers as an overlay in the layer stack. Added these to the allowlist of shortcuts that work when overlays (but not modals) are open.
…tration Tab behavior changes: - Escape key no longer closes file tabs (only closes internal UI like search and TOC overlay). Use Cmd+W or close button to close tabs. - File preview in tab mode skips layer stack registration entirely since tabs are main content, not overlays. This prevents tabs from intercepting keyboard shortcuts or participating in overlay logic. Updated tests to verify tab-mode Escape behavior.
Added isTabSwitcherShortcut to the overlay allowlist so users can open the tab switcher modal while viewing a file preview tab.
Add onWheel stopPropagation to TOC overlay container so mouse wheel events over the ToC scroll only the ToC, not the underlying file content. Also update tests to reflect the previous change where ToC stays open when clicking items (dismiss via click outside or Escape).
Extended isTabManagementShortcut to include Cmd+Shift+T for reopening closed tabs when overlays are open. The closeFileTab helper already adds file tabs to unifiedClosedTabHistory, and reopenUnifiedClosedTab handles restoring both AI and file tabs. This ensures Cmd+Shift+T works from file preview and other overlay contexts.
Added global image cache to MarkdownImage component to prevent re-fetching images on re-renders, which was causing scrollbar flickering. Key changes: - Global imageCache Map stores loaded images with TTL (10 min) - Images are cached by resolved path, including dimensions - Loading placeholder has min-height/min-width to reduce layout shift - Loaded images use aspectRatio from cached dimensions - onLoad handler captures and caches natural dimensions This eliminates the flickering/scrollbar jumping when viewing markdown files with embedded images.
Added overscrollBehavior: 'contain' to both the main content container and the TOC overlay's scrollable entries section. Also added onWheel stopPropagation to the TOC entries div to fully prevent scroll events from leaking between the ToC overlay and the main file preview content. This prevents scroll chaining that could cause scrollbar flickering.
Regular click on file links replaces current tab content, while Cmd/Ctrl+Click opens a new tab adjacent to the current tab.
Replace backdrop-based click-outside detection with useClickOutside hook. The previous fixed backdrop div intercepted all pointer events including wheel events, preventing file content from scrolling while ToC was open. Now wheel events over file content scroll the content, while wheel events over the ToC scroll the ToC list.
- Added FilePreviewHistoryEntry type to track navigation history per tab - Extended FilePreviewTab with navigationHistory and navigationIndex fields - Updated handleOpenFileTab to build navigation history when replacing content - Added handleFileTabNavigateBack/Forward/ToIndex handlers for per-tab nav - Wired navigation props through MainPanel to FilePreview component - Each file tab maintains its own independent navigation history
After saving a file in the file preview tab, the UI was reverting to the original content despite showing "Saved". This occurred because: - editContent was cleared to undefined after save - The base content field was never updated to the saved value - UI fell back to stale original content Added savedContent parameter to handleFileTabEditContentChange to update the tab's base content alongside clearing editContent after save.
Pull Request Review: In-Tab File Preview (#281)OverviewThis is a substantial feature addition that implements a unified tab system allowing AI tabs and file preview tabs to coexist in the same tab bar. The implementation is comprehensive, well-tested, and follows the project's architectural patterns. Summary: ✅ Approved with minor suggestions Code Quality: ⭐⭐⭐⭐⭐ (Excellent)Strengths
Potential Issues & Suggestions🟡 Minor: Memory ManagementIssue: File content is stored directly on FilePreviewTab export interface FilePreviewTab {
// ...
content: string; // Could be large for big files
editContent: string | undefined; // Duplicates content when editing
}Location: Suggestion: Consider implementing:
Severity: Low - Acceptable for typical usage, but could be an issue with many large files open 🟡 Minor: Navigation History Memory Leak RiskIssue: Each file tab maintains its own navigation history, but there's no limit on history size export interface FilePreviewTab {
// ...
navigationHistory: FilePreviewHistoryEntry[]; // No max size
navigationIndex: number;
}Location: Suggestion: Implement a max history size (similar to MAX_CLOSED_TAB_HISTORY = 25) Severity: Low - Would take extensive navigation to cause issues 🟢 Good: Unified Closed Tab HistoryPositive: The unified closed tab history (unifiedClosedTabHistory) correctly limits to 25 entries, preventing unbounded growth: const MAX_CLOSED_TAB_HISTORY = 25;Location: 🟡 Minor: Duplicate Content Storage During EditIssue: When editing a file, content is stored twice:
Location: Suggestion: Consider using a diff/patch approach for large files, or at least document this trade-off Severity: Low - Acceptable trade-off for simplicity 🟢 Good: SSH Remote Loading StatePositive: Excellent handling of async file loading for SSH remote files with proper loading states: export interface FilePreviewTab {
// ...
isLoading: boolean;
sshRemoteId?: string;
}This prevents UI jank and provides good UX feedback. Location: 🟡 Minor: Global Image Cache CleanupIssue: Image cache cleanup runs on a setInterval at module level, which means it persists even if all file tabs are closed Location: setInterval(() => {
const now = Date.now();
for (const [key, value] of imageCache.entries()) {
if (now - value.loadedAt > IMAGE_CACHE_TTL) {
imageCache.delete(key);
}
}
}, IMAGE_CACHE_TTL);Suggestion: Consider using a more sophisticated cleanup strategy (e.g., clear on component unmount if no tabs remain, or use WeakMap if possible) Severity: Very Low - 10 minute interval is reasonable, minimal resource impact 🔵 Code Style: Debug CommentsIssue: Several DEBUG comments left in production code // DEBUG: Log context calculation details
// DEBUG: Log to trace progress updates
// DEBUG: Log MainPanel context display calculationLocation: Multiple files in Suggestion: Either remove or convert to proper logging with log levels Severity: Very Low - Doesn't affect functionality Security Considerations: ✅ No Issues
Performance: ⭐⭐⭐⭐⭐ (Excellent)Strengths
Test Evidence
Best Practices Adherence: ⭐⭐⭐⭐⭐ (Excellent)Follows Project Conventions ✅
Architecture: ⭐⭐⭐⭐⭐ (Excellent)Design Decisions
Testing: ⭐⭐⭐⭐⭐ (Excellent)Coverage Breakdown
Test Quality
Migration & Compatibility: ✅ ExcellentBackward Compatibility
RecommendationsMust Address Before Merge: None ✅The PR is production-ready as-is. Should Address Soon:
Nice to Have:
Final Verdict: ✅ APPROVEDThis is exemplary work:
The minor issues noted are truly minor and don't block merging. This is one of the best-structured PRs I've reviewed. Recommendation: Merge with confidence. Address the minor suggestions in follow-up work if desired. Additional NotesThe phased approach (62 commits) makes this large change reviewable:
This is a textbook example of how to implement a large feature incrementally. Great work! 🎉 |
Comprehensive PR Review: File Preview Tab SystemExecutive SummaryThis PR introduces a well-architected file preview tab system that successfully integrates with the existing AI tab infrastructure. The implementation is production-ready with some recommended improvements. Code quality is high, test coverage is good, and security concerns are minimal. Overall Assessment: ⭐⭐⭐⭐ (4/5 stars) Recommendation: Approve with required changes. Address the 4 high-priority issues below before merging. 🔴 High Priority Issues (Fix Before Merge)1. CRITICAL: Global setInterval Memory LeakFile: // Clean up old cache entries periodically
setInterval(() => {
const now = Date.now();
for (const [key, value] of imageCache.entries()) {
if (now - value.loadedAt > IMAGE_CACHE_TTL) {
imageCache.delete(key);
}
}
}, IMAGE_CACHE_TTL);Problem: This Fix: Use lazy cleanup on cache access instead: function cleanupImageCache() {
const now = Date.now();
for (const [key, value] of imageCache.entries()) {
if (now - value.loadedAt > IMAGE_CACHE_TTL) {
imageCache.delete(key);
}
}
}
// Call in ImageDisplay useEffect before accessing cache2. BUG: Stale Closure in Scroll Save TimerFile: Problem: The scroll save timeout references Fix: const onScrollPositionChangeRef = useRef(onScrollPositionChange);
useEffect(() => {
onScrollPositionChangeRef.current = onScrollPositionChange;
}, [onScrollPositionChange]);
// Then use onScrollPositionChangeRef.current in setTimeout3. SECURITY: Potential Path TraversalFile: const treeRoot = activeSession.projectRoot || activeSession.fullPath;
const fullPath = `${treeRoot}/${path}`;Problem: Concatenating paths without validation could allow traversal (e.g., Fix: import * as path from 'path';
const treeRoot = activeSession.projectRoot || activeSession.fullPath;
const fullPath = path.normalize(path.join(treeRoot, path));
if (!fullPath.startsWith(path.normalize(treeRoot))) {
console.error('Path traversal attempt detected:', path);
return;
}4. BUG: Empty File Extension HandlingFile: const extension = file.name.includes('.')
? '.' + file.name.split('.').pop()
: '';
const nameWithoutExtension = extension
? file.name.slice(0, -extension.length)
: file.name;Problem: Files like
Fix: const lastDotIndex = file.name.lastIndexOf('.');
const extension = lastDotIndex > 0 ? file.name.substring(lastDotIndex) : '';
const nameWithoutExtension = lastDotIndex > 0
? file.name.substring(0, lastDotIndex)
: file.name;🟡 Medium Priority Issues (Fix Soon)5. PERFORMANCE: Unnecessary Re-renders in FilePreviewFile: The component is 2,421 lines and accepts 30+ props but isn't memoized. Every parent re-render triggers a full FilePreview re-render. Recommendation: export const FilePreview = React.memo(
forwardRef<FilePreviewHandle, FilePreviewProps>((props, ref) => { ... }),
(prev, next) => prev.file?.path === next.file?.path // Custom comparison
);6. BUG: Missing Error Handling for File SaveFile: Save failures only show a 2-second toast. Users might not notice critical errors (permissions, disk full, SSH timeout) and lose work. Recommendation:
7. Race Condition: Duplicate Tab PreventionFile: If the same file is clicked rapidly before setState completes, two tabs could be created. The duplicate check happens in setState callback, which is not atomic. Fix: const inFlightFileOpens = useRef(new Set<string>());
// Before opening:
if (inFlightFileOpens.current.has(file.path)) return;
inFlightFileOpens.current.add(file.path);
// After setState:
inFlightFileOpens.current.delete(file.path);8. TESTING: Missing Navigation History TestsFile: Complex navigation history logic (breadcrumb back/forward) has no test coverage. High risk of off-by-one errors. Recommendation: Add tests for:
9. TESTING: Missing SSH Remote File TestsNo tests for:
✅ StrengthsCode Quality
Performance
Security
Test Coverage
📝 Low Priority Improvements (Future)
SummaryThis is a well-implemented feature that successfully extends Maestro's architecture. The unified tab system is elegant, state management is solid, and SSH integration follows documented patterns. The critical issues are straightforward to fix:
With these fixes, this will be a robust production feature that achieves its goal: "File content and AI content can now live side by side." 🎉 |
File content and AI content can now live side by side... and soon, CLI content too. Then, multi-window, then tiling, and then all the flexibility is yours. Closes #272