- 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
- 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.
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.
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
- 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
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
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
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 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
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
- 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
- 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
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.
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
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
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.
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.
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
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).
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.
Changed IssueCard from <button disabled> to <div role="button"> to allow
nested PR link button to receive click events. Disabled buttons block all
pointer events on children, even with pointerEvents: 'auto' style.
- Outer container is now a div with role="button" and manual keyboard handling
- PR link is now a proper <button> that can receive clicks independently
- Maintains same visual styling (opacity-60 for unavailable issues)
- Keyboard navigation preserved via tabIndex and onKeyDown
- Moderator @mentions now normalize names (spaces→hyphens) for reliability 🧭
- Participant and available-session lists now show mention-ready normalized handles 👥
- Header responsiveness tuned for AUTO mode’s extra button width 📐
- Session name now hides on ultra-narrow panels to save space 🪟
- Git branch icon won’t shrink; branch text truncates cleanly ✂️
- Symphony issue PR link upgraded from span to proper button 🧷
- PR link click handling simplified; keyboard hacks removed for cleaner UX ⌨️
- Batch TTS settings now use refs to avoid stale closures mid-run 🎙️
- Audio feedback toggles take effect immediately during batch processing ⚡
- Batch processor deps slimmed since audio settings are ref-driven 🧼
- 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
Moved additional visual/output settings from General to Display:
- Interface font family and size
- Terminal width (columns)
- System log level and max buffer
Updated tests and docs to reflect new Display tab location.
When closing the Auto Run Configuration modal, show a confirmation
dialog if there are unsaved changes to:
- Document list (documents added or removed)
- Loop settings (enabled/disabled, max loops)
- Agent prompt (edited from initial value)
If no changes were made, the modal closes immediately without
confirmation.
This prevents accidental loss of configuration when users press
Escape, click Cancel, or click the X button.
Add comprehensive test suite to verify modal priority ordering:
- Child modals must have higher priority than parents (30 test cases)
- Ensures DocumentSelectorModal closes before BatchRunner
- Ensures MarketplaceModal closes before BatchRunner/AutoRunExpanded
- Verifies confirmation dialogs are 1000+, overlays are lower priority
- Tests priority ranges match documented conventions
These tests will catch future regressions where child modals have
incorrect priorities, causing Escape to close parent modals first.
Split General settings panel by creating new Display tab containing:
- Max output lines per response
- Document graph settings (external links, max nodes)
- Context window warnings (thresholds)
Updated docs to reflect new panel location.
When a new session starts and the first message is sent, Maestro now
automatically generates a descriptive tab name based on the user's
request. This runs in parallel with the main prompt processing and
uses the same AI agent (honoring SSH remote configurations).
Implementation:
- Add tab naming prompt at src/prompts/tab-naming.md
- Add IPC handler (tabNaming:generateTabName) that spawns ephemeral
session to generate names with 30s timeout
- Integrate with onSessionId callback to trigger naming for new tabs
- Only update name if tab is still in UUID format (user hasn't renamed)
- Add automaticTabNamingEnabled setting (default: true)
- Add Settings UI checkbox under General section
Tab names are 2-5 words, Title Case, and capture the specific intent
rather than generic descriptions. Examples:
- "Help me implement JWT auth" → "JWT Auth Implementation"
- "Fix the checkout bug" → "Checkout Bug Fix"
Tests: 22 new tests covering IPC handler, settings, and edge cases
Docs: Updated general-usage.md, features.md, and configuration.md
Apply RTL text direction with LTR text isolation to show the rightmost
(filename) portion of document paths when truncated, while maintaining
full path on hover via title attribute.
Updated components:
- DocumentsPanel: Auto Run document list entries
- RightPanel: Auto Run progress indicator (single and multi-doc views)
When opening the lightbox from staged images, a snapshot of stagedImages
was saved to lightboxImages. The LightboxModal used this snapshot for
navigation. When deleting an image, only stagedImages was updated but
lightboxImages remained stale, causing the deleted image to still appear.
Fix: Update both stagedImages AND lightboxImages in handleDeleteLightboxImage.
Added regression tests that simulate the parent state synchronization
pattern and verify both states are updated on deletion.
Add overflow-y-auto and max-h-[calc(100vh-120px)] to hamburger menu
dropdown containers so content can scroll when the viewport doesn't
have enough vertical space to display all menu items.
Applied to both expanded and collapsed sidebar menu overlays.
- Stabilized group-chat integration tests by mocking Electron `app.getPath()` 🧪
- Added preload filesystem support to write files on SSH remotes 🚀
- Expanded `fs:writeFile` IPC signature to include optional remote identifier 🔌
The useInputProcessing test for "preserves multi-word arguments with
spaces" was failing because mockProcessQueuedItemRef.current accumulated
calls across tests in the nested describe block. Added a beforeEach to
explicitly clear the mock, ensuring mock.calls[0] always references the
current test's call.
Adds a "Response Completeness" section to the Maestro system prompt
instructing agents to make each response self-contained with clear
summaries, relevant context, and key details. This ensures users can
understand responses even when only seeing the most recent message.
Updated all agent prompts to explicitly state that only the LAST
message is captured by the system. This ensures agents produce
complete, self-contained responses with all relevant information.
Prompts updated:
- context-grooming.md, context-summarize.md, context-transfer.md
- autorun-default.md, autorun-synopsis.md
- group-chat-participant.md, group-chat-participant-request.md
- group-chat-moderator-system.md, group-chat-moderator-synthesis.md
- maestro-system-prompt.md (new "Response Completeness" section)
Enable saving markdown content directly to remote filesystems
when connected via SSH. The fs:writeFile IPC handler now accepts
an optional sshRemoteId parameter and uses writeFileRemote for
SSH transport.
The native folder dialog can only browse local filesystems, so hide the
browse button when the session is running over SSH (where the default
path would be a remote path). Users can still manually type a path.
Changed the git info popup's remote URL link to use
window.maestro.shell.openExternal() instead of window.open()
so it opens in the user's default system browser.
- Add await to shell.openExternal call to ensure proper async handling
- Show toast notification when no remote URL is found for the repository
- Add error handling with toast feedback for any failures
- Update tests to verify toast notifications are shown on error
Fixes issue where the command didn't provide feedback when failing.
Custom notification commands (e.g., TTS, fabric pipelines) should only
run for regular AI conversation flow toasts, not for synopsis messages.
Added skipCustomNotification flag to Toast interface and set it to true
for synopsis toasts. This prevents the notification command from being
executed when a synopsis completes, while still showing OS notifications
and toast UI.
Document nodes with long unbroken text (like separator lines with ====)
were bleeding outside their boundaries. Fixed by:
- Setting fixed width (280px) on container instead of just maxWidth
- Using break-all word breaking for description text
- Adding webkit line clamping to limit description to 3 lines
- Using text-overflow: ellipsis with overflow: hidden
- Shows "Drop here to ungroup" zone when dragging a session and no ungrouped sessions exist
- Allows users to drag sessions out of groups even when Ungrouped Agents folder is hidden
- Added tests for drop zone visibility and drop handler
Previously, the Test button would show "Stop" after the notification
command started and remain visible even after the process exited,
requiring an 8-second timeout fallback to reset.
Now the component listens for the onCommandCompleted event and
immediately resets to "Test" (via Success state) when the process
exits naturally. The Stop button is only shown while the process
is actively running.
Remove backward compatibility shim and fully migrate from legacy ttsId
property to notificationId across all notification-related code:
- Update NotificationsPanel.tsx to use result.notificationId
- Remove deprecated ttsId from NotificationCommandResponse interface
- Remove legacy TTS alias exports from handler
- Update all test files and mocks to use notificationId
Changed elapsed time calculation for USER history entries to use the
actual task duration (from thinkingStartTime to completion) instead of
time since the last synopsis. This accurately reflects how long the
agent was actively working on the task being synopsized.
Add elapsed time calculation when creating USER history entries via
the saveToHistory/synopsis feature. Time is calculated from lastSynopsisTime
(if a previous synopsis exists) or tabCreatedAt (for first synopsis).
This complements the existing elapsed time tracking for AUTO entries.