- Toast clicks now always reveal the AI terminal, not file previews 🧠
- Session switching clears active file preview to prevent confusing UI state 🧹
- Toast-driven navigation can target a specific AI tab when available 🎯
- Missing tab IDs fall back gracefully while still entering AI input mode 🛟
- Context usage warnings are less noisy with higher yellow threshold (75%) 🟡
- Red context warnings now trigger later at 90% for calmer workflows 🔴
When closing an AI tab via Cmd+W, the keyboard handler was calling
closeTab() directly which only adds to the legacy closedTabHistory.
This meant AI tabs were not being added to unifiedClosedTabHistory,
so Cmd+Shift+T could not restore them in the correct order with
file preview tabs.
Now uses performTabClose() which properly calls addAiTabToUnifiedHistory()
to ensure both AI tabs and file tabs appear in the unified closed tab
history in the order they were closed.
Fixes tab restore order: closing AI-tab-1, file-preview-A, AI-tab-2
then pressing Cmd+Shift+T three times now correctly restores:
AI-tab-2, file-preview-A, AI-tab-1.
- Move max-height from Tailwind class to inline style for better control
- Change max-height from calc(100vh-120px) to calc(100vh-90px) to allow
more content to be visible when screen height is limited
- Update test to check inline style instead of class pattern
When closing a tab, now selects the previous (left) tab instead of
staying at the same index position. This provides more intuitive
behavior matching browser tab behavior.
- closeTab: select tab to the left, or new first tab if closing first
- closeFileTab: same behavior for unified tab order
- Updated tests to reflect new selection behavior
Add guards in both frontend (ToastContext) and backend (notifications
handler) to prevent sending empty content to custom notification
commands. This prevents unnecessary command executions when there's
no message to send.
- ToastContext: Check if toast.message has content before calling speak
- notifications.ts: Early return if text is empty/whitespace
- Add tests for empty and whitespace-only content handling
The OpenCode question tool waits for stdin input when invoked, which
causes Maestro to hang indefinitely in batch mode (Auto Run) since
stdin is closed immediately after the prompt is sent.
Added `"tools":{"question":false}` to the OPENCODE_CONFIG_CONTENT
environment variable to disable the question tool. This forces the
agent to communicate questions via normal text output instead.
Root cause analysis from user debug logs:
- Auto Run batch session ran for ~10 minutes without any output
- Process had to be killed manually (0/20 tasks completed)
- OpenCode's permission config (`"*":"allow"`) doesn't prevent the
question tool from waiting for stdin input
Testing confirmed that disabling the question tool via tools config
prevents the hang while still allowing all other permissions.
Add "Command Palette" menu item with CMD+K shortcut below "Introductory Tour"
in the main hamburger menu. This provides quick access to the quick actions
modal from the menu for discoverability.
- Add Command icon import from lucide-react
- Add setQuickActionOpen prop through the component chain
- Display keyboard shortcut (Cmd+K) with proper formatting
- Update tests with Command icon mock
The test was looking for 'Search...' but the actual placeholder is
'Search in file... (Enter: next, Shift+Enter: prev)'. Updated to use
regex /Search in file/ pattern consistent with other tests in the file.
In tab mode, the layer system isn't registered so Escape wasn't being
handled. Added explicit Escape key handling in handleKeyDown to dismiss
the ToC overlay (and search) when open. ToC is dismissed first if both
are open, matching the expected priority order.
When a file preview tab is closed and later restored via Cmd+Shift+T,
the navigation history could become corrupted or stale, causing
confusing breadcrumb behavior (e.g., pressing forward navigates to
an unexpected file).
Fix by resetting the navigation history to just the current file
when restoring a tab. The restored tab will show the last viewed
file with a fresh, empty breadcrumb history.
Changes:
- Modified reopenUnifiedClosedTab() to reset navigationHistory and
navigationIndex when restoring file tabs
- Added test case to verify navigation history is reset on restore
- File preview tab scroll now syncs into navigation history for restores 🧭
- Breadcrumb close/restore keeps the exact scroll position per history entry 📌
- Session UUID pill hides when a file preview tab is focused 🪪
- Cost tracking pill hides during file preview to reduce UI noise 💸
- Context window usage widget hides in file preview for cleaner focus 🧹
Tier 1 performance optimizations for snappier app experience:
1. Lazy-load SettingsModal in App.tsx
- SettingsModal is ~2.6K lines, now only loaded when settings opened
- Wrapped in Suspense with conditional rendering
2. Add manual chunk splitting to vite.config.mts
- Split vendor chunks: react, xterm, markdown, syntax, mermaid, charts, flow, diff
- Enables better browser caching and smaller initial bundle
- Heavy visualization libs (mermaid ~500KB, recharts ~400KB, etc.) now in separate chunks
3. Cache static files in web server (staticRoutes.ts)
- index.html, manifest.json, sw.js now read once and cached
- Eliminates blocking sync file reads on every web request
- Improves web/mobile interface responsiveness
When pressing cmd+j to switch between AI/terminal mode, the previously
active file preview tab is now saved and restored. This allows users
to view a file, switch to terminal, run commands, then switch back and
have the same file preview re-opened automatically.
Added preTerminalFileTabIdRef to UILayoutContext to track the active
file tab before switching to terminal mode. Updated toggleInputMode
to save/restore this state.
- Switching to Terminal now clears file preview, avoiding confusing leftovers 🧹
- Cmd+J toggle works from file preview straight into Terminal view ⌨️
- Keyboard handler now whitelists toggleMode even with modals open, improving flow 🔓
The FilePreview component was receiving new object/function references on
every render of MainPanel, causing unnecessary re-renders that made images
flicker. This happened because:
1. The `file` prop was an inline object literal created each render
2. All callback props (onClose, onSave, etc.) were inline arrow functions
3. The `cwd` prop used an IIFE that recalculated each render
4. The `sshRemoteId` prop used inline ternary logic
Fixed by:
- Memoizing the file object with useMemo (keyed on actual values)
- Memoizing all callbacks with useCallback
- Memoizing cwd and sshRemoteId with useMemo
- Using stable references for all FilePreview props
This ensures FilePreview only re-renders when actual data changes, not on
every parent render cycle.
The tests were causing unhandled rejection errors because they weren't
waiting for the `finally` block's `setSaving(false)` to complete after
error handling. Updated waitFor assertions to also check that the save
button text returns to "Save" (from "Saving..."), ensuring all async
state updates are complete before the test ends and the component unmounts.
The TabBar scroll-into-view effect only triggered for AI tabs. Added
activeFileTabId to the useEffect dependencies so file preview tabs
are also scrolled into view when opened.
handleCloseCurrentTab was duplicating file tab close logic inline
but not adding closed tabs to unifiedClosedTabHistory, causing
Cmd+Shift+T to restore a different tab instead of the most recently
closed one. Now uses closeFileTabHelper which properly tracks history.
- App.tsx: Remove unused import (reopenClosedTab), legacy navigation
variables (backHistory, forwardHistory, filePreviewHistory,
filePreviewHistoryIndex), legacy callbacks (setFilePreviewHistory,
setFilePreviewHistoryIndex), unused async handler (handleOpenFileTabAsync),
and legacy navigation handlers (handleNavigateBack, handleNavigateForward,
handleNavigateToIndex) - all superseded by per-tab navigation system
- MainPanel.tsx: Prefix unused destructured props with underscore
- contextUsage.ts: Remove unused UsageStats import
This removes ~250 lines of dead code from the legacy session-level
file preview navigation system, which has been replaced by the per-tab
breadcrumb navigation in the unified tab system.
- detector.test.ts: Update agent count from 7 to 8 (aider was added)
- session-storage.test.ts: Fix import paths for factory-droid storage
- agents.test.ts: Add AGENT_DEFINITIONS to mock for handler tests
- stats.test.ts: Remove tests for non-existent initialization handlers
- usage-aggregator.test.ts: Update DEFAULT_CONTEXT_WINDOWS test for actual ToolType values
- MainPanel.test.tsx: Migrate from previewFile prop to activeFileTabId/activeFileTab system
- contextUsage.test.ts: Update tests for current ToolType values (removed 'claude', 'aider')
- SessionStatusBanner.test.tsx: Update test for null return when tokens exceed context
- usage-listener.test.ts: Fix expected contextUsage (falls back to 200k default)
Key changes:
- Accept main's fix for context usage calculation (returns null for
accumulated multi-tool turn values instead of capping at 100%)
- Adopt main's refactored structure:
- agent-detector.ts → agents/detector.ts + definitions.ts + capabilities.ts
- stats-db.ts → stats/*.ts modules
- agent-session-storage types → agents/index.ts
- Port factory-droid agent to new agents/definitions.ts structure
- Remove obsolete shared/contextUsage.ts (logic now in renderer/utils)
- Update all import paths to reference new module locations
- Preserve all RC features: Symphony, File Preview Tabs, TabNaming, etc.
The context window fix is critical: main's approach correctly handles
when Claude Code reports accumulated token values from multi-tool turns
by returning null, causing the UI to preserve the last valid percentage.
RC's approach masked this by capping at 100%, hiding the issue.
The tests were failing because FilePreview uses useClickOutside twice (once for
container dismiss, once for TOC dismiss) and the mock was only capturing the
last call. Updated the mock to track both calls separately and added proper
act() wrapping for React state updates.
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.
- 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
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.
The context formula was excluding cacheReadInputTokens, causing the gauge to
drastically underestimate usage (e.g., 3% when reality was 23%). During
multi-tool turns, accumulated token totals could exceed the context window,
producing false 100% readings and premature compact warnings.
- Include cacheReadInputTokens in the formula (input + cacheRead + cacheCreation)
- Detect accumulated values (total > window) and return null to preserve last valid %
- Skip context updates during accumulated turns instead of displaying inflated values
- Fix MainPanel tooltip deriving from raw tab stats instead of preserved session percentage
- Handle group chat participant/moderator accumulated values with -1 sentinel
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.
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.
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.
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).
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.
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.
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
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.
- 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
- 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').
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 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
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)