docs/references/window-manager/window-manager-platform.md
WindowManager splits per-window configuration into three orthogonal layers:
windowOptions — Electron BrowserWindow constructor parameters (including platformOverrides for static per-OS differences).behavior — cross-platform declarative WM behavior that Electron's constructor cannot express (blur-auto-hide, setAlwaysOnTop level, setVisibleOnAllWorkspaces options, Dock visibility). See README → Configuration Layers.quirks — OS-specific hacks / workarounds applied via method-slot monkey-patches around hide() / show() / close().Naming rule: any field effective on only one platform carries a mac / win / linux prefix, irrespective of layer (e.g. behavior.macShowInDock, quirks.macRestoreFocusOnHide).
Some OS-specific behaviors are tedious to hand-roll at every call site (e.g. the macOS focus dance around hide()). WindowManager ships these as declarative opt-in flags under WindowTypeMetadata.quirks. When set, the manager transparently monkey-patches the corresponding BrowserWindow instance methods so business code continues calling window.hide() / window.show() as usual.
| Quirk | Patches | Behavior |
|---|---|---|
macRestoreFocusOnHide: boolean | hide(), close() | Before invoking the native method, iterate every visible focusable BrowserWindow and setFocusable(false); restore them 50ms later. Prevents other windows from being brought to the front when this one disappears. |
macClearHoverOnHide: boolean | hide() | After invoking the native hide(), send webContents.sendInputEvent({ type: 'mouseMove', x: -1, y: -1 }) to clear any residual hover state. |
macReapplyAlwaysOnTop: boolean | show(), showInactive() | After invoking the native method, call setAlwaysOnTop(true, level, relativeLevel) with values read from behavior.alwaysOnTop (single source of truth). When behavior.alwaysOnTop.level is unset, falls back to 'floating'. Compensates for macOS level resets between hide/show. |
All quirks are macOS-only: on other platforms the methods are left untouched, and window.hide === originalHide (identity preserved).
[WindowType.SelectionToolbar]: {
type: WindowType.SelectionToolbar,
lifecycle: 'singleton',
showMode: 'manual',
windowOptions: { /* ... */ },
behavior: {
hideOnBlur: true,
alwaysOnTop: { level: 'screen-saver' }, // level lives here, not in quirks
visibleOnAllWorkspaces: { enabled: true, visibleOnFullScreen: true },
macShowInDock: false
},
quirks: {
macRestoreFocusOnHide: true,
macClearHoverOnHide: true,
macReapplyAlwaysOnTop: true // boolean switch; reads level from behavior above
}
}
With that in place, this.toolbarWindow.hide() from the domain service will:
setFocusable(false) on them.hide().mouseMove(-1, -1) to clear hover.setFocusable(true) restoration for the snapshot after 50ms.The domain service carries none of this code.
w.hide.bind(w) captures the native method with this correctly bound, so Electron's C++ bindings continue to see the real BrowserWindow..on('hide', ...), .once('close', ...)) is untouched — the quirks patch only the method slots, not the emitter wiring.onWindowCreated and after applyWindowBehavior fires. This ordering means the behavior layer's initial setter calls (e.g. first setAlwaysOnTop(true, level)) do not trigger the monkey-patched show/showInactive.behavior captures configuration that's non-hacky, cross-platform, and needed beyond the Electron constructor. WindowManager applies these on window creation via applyWindowBehavior (in src/main/core/window/behavior.ts).
| Field | Type | What it does |
|---|---|---|
hideOnBlur | boolean | Installs a blur listener that calls window.hide() (with optional runtime override via wm.behavior.setHideOnBlur(id, enabled)). |
alwaysOnTop | { level?: AlwaysOnTopLevel, relativeLevel?: number } | Supplies the level / relativeLevel to setAlwaysOnTop calls — the single source of truth, read by: (1) the initial application after create (when windowOptions.alwaysOnTop is true), (2) wm.behavior.setAlwaysOnTop(id, enabled) runtime calls, (3) the macReapplyAlwaysOnTop quirk. |
visibleOnAllWorkspaces | { enabled: boolean } & VisibleOnAllWorkspacesOptions | Runs window.setVisibleOnAllWorkspaces(enabled, options) once on create. Windows whose true/false options differ per call should not declare this (e.g. SelectionAction) — drive directly on BrowserWindow instead. |
macShowInDock | boolean | macOS-only default for whether a window of this type CONTRIBUTES to Dock visibility (Dock shown iff any alive window contributes). Existence-based, not visibility-based: hiding a contributing window does NOT hide the Dock (Cmd+W semantics). When omitted, defaults to true. false is for helper windows (floating panels, menu-bar style overlays) that should never affect the Dock. Runtime override via wm.behavior.setMacShowInDockByType(type, value) — set it to false before window.hide() to enter tray mode, true before window.show() to leave. No-op on Windows/Linux. |
Runtime setters for the behavior layer live on wm.behavior (a BehaviorController instance defined in src/main/core/window/behavior.ts). Grouping them under this sub-namespace mirrors the three-layer windowOptions / behavior / quirks split at the API surface.
| Setter | Purpose |
|---|---|
wm.behavior.setHideOnBlur(id, enabled) | Override the declared behavior.hideOnBlur per instance. Cleared on destroy and on pool releaseToPool — pool consumers that need a non-default value must re-apply after open() / reuse. No-op when the window does not declare behavior.hideOnBlur. |
wm.behavior.setAlwaysOnTop(id, enabled) | Toggle always-on-top using the level / relativeLevel declared in behavior.alwaysOnTop. When neither is declared, calls setAlwaysOnTop(enabled) with no level. |
wm.behavior.setMacShowInDockByType(type, value) | Override behavior.macShowInDock for an entire window type (not a single instance). Use for app-level tray-mode transitions: (Main, false) then hide() pulls the Dock icon down; (Main, true) then show() brings it back. Keyed by type so it can be set BEFORE the first instance exists (tray-on-launch). Multi-window safe: with Main + SubWindow both contributing, a wm.behavior.setMacShowInDockByType(Main, false) alone does NOT hide the Dock while any SubWindow is alive. |
setVisibleOnAllWorkspaces intentionally has no WM-level setter — consumers call it directly on the BrowserWindow when needed. See README → When to Provide a Runtime Setter.
Static BrowserWindowConstructorOptions that differ per OS go in windowOptions.platformOverrides. Only the branch matching the current runtime is deep-merged into the final config; unmatched branches are discarded, and the platformOverrides field itself is stripped before reaching new BrowserWindow(...).
windowOptions: {
width: 350, height: 43,
frame: false, transparent: true,
platformOverrides: {
mac: { type: 'panel', hiddenInMissionControl: true, acceptFirstMouse: true },
win: { type: 'toolbar', focusable: false },
linux: { type: 'toolbar' } // focusable is set at runtime by the domain service
},
webPreferences: { /* ... */ }
}
Precedence (later wins) when merging inside mergeWindowOptions:
baseOptions (registry windowOptions)baseOptions.platformOverrides[currentPlatform]overrides (via wm.open(type, { options }))overrides.platformOverrides[currentPlatform]webPreferences is deep-merged in the same order.
| Situation | Layer |
|---|---|
BrowserWindow constructor can accept it directly | windowOptions |
| Only a subset of OSes need a different static value | windowOptions.platformOverrides[mac/win/linux] |
Cross-platform, non-hacky declarative behavior (auto-hide on blur, initial setAlwaysOnTop level, dock visibility, initial setVisibleOnAllWorkspaces) | behavior |
| OS-specific bug workaround requiring a hide/show/close hook | quirks |
The layers are composable: Selection's toolbar uses all three (windowOptions.platformOverrides for static per-OS differences, behavior.hideOnBlur / behavior.alwaysOnTop / behavior.visibleOnAllWorkspaces / behavior.macShowInDock for declarative behavior, and quirks.* for the macOS hide/show hacks).
setAlwaysOnTop(false, level) — Electron ignores level when enabled is false. The WM wm.behavior.setAlwaysOnTop(id, false) preserves the registry-declared level arg only for signature symmetry; the effect is identical.VisibleOnAllWorkspacesOptions — both visibleOnFullScreen and skipTransformProcessType are documented as @platform darwin in Electron. They are silently ignored on Windows / Linux.setVisibleOnAllWorkspaces can put windows into a broken "floating popup" state on KDE Wayland. See MainWindowService.ts:573 for context. WM does not intervene; consumers using behavior.visibleOnAllWorkspaces on Linux should guard via runtime display-protocol checks if they see the regression.Parameters<> type derivation — AlwaysOnTopLevel is derived from Parameters<BrowserWindow['setAlwaysOnTop']>[1]. If Electron adds method overloads to setAlwaysOnTop, this derivation resolves against the last overload only and may silently narrow. Re-verify after Electron upgrades.