Skip to content

fix: preserve macOS app lifecycle on window close#105

Merged
Astro-Han merged 2 commits into
devfrom
codex/fix-mac-window-close
Apr 21, 2026
Merged

fix: preserve macOS app lifecycle on window close#105
Astro-Han merged 2 commits into
devfrom
codex/fix-mac-window-close

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Apr 21, 2026

Copy link
Copy Markdown
Owner

Summary

Fix macOS window lifecycle so closing the last PawWork window with Cmd+W keeps the app process running instead of quitting.

Add a small tested lifecycle helper for macOS window-close behavior, activate-based window restoration, deep-link queueing while no window is ready, and focused-window menu command routing.

Why

Fixes #96.

PawWork used Electron's File > Close role, but the main process did not handle window-all-closed or activate. On macOS, closing the last BrowserWindow therefore quit the app instead of keeping PawWork available in the Dock/menu bar.

Related Issue

Fixes #96

How To Verify

bun test src/main/window-lifecycle.test.ts
bun run typecheck
bun test
bun run build
bun ./scripts/ci-smoke.ts

Manual check: launched the built Electron app from this branch with an isolated temporary HOME, waited for CI smoke readiness, sent Cmd+W via System Events, and confirmed the process stayed alive. Then activated the app again and confirmed it stayed alive after recreating/focusing a window.

Screenshots or Recordings

Not applicable. This is desktop shell lifecycle behavior, not a visible UI change.

Checklist

  • I ran the relevant verification steps
  • I tested visible changes manually when needed
  • I am targeting the dev branch

Summary by CodeRabbit

  • New Features

    • Enhanced deep link handling with queuing support, ensuring protocol links are properly delivered when the app initializes.
    • Multi-window support with improved lifecycle management, platform-specific quit behavior (macOS keeps app alive; Windows/Linux quit when all windows close).
    • Menu routing improvements for better command window selection.
  • Tests

    • Added comprehensive test suite for window lifecycle, deep link queuing, and external event handling.

@Astro-Han Astro-Han added bug Something isn't working P2 Medium priority platform Electron shell, OS integration, packaging, updater, signing, paths, and permissions labels Apr 21, 2026
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7f83be92-115f-433f-93e4-d4bd289b37c5

📥 Commits

Reviewing files that changed from the base of the PR and between 6296764 and c17bf66.

📒 Files selected for processing (8)
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/ipc.ts
  • packages/desktop-electron/src/main/menu.ts
  • packages/desktop-electron/src/main/window-lifecycle.test.ts
  • packages/desktop-electron/src/main/window-lifecycle.ts
  • packages/desktop-electron/src/preload/index.ts
  • packages/desktop-electron/src/preload/types.ts
  • packages/desktop-electron/src/renderer/index.tsx

📝 Walkthrough

Walkthrough

The changes implement a comprehensive window lifecycle management system for the Electron app, addressing platform-specific behavior around window closure and app termination. The solution introduces deep-link queuing/flushing for windows awaiting readiness, multi-window support with intelligent window selection, and IPC-based coordination between main and renderer processes.

Changes

Cohort / File(s) Summary
Window Lifecycle Core
window-lifecycle.ts, window-lifecycle.test.ts
New module exporting platform-aware decision helpers (shouldQuitWhenAllWindowsClosed, shouldCreateWindowOnActivate) and lifecycle registration logic. Includes window selection (selectNextMainWindow, selectCommandWindow) and deep-link queuing helpers (shouldQueueDeepLinks, takeQueuedDeepLinksForReadyWindow). Comprehensive test suite validates macOS/non-macOS platform rules and deep-link queuing behavior.
Main Process Integration
index.ts
Integrated window lifecycle via registerWindowLifecycle(...), added deepLinkReadyWindows weak set for tracking ready windows, and refactored window creation into openMainWindow(). Updated focusMainWindow() to optionally open missing windows. Enhanced emitDeepLinks() with conditional queuing based on window readiness.
IPC & Menu
ipc.ts, menu.ts
Added report-deep-link-ready IPC handler mapping renderer sender to BrowserWindow and invoking reportDeepLinkReady() callback. Refactored menu to use dependency-injected newWindow() callback instead of direct createMainWindow() import; menu commands now route via selectCommandWindow() instead of always targeting mainWindow.
Renderer & Preload Integration
preload/index.ts, preload/types.ts, renderer/index.tsx
Exposed reportDeepLinkReady() IPC method on preload API surface with corresponding TypeScript type. Updated renderer's listenForDeepLinks() to invoke reportDeepLinkReady() immediately after registering the handler and return the disposer function.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer
    participant MainProcess as Main Process
    participant DeepLinkQueue as Deep-Link Queue
    
    Renderer->>MainProcess: External deep link arrives (on-url/second-instance)
    MainProcess->>DeepLinkQueue: emitDeepLinks() checks shouldQueueDeepLinks()
    
    alt mainWindow not ready
        DeepLinkQueue-->>DeepLinkQueue: Queue deep links
    else mainWindow ready
        DeepLinkQueue-->>Renderer: Send deep links immediately
    end
    
    Renderer->>Renderer: Register handler via onDeepLink()
    Renderer->>MainProcess: reportDeepLinkReady() via IPC
    MainProcess->>MainProcess: Mark window in deepLinkReadyWindows
    MainProcess->>DeepLinkQueue: flushPendingDeepLinksForReadyWindow()
    DeepLinkQueue->>DeepLinkQueue: takeQueuedDeepLinksForReadyWindow()
    DeepLinkQueue-->>Renderer: Flush queued deep links
    Renderer->>Renderer: Handler processes deep links
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Closes a window, the app stays alive,
On macOS it thrives in the dock, yet survives,
Deep links once queue'd, now flush when ready,
Multiple windows—focused routing, rock-steady!
Cmd+W no longer a kiss of goodbye,
Just the window that closes, the app says "hi!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main objective: preserving macOS app lifecycle when closing windows, which directly addresses the linked issue #96.
Description check ✅ Passed The PR description covers all required template sections: summary, why, related issue, verification steps, and completed checklist. It clearly explains the problem and solution.
Linked Issues check ✅ Passed All objectives from issue #96 are met: macOS lifecycle preservation (window-all-closed/activate handlers), window queuing deep links, menu command routing, and comprehensive test coverage demonstrating proper platform-specific behavior.
Out of Scope Changes check ✅ Passed All code changes align with the stated objectives: window-lifecycle helpers, deep-link queuing logic, menu routing, preload/renderer integration, and tests are all necessary for fixing the macOS lifecycle issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-mac-window-close

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the Electron window lifecycle management by introducing a dedicated module and unit tests. Key changes include platform-specific handling for window closing and activation, improved deep link queuing, and more flexible menu command targeting. A potential race condition was identified in the deep link handling logic where links could become stuck in the queue if they arrive while the main window is still loading.

Comment thread packages/desktop-electron/src/main/index.ts Outdated
@Astro-Han

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working P2 Medium priority platform Electron shell, OS integration, packaging, updater, signing, paths, and permissions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Cmd+W quits the app instead of closing the window

1 participant