Skip to content

Conversation

@b3nw
Copy link

@b3nw b3nw commented Jan 31, 2026

Summary

This PR implements comprehensive multi-window support for Maestro, allowing users to:

  • Move tabs to new windows via keyboard shortcut (Cmd+Shift+O), context menu, or drag-and-drop
  • Organize workspace across multiple windows with automatic state persistence
  • Identify windows with numbered badges (W2, W3, etc.) in secondary windows
  • Control sessions remotely via web interface with window-specific targeting

Changes

Core Infrastructure

  • Add WindowContext for window state management
  • Add useWindowState hook for window-level state
  • Add multi-window TypeScript type definitions
  • Implement window registry for tracking window assignments

Window Management

  • Tab drag-out detection with getWindowBounds IPC method
  • Last-session drag-out protection for primary window
  • Multi-window state saving on application quit
  • Legacy to multi-window state migration with session IDs

UI Enhancements

  • Window identification badges in TabBar
  • Shortcuts help modal with scope indicators (global vs window)
  • Per-window panel collapse state persistence
  • Quick Actions modal support for window operations

Web Interface

  • Multi-window support for remote commands
  • Window-specific session targeting

Documentation

  • Multi-window support section in keyboard-shortcuts.md
  • ARCHITECTURE.md updates

Tests

  • Comprehensive window manager tests
  • SessionItem component tests
  • QuickActionsModal multi-window tests
  • RightPanel and MainPanel test updates

Test plan

  • Create new session, move to new window with Cmd+Shift+O
  • Verify window badge appears in secondary window (W2)
  • Close secondary window, verify session returns to main window
  • Drag tab out of tab bar to create new window
  • Open Shortcuts Help (Cmd+/), verify scope icons display correctly
  • Toggle left/right panels in secondary window, verify state persists on restart
  • Run npm test to verify all tests pass

Closes #133

b3nw added 11 commits January 31, 2026 07:20
Create shared type definitions for multi-window support (issue pedramamini#133):
- WindowState: serializable window state for persistence
- MultiWindowState: complete app window layout state
- WindowInfo: lightweight IPC communication type
- CreateWindowRequest/Response: IPC request/response types
- MoveSessionRequest: session transfer between windows
- DEFAULT_WINDOW_BOUNDS and MIN_WINDOW_BOUNDS constants

Includes comprehensive test suite (17 tests).
- Updated MainPanel.tsx to use useWindow() hook from WindowContext
- Added isActiveSessionInThisWindow memoized check to determine if
  the active session belongs to the current window
- TabBar now only renders when active session is in windowSessionIds
- Backwards compatible: renders for all sessions when windowSessionIds
  is empty or window state not yet loaded
- Added WindowContext mock to MainPanel.test.tsx
- Added 4 new tests for window-based TabBar filtering:
  - should render TabBar when session is in this window
  - should not render TabBar when session is not in this window
  - should render TabBar when window has no assigned sessions
  - should render TabBar when window state is not loaded yet
- All 128 MainPanel tests pass
- Add windows:getWindowBounds IPC handler to retrieve window screen coordinates
- Add getWindowBounds() method to preload windows API
- Update global.d.ts with getWindowBounds type definition
- Add onTabDragOut callback to TabBar that fires when drag exits window bounds
- Add TabDragOutEvent interface with tabId, screenX, screenY for drag tracking
- Add onDrag handler to Tab component for position tracking during drag
- Add tests for getWindowBounds IPC handler and preload API
Prevent the last session from being dragged out of or moved from the
primary window, ensuring it always has at least one session open.

Implementation details:
- Add refs for isMainWindow and windowFilteredSessions in App.tsx
- Add safety checks in handleTabDragOut and handleMoveToNewWindow that
  show a warning toast when attempting to move the last session
- Conditionally disable handlers (set to undefined) when it's the last
  session in primary window for cleaner UX
- Make handleTabDragOut and handleMoveToNewWindow optional in
  UseMainPanelPropsDeps interface

The TabBar already handles undefined handlers gracefully:
- onTabDragOut undefined: drag tracking disabled, no events fired
- onMoveToNewWindow undefined: context menu button hidden

Part of multi-window support - GitHub issue pedramamini#133
- Implement saveAllWindowStates() function in quit-handler.ts
- Collect window bounds, maximized/fullscreen state, sessionIds, activeSessionId
- Persist to MultiWindowStateStore for restoration on app restart
- Handle destroyed windows gracefully by skipping them
- Add comprehensive tests for window state saving scenarios

Part of multi-window support Phase 4 (GitHub pedramamini#133)
Implement migration logic for existing users upgrading from single-window
to multi-window state persistence. When old WindowState exists without
MultiWindowState, migrate window bounds and include all existing session
IDs in the primary window.

- Add migrateFromLegacyWindowState() function to instances.ts
- Include all session IDs from sessions store in migrated primary window
- Filter invalid session IDs (undefined, null, empty) defensively
- Set first session as activeSessionId (reasonable migration default)
- Preserve legacy data for rollback safety
- Add 16 comprehensive tests for migration scenarios
Implements Phase 4 task: Persist per-window panel collapse state (left
sidebar, right panel) as part of WindowState.

- UILayoutProvider now loads panel state from multi-window store on mount
- Panel state changes are persisted immediately via IPC
- Each window remembers its own panel configuration independently
- Graceful handling of null state and IPC errors
- Prevents persisting before initial load (avoids overwriting persisted state)
- Added 14 tests for panel state persistence in UILayoutContext.test.tsx
- Added windows mock to global test setup for consistent testing
- Update web-server-factory.ts to broadcast remote:* IPC commands to ALL
  windows instead of just mainWindow (sessions could be in secondary windows)
- Add broadcastToAllWindows() helper function that iterates over all windows
  in windowRegistry, with fallback to mainWindow for single-window mode
- Update useRemoteIntegration.ts to filter commands by windowSessionIds,
  so only the window containing the session handles the command
- Add windowSessionIdsRef parameter to UseRemoteIntegrationDeps interface
- Add shouldHandleSession() helper to check if a session belongs to the
  current window before processing remote commands
- Update newTab callback to target specific window via getWindowForSession()
  since it requires synchronous response (can't broadcast)
- Update web-server-factory tests to mock isDestroyed() on BrowserWindow

This fixes a bug where web interface commands for sessions in secondary
windows would fail because they were only sent to the main window.
- Add windowNumber field to WindowInfo type (1 for primary, 2+ for secondary)
- Add computeWindowNumber() in windows.ts for stable window number assignment
- Set OS window titles: "Maestro" for primary, "Maestro [2]", etc. for secondary
- Add window number badge (W2, W3, etc.) in TabBar for secondary windows
- Expose windowNumber in WindowContext for renderer access
- Update global.d.ts with windowNumber in window list type
- Add comprehensive tests for window number assignment and badge display
- Add session transfer logic when closing secondary windows with sessions
- Create comprehensive docs/multi-window.md covering all multi-window features:
  window creation, tab movement, session preservation, multi-display support
- Add multi-window page to docs.json navigation under Usage section
- Add multi-window feature entry to features.md Power Features list
Add Cmd+Shift+O shortcut to move tabs to new windows. Update shortcuts
help modal to display scope indicators (global vs window) for each
shortcut. Add comprehensive test coverage for multi-window functionality
including window manager, session item rendering, and quick actions.
@pedramamini
Copy link
Owner

Closes #133 first Symphony contribution! Thought we're having trouble tracking it and i'm debugging that now.

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.

Feature Request: multiple windows

2 participants