Skip to content

open new window#59

Merged
Kitenite merged 3 commits into
mainfrom
new-window
Nov 11, 2025
Merged

open new window#59
Kitenite merged 3 commits into
mainfrom
new-window

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Nov 11, 2025

Description

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes

Summary by CodeRabbit

  • New Features

    • Added "New Window" menu item (shortcut: Cmd/Ctrl+Shift+N) to open additional application windows.
  • Refactor

    • Centralized window management and IPC handling to better support multiple windows, improve terminal lifecycle handling, and reduce cross-window side effects.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 11, 2025

Walkthrough

Refactors app startup to centralize IPC registrations and window creation: introduces a WindowManager singleton, registers workspace/port/deep-link/window/terminal IPCs once at startup, converts per-window IPC setup to global one-time handlers, and adds a "New Window" menu item that uses windowManager.createWindow().

Changes

Cohort / File(s) Summary
Startup entrypoint
apps/desktop/src/main/index.ts
Move workspace/port/deep-link/window/terminal IPC registration to startup; pass a factory () => windowManager.createWindow() into app setup; remove direct MainWindow usage.
Window manager
apps/desktop/src/main/lib/window-manager.ts
New default-exported WindowManager instance with createWindow(): Promise<BrowserWindow>, getWindows(), and getWindowCount(); tracks and cleans up BrowserWindow instances.
Menu
apps/desktop/src/main/lib/menu.ts
Add "New Window" menu item (CmdOrCtrl+Shift+N) that calls windowManager.createWindow() with try/catch and error dialog; import windowManager.
Terminal IPCs
apps/desktop/src/main/lib/terminal-ipcs.ts
Introduce one-time IPC init guard (ipcHandlersRegistered); rename BrowserWindow type alias; change function signature to registerTerminalIPCs(window: BrowserWindowType); register terminal IPC handlers once; register/unregister windows per terminal and hook window 'closed' for cleanup.
Window IPC bridge
apps/desktop/src/main/lib/window-ipcs.ts
New module exporting registerWindowIPCs() which registers an ipcMain.handle("window-create", ...) handler that calls windowManager.createWindow() and returns structured { success, error } responses.
Main window adjustments
apps/desktop/src/main/windows/main.ts
Remove workspace/port/deep-link IPC registrations from per-window init; retain terminal IPC setup scoped to terminal lifecycle; stop destroying other windows on close.
Tmux manager
apps/desktop/src/main/lib/tmux-manager.ts
Replace single mainWindow with terminalWindows: Map<string, Set<BrowserWindow>>; add registerTerminalWindow, unregisterTerminalWindow, and unregisterWindow; broadcast emissions to all registered windows for a terminal.

Sequence Diagram(s)

sequenceDiagram
    participant App as Electron App
    participant Startup as index.ts (startup)
    participant WM as windowManager
    participant IPC as ipcMain
    participant Menu as Menu

    App->>Startup: app.whenReady()
    Startup->>IPC: registerWorkspaceIPCs()
    Startup->>IPC: registerPortIpcs()
    Startup->>IPC: registerDeepLinkIpcs()
    Startup->>IPC: registerWindowIPCs()
    Startup->>IPC: registerTerminalIPCs(window?)  %% one-time guarded
    Startup->>WM: create initial window via factory

    Menu->>WM: user selects "New Window"
    WM->>WM: createWindow() -> MainWindow instance
    WM->>Startup: new BrowserWindow tracked

    IPC->>WM: "window-create" handler -> WM.createWindow()
    IPC->>TM: terminal IPCs emit -> tmux-manager broadcasts to registered windows
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Special attention areas:
    • apps/desktop/src/main/lib/terminal-ipcs.ts — verify one-time registration guard, sender-window resolution, and cleanup hookup
    • apps/desktop/src/main/lib/tmux-manager.ts — confirm multi-window tracking, emission correctness, and destroyed-window handling
    • apps/desktop/src/main/index.ts — ensure IPC registration order and app setup factory usage are correct
    • apps/desktop/src/main/lib/window-manager.ts — check promise handling and "closed" cleanup to prevent leaks

Poem

🐰 A hop, a click, a brand new view,
The manager opens windows two by two.
IPCs sleep until called just once,
Terminals chatter, no duplicate dunce.
Hooray — the desktop bounces anew! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely empty—all template sections lack content, with no description, related issues, type selection, testing details, or additional notes provided. Fill out all required sections including a description of changes, type of change selection, testing approach, and any related issue links.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'open new window' directly matches the main changeset objective of adding window creation functionality throughout the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch new-window

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/desktop/src/main/windows/main.ts (2)

124-131: Closing one window now nukes every window
The new multi-window flow can’t work because closing any window iterates BrowserWindow.getAllWindows() and forcefully destroy()s each instance. That bypasses Electron’s normal lifecycle and immediately tears down the other windows as well, so users lose every window whenever they close just one. Please drop the loop (or only destroy the window being closed) and rely on the default close semantics instead; destroy() is documented as a force-close that skips close/beforeunload events, so it’s only for last-resort crashes, not routine shutdown. (electronjs.org)

 	window.on("close", () => {
 		// Clean up terminal processes
 		cleanupTerminal();
-
-		for (const window of BrowserWindow.getAllWindows()) {
-			window.destroy();
-		}
 	});

47-98: Register port detector listeners only once
Every call to MainWindow() reattaches portDetector handlers, so by the time a few windows have been opened you have N identical listeners firing per event. That balloons log noise, repeats config writes, and eventually triggers listener leak warnings (MaxListenersExceeded)—a classic symptom of duplicated listeners. Move these registrations behind a one-time guard (similar to what you already did for terminal IPCs) so they’re added exactly once per process. (openfin.zendesk.com)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4e9c53 and b950af2.

📒 Files selected for processing (6)
  • apps/desktop/src/main/index.ts (2 hunks)
  • apps/desktop/src/main/lib/menu.ts (1 hunks)
  • apps/desktop/src/main/lib/terminal-ipcs.ts (1 hunks)
  • apps/desktop/src/main/lib/window-ipcs.ts (1 hunks)
  • apps/desktop/src/main/lib/window-manager.ts (1 hunks)
  • apps/desktop/src/main/windows/main.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/desktop/src/main/index.ts (4)
apps/desktop/src/main/lib/workspace-ipcs.ts (1)
  • registerWorkspaceIPCs (16-651)
apps/desktop/src/main/lib/port-ipcs.ts (1)
  • registerPortIpcs (9-69)
apps/desktop/src/main/lib/deep-link-ipcs.ts (1)
  • registerDeepLinkIpcs (7-14)
apps/desktop/src/lib/electron-app/factories/app/setup.ts (1)
  • makeAppSetup (14-56)
apps/desktop/src/main/lib/window-manager.ts (1)
apps/desktop/src/main/windows/main.ts (1)
  • MainWindow (19-134)

Comment on lines +53 to +58
// Register IPC handlers once at startup (not per-window)
registerWorkspaceIPCs();
registerPortIpcs();
registerDeepLinkIpcs();

await makeAppSetup(() => windowManager.createWindow());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Wire up the new window-create IPC channel
You added registerWindowIPCs() but never invoke it here, so any renderer calling ipcRenderer.invoke("window-create") will reject because no ipcMain.handle was registered. Please import the new helper and call it alongside the other startup registrations so the channel is live before windows request it. (electronjs.org)

-import { registerWorkspaceIPCs } from "main/lib/workspace-ipcs";
-import { registerPortIpcs } from "main/lib/port-ipcs";
-import { registerDeepLinkIpcs } from "main/lib/deep-link-ipcs";
+import { registerWorkspaceIPCs } from "main/lib/workspace-ipcs";
+import { registerPortIpcs } from "main/lib/port-ipcs";
+import { registerDeepLinkIpcs } from "main/lib/deep-link-ipcs";
+import { registerWindowIPCs } from "main/lib/window-ipcs";
…
 	// Register IPC handlers once at startup (not per-window)
 	registerWorkspaceIPCs();
 	registerPortIpcs();
 	registerDeepLinkIpcs();
+	registerWindowIPCs();

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/desktop/src/main/index.ts around lines 53 to 58, the new
registerWindowIPCs() helper was added but not invoked at startup, so ipcMain has
no handler for the "window-create" channel and
ipcRenderer.invoke("window-create") will reject; import registerWindowIPCs at
the top of the file (if not already) and call registerWindowIPCs() alongside
registerWorkspaceIPCs(), registerPortIpcs(), and registerDeepLinkIpcs() so the
IPC handler is registered before makeAppSetup creates windows.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/desktop/src/main/lib/terminal-ipcs.ts (2)

1-6: Clarify the dual BrowserWindow import pattern.

The import pattern imports BrowserWindow as both a value (for BrowserWindow.fromWebContents on line 35) and a type alias (BrowserWindowType). While this works, it's unconventional.

Consider using a clearer pattern:

-import {
-	BrowserWindow,
-	type BrowserWindow as BrowserWindowType,
-	ipcMain,
-	shell,
-} from "electron";
+import { BrowserWindow, ipcMain, shell } from "electron";

Then use BrowserWindow for both the type and the value throughout the file.


19-103: Consider renaming for clarity.

The function name registerTerminalIPCs suggests per-window registration, but the implementation registers IPC handlers globally only once (guarded by ipcHandlersRegistered). Subsequent calls only set up cleanup hooks for additional windows.

Consider renaming to better reflect the actual behavior:

export function setupTerminalIPCsForWindow(window: BrowserWindow) {
  // Initialize once globally
  if (!ipcHandlersRegistered) {
    // ... register global handlers
  }
  // Register per-window cleanup
  window.on("closed", cleanup);
}

Or split into two functions:

function registerGlobalTerminalIPCs() { /* one-time registration */ }
export function attachWindowToTerminals(window: BrowserWindow) { /* cleanup only */ }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b950af2 and f436b48.

📒 Files selected for processing (4)
  • apps/desktop/src/main/index.ts (3 hunks)
  • apps/desktop/src/main/lib/terminal-ipcs.ts (1 hunks)
  • apps/desktop/src/main/lib/tmux-manager.ts (3 hunks)
  • apps/desktop/src/main/windows/main.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/desktop/src/main/windows/main.ts
  • apps/desktop/src/main/index.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/desktop/src/main/lib/terminal-ipcs.ts (1)
apps/desktop/src/main/lib/terminal.ts (1)
  • cleanup (214-229)
🔇 Additional comments (9)
apps/desktop/src/main/lib/tmux-manager.ts (5)

34-34: LGTM!

The terminalWindows map structure correctly supports the new multi-window architecture, allowing multiple windows to receive output from the same terminal.


55-66: LGTM!

The registration logic correctly initializes the Set for new terminals and tracks window associations. The logging provides useful debugging information.


68-82: LGTM!

The unregistration logic correctly removes windows and cleans up empty Sets to prevent memory leaks.


84-99: LGTM!

The window-wide cleanup correctly removes the window from all terminal associations. This is essential for proper lifecycle management when windows close.


640-655: LGTM!

The broadcast logic correctly sends terminal data to all registered windows. The isDestroyed() check is essential to prevent crashes when sending to destroyed windows.

apps/desktop/src/main/lib/terminal-ipcs.ts (4)

9-9: LGTM!

The one-time initialization guard prevents duplicate IPC handler registrations across multiple window creations.


11-17: LGTM!

The one-time tmux manager initialization is correctly guarded and includes proper error handling.


34-41: LGTM!

The terminal-create handler correctly identifies the requesting window and registers it to receive output from the newly created terminal. The null check for senderWindow is appropriate.


105-114: LGTM!

The cleanup logic correctly unregisters the window from all terminals when the window closes. The 'closed' event is the appropriate lifecycle hook for this cleanup.

@Kitenite Kitenite merged commit 0c736e4 into main Nov 11, 2025
2 of 5 checks passed
@Kitenite Kitenite deleted the new-window branch November 11, 2025 21:24
@Kitenite Kitenite mentioned this pull request Nov 11, 2025
5 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Nov 11, 2025
5 tasks
@Kitenite Kitenite mentioned this pull request Nov 11, 2025
5 tasks
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.

1 participant