docs/references/window-manager/window-manager-migration-guide.md
How to migrate an existing window from direct BrowserWindow creation to WindowManager.
In types.ts, add a new enum value:
export enum WindowType {
// ...
MyWindow = 'myWindow',
}
Define the window's metadata and default configuration:
WINDOW_TYPE_REGISTRY[WindowType.MyWindow] = {
type: WindowType.MyWindow,
lifecycle: 'singleton', // or 'default' or 'pooled'
htmlPath: 'my-window.html',
// preload omitted → defaults to 'preload.js'. Write basename (with extension)
// to select a different file in src/preload/. Empty string → no preload.
// preload: 'simplest.js',
showMode: 'auto', // 'auto' | 'immediate' | 'manual'
windowOptions: {
...DEFAULT_WINDOW_CONFIG,
width: 800,
height: 600,
},
behavior: {
// Declarative WM-level behaviors (all optional). See the README "Configuration Layers" section.
// hideOnBlur: true, // auto-hide on blur (runtime override: wm.behavior.setHideOnBlur)
// alwaysOnTop: { level: 'floating' }, // level/relativeLevel for setAlwaysOnTop (runtime override: wm.behavior.setAlwaysOnTop)
// visibleOnAllWorkspaces: { enabled: true, visibleOnFullScreen: true },
// macShowInDock: false, // do not contribute to Dock visibility (macOS helper windows only; default true)
// // runtime override: wm.behavior.setMacShowInDockByType(type, value) for tray-mode transitions
},
// quirks: { ... }, // OS hacks — see Platform Configuration
}
See Lifecycle Modes for choosing between default / singleton / pooled.
Optional: for singleton types that benefit from pre-warm or close→hide, set singletonConfig. See Warmup Mechanics → Singleton Variant.
Replace direct new BrowserWindow() + setup code with an onWindowCreated subscription in your domain service:
Before:
class MyService {
private window: BrowserWindow | null = null
createWindow() {
this.window = new BrowserWindow({ width: 800, height: 600, ... })
this.window.loadFile('my-window.html')
this.window.on('closed', () => { this.window = null })
}
}
After:
@Injectable('MyService')
@ServicePhase(Phase.WhenReady)
class MyService extends BaseService {
private windowId: string | undefined
protected override onInit(): void {
const wm = application.get('WindowManager')
wm.onWindowCreatedByType(WindowType.MyWindow, ({ window, id }) => {
this.windowId = id
// attach listeners here — use `window` directly, or switch to the `mw` shorthand
// if the callback body has inner closures (see Usage Guide → Callback styles).
})
wm.onWindowDestroyedByType(WindowType.MyWindow, () => {
this.windowId = undefined
})
}
openWindow(): void {
const wm = application.get('WindowManager')
this.windowId = wm.open(WindowType.MyWindow)
}
}
See Injecting behavior: onWindowCreated is the canonical hook for the full rationale behind this pattern.
| Old Pattern | New Pattern |
|---|---|
this.window = new BrowserWindow(...) | wm.open(WindowType.MyWindow) |
this.window.show() | wm.show(windowId) |
this.window.hide() | wm.hide(windowId) |
this.window.close() | wm.close(windowId) |
this.window.webContents.send(...) | wm.getWindow(windowId)?.webContents.send(...) or wm.broadcastToType(...) |
BrowserWindow.fromWebContents(e.sender) | wm.getWindowIdByWebContents(e.sender) |
Note: there is intentionally no entry for this.window.destroy(). wm.close() already handles destruction for non-pooled windows and pool-return for pooled windows. wm.destroy() is an internal primitive — see Window API layers.
Remove manual show / ready-to-show logic if using showMode: 'auto' (the default). WindowManager handles:
ready-to-show (fresh path) or immediately (recycled path)If your window needs custom show timing, set showMode: 'manual' in the registry and manage visibility yourself.
WindowType enum value in types.tsWINDOW_TYPE_REGISTRY in windowRegistry.tsdefault / singleton / pooled)preload filename if not using the default ('preload.js')showMode behavior ('auto' / 'immediate' / 'manual')behavior.macShowInDock: false ONLY for helper windows (floating panels, selection overlays); primary app windows leave it at the default true. Use wm.behavior.setMacShowInDockByType(type, value) for runtime tray-mode transitions, not a different registry default.behavior.hideOnBlur / behavior.alwaysOnTop / behavior.visibleOnAllWorkspaces as neededonWindowCreated hookBrowserWindow references with WindowManager API callsready-to-show handling (if using showMode: 'auto')getInitData + reset IPC wiring with the useWindowInitData hookPoolConfig axes (standbySize for active pre-warm, recycleMinSize/recycleMaxSize for recycling). Leave recycleMaxSize unset for one-shot "close destroys" semantics; set standbySize when zero-wait matters under concurrent opens.onWindowDestroyed cleanup in the domain service