Back to Cherry Studio

WindowManager API Reference

docs/references/window-manager/window-manager-api-reference.md

2.0.015.2 KB
Original Source

WindowManager API Reference

Full method reference for WindowManager. For conceptual guidance and when-to-use each group, see Usage Guide.

Open / Create / Close

Two layers: Consumer methods are the universal API and should be used by all business code. Internal methods are lower-level primitives for defensive assertions or pool-wide shutdown — consumer code should not reach for them. See Window API layers: consumer vs internal.

MethodLayerSignatureDescription
openConsumer(type: WindowType, args?: OpenWindowArgs) => stringLifecycle-aware open: singleton reuse, pool recycle, or fresh create per registry lifecycle. Returns window ID.
closeConsumer(windowId: string) => booleanLifecycle-aware release: destroys default and singleton-without-config windows; hides pooled / singleton-with-retention windows into the warmup state machine (GC destroys per config).
createInternal(type: WindowType, args?: OpenWindowArgs) => stringForce fresh creation; throws if a singleton of this type already exists. Use only as a defensive assertion — consumer code should use open() + onWindowCreatedByType instead.
destroyInternal(windowId: string) => booleanForce destroy via window.destroy(), which skips the close event — and therefore skips the pool's close interception, bypassing pool recycling. Non-pooled windows: identical to close(). Pooled windows: use suspendPool(type) for pool-wide shutdown instead of destroying individual pooled windows.

Window Operations

MethodSignatureDescription
show(windowId: string) => booleanShow a window. Does NOT change macOS Dock state — the Dock tracks window existence + per-type override, not visibility (matching native macOS: Cmd+W hiding a window does not remove the app from the Dock).
hide(windowId: string) => booleanHide a window. Does NOT change macOS Dock state — same reason as show. If callers want the Dock to disappear too (tray-mode UX), use wm.behavior.setMacShowInDockByType BEFORE hide.
minimize(windowId: string) => booleanMinimize a window.
maximize(windowId: string) => booleanToggle maximize/unmaximize.
restore(windowId: string) => booleanRestore a minimized window.
focus(windowId: string) => booleanFocus a window.

Behavior Runtime Setters

These operate on the declarative behavior layer per instance and are exposed on wm.behavior (a BehaviorController instance). See Platform Configuration → Declarative Behavior Layer for field semantics.

MethodSignatureDescription
wm.behavior.setHideOnBlur(windowId: string, enabled: boolean) => voidOverride the declared behavior.hideOnBlur at runtime. enabled: true keeps auto-hide on; enabled: false suppresses (effectively "pinned"). No-op when the window type does not declare behavior.hideOnBlur (no listener to override). Override is cleared on window destroy and on pool releaseToPool.
wm.behavior.setAlwaysOnTop(windowId: string, enabled: boolean) => voidToggle always-on-top using level / relativeLevel from behavior.alwaysOnTop (single source of truth). When neither is declared, setAlwaysOnTop(enabled) is called with no level — matching Electron's default.
wm.behavior.setMacShowInDockByType(type: WindowType, value: boolean) => voidOverride behavior.macShowInDock for an entire type at runtime. Use this to express "app is entering / leaving tray mode": (Main, false) before window.hide() makes the Dock track the transition; (Main, true) before window.show() lifts the suppression. Keyed by type (not windowId) so it can be set BEFORE the first instance exists (e.g. tray-on-launch path). When multiple window types contribute (e.g. Main + SubWindow), the Dock stays visible as long as any contributing type is alive — wm.behavior.setMacShowInDockByType(Main, false) will not hide the Dock if a SubWindow is still present.

No WM-level setVisibleOnAllWorkspaces is provided: its options differ per call in real usage (e.g. SelectionAction's full-screen show sequence), and WM has no state to maintain. Consumers call window.setVisibleOnAllWorkspaces(enabled, options) directly on the BrowserWindow instance. See README → When to Provide a Runtime Setter for the decision rule.

Bounds Persistence

Top-level primitives for the declarative rememberBounds capability (singleton-only). These are WindowManager methods, not part of wm.behavior. See README → Bounds Persistence.

MethodSignatureDescription
setRememberBounds(type: WindowType, enabled: boolean) => voidRuntime toggle for the rememberBounds capability, orthogonal to the registry flag. true persists position/size on teardown and restores on the next open; false stops persisting AND drops the saved record immediately, so the next open uses the registry default. Affects restore on the next open; the live window keeps its geometry.
peekWindowBounds(type: WindowType) => WindowBoundsState | undefinedRead a type's saved bounds without restoring them. Lets a consumer apply state WindowManager does not — e.g. MainWindowService re-applies the saved maximized flag on its own show schedule. undefined when nothing is saved.

Queries

Naming convention: methods with Info in the name return serializable WindowInfo snapshots (safe across IPC); methods without it return live BrowserWindow instances.

MethodSignatureDescription
getWindow(windowId: string) => BrowserWindow | undefinedGet BrowserWindow instance by ID.
getWindowInfo(windowId: string) => WindowInfo | undefinedGet serializable window metadata.
getWindowType(windowId: string) => WindowType | undefinedGet a window's registered type by ID (O(1); undefined if unknown/closed).
getWindowsByType(type: WindowType) => BrowserWindow[]Get all live window instances of a specific type (skips destroyed).
getWindowInfosByType(type: WindowType) => WindowInfo[]Get serializable metadata for all windows of a specific type.
getWindowId(window: BrowserWindow) => string | undefinedResolve window ID from BrowserWindow.
getWindowIdByWebContents(wc: WebContents) => string | undefinedResolve window ID from WebContents (e.g., IPC event.sender).
count(getter)Number of managed windows.

Broadcast

MethodSignatureDescription
broadcast(channel: string, ...args: unknown[]) => voidSend IPC to all managed windows. Skips destroyed windows.
broadcastToType(type: WindowType, channel: string, ...args: unknown[]) => voidSend IPC to windows of a specific type.

Init Data

MethodSignatureDescription
open<T>(type: WindowType, args?: { initData?: T, options?: Partial<WindowOptions> }) => stringWhen args.initData is supplied, written atomically to the store before the method returns; also pushed to the renderer as the WindowManager_Reused payload on reuse paths.
create<T>(type: WindowType, args?: { initData?: T, options?: Partial<WindowOptions> }) => stringSame atomicity as open, but never fires Reused (all create paths are fresh creation).
setInitData(windowId: string, data: unknown) => voidLow-level primitive. Prefer the open/create args form in new code.
getInitData(windowId: string) => unknown | nullRetrieve initialization data. Cleared on pool release; preserved on singleton hide.
pushInitData<T>(windowId: string, data: T) => booleanPush fresh init data to an already-open window. Writes the store and fires WindowManager_Reused in one step. Returns false if the window is missing or destroyed. Main-process only.
pushInitDataToType<T>(type: WindowType, data: T) => numberSame as pushInitData but fans out to every live window of the given type. Returns the number of windows that received the event. Does not filter by visibility — idle pooled windows receive the payload too.

Timing contract:

  • Cold start (fresh creation): createWindow writes initData to the store synchronously before returning, so any getInitData invoke from the renderer (after React mounts) sees the fresh value. The renderer should use the useWindowInitData hook — it handles the invoke on mount automatically.
  • Reuse (pool recycle / singleton reopen): open() simultaneously writes to the store AND fires WindowManager_Reused with the same payload. The useWindowInitData hook updates its state directly from the event payload — no round-trip.
  • No initData on a reuse call: the event is NOT fired. No "empty Reused" events — the hook therefore never needs a fallback invoke.
  • Live update (already-open window): call pushInitData / pushInitDataToType from any main-process service. Both paths reuse the WindowManager_Reused channel, so useWindowInitData picks up the new payload in-place with no remount — useful for "swap the visible window's context without close()+open() flicker". Unlike reuse, these methods forbid undefined payloads: pushing nothing has no meaningful semantics here.

webContents.send is fire-and-forget and does not buffer messages sent before the renderer registers listeners. This is exactly why fresh windows can't use PUSH — they still must PULL via getInitData on mount.

Pool Management

MethodSignatureDescription
suspendPool(type: WindowType) => numberSuspend pool: destroy idle windows, disable pool tracking. Returns count destroyed.
resumePool(type: WindowType) => voidResume pool: restore lifecycle behavior, trigger eager warmup if configured.

See Suspend / Resume for semantics while suspended.

Title Bar

MethodSignatureDescription
setTitleBarOverlay(options: TitleBarOverlayOptions) => voidUpdate title bar overlay on all windows with overlay configured.

Renderer IPC Surface

All methods above are main-process APIs. WindowManager also exposes an IPC surface so the renderer can drive window operations for itself. Channel constants live in src/shared/IpcChannel.ts; handlers are registered in WindowManager.registerIpcHandlers().

Preload only wraps getInitData as window.api.windowManager.getInitData(). The other channels are invoked directly via window.electron.ipcRenderer.invoke(IpcChannel.WindowManager_*, ...). WindowManager_Reused is a push-only channel (main → renderer) — see Warmup Mechanics → WindowManager_Reused IPC.

ChannelDirectionArgsEffect
WindowManager_Openrenderer → main(type, initData?)wm.open(type, { initData }). Returns window ID. Throws if type is not registered.
WindowManager_GetInitDatarenderer → mainwm.getInitData(senderWindowId). Returns stored init data or null.
WindowManager_Closerenderer → main(type?)wm.close(resolveTargetWindowId(sender, type)). Returns boolean.
WindowManager_Showrenderer → main(type?)wm.show(...).
WindowManager_Hiderenderer → main(type?)wm.hide(...).
WindowManager_Minimizerenderer → main(type?)wm.minimize(...).
WindowManager_Maximizerenderer → main(type?)wm.maximize(...).
WindowManager_Focusrenderer → main(type?)wm.focus(...).
WindowManager_Reusedmain → renderer (push)(payload)Fires on pool recycle or singleton reopen when the caller supplied initData.

Target resolution for the optional type argument (Close / Show / Hide / Minimize / Maximize / Focus):

  • No type: the target is the sender's own window, resolved via getWindowIdByWebContents(event.sender). This is the common case — a window acting on itself.
  • With type: the target must be a singleton — the first (and only) window of that type. default and pooled lifecycles are not supported for cross-window targeting via IPC; the call silently returns false and the operation is a no-op.

The bare renderer consumption pattern for Reused uses ipcRenderer.on(IpcChannel.WindowManager_Reused, ...) — but most renderer code should prefer the useWindowInitData hook, which encapsulates both cold-start getInitData invoke and reuse payload delivery.

Events

Pooled windows traverse a four-stage conceptual lifecycle, but only the endpoints have dedicated events:

Created ──▶ [Released ──▶ Recycled ──▶ Released ──▶ ...] ──▶ Destroyed

For non-pooled windows, the same two endpoints apply without any intermediate stages.

EventTypeDescription
onWindowCreatedEvent<ManagedWindow>Fires when a new window is created (before content loads). Fresh-path only for pooled windows.
onWindowDestroyedEvent<ManagedWindow>Fires when a window is truly destroyed (not on pool release).
onWindowCreatedByType(type, listener)(type, listener) => DisposableConvenience variant of onWindowCreated that filters to a single WindowType. Equivalent to onWindowCreated + an inline if (managed.type === type) guard, but avoids the boilerplate at every call site. Prefer this for single-type subscriptions (the typical consumer case).
onWindowDestroyedByType(type, listener)(type, listener) => DisposableType-filtered counterpart to onWindowDestroyed. Same filtering semantics as onWindowCreatedByType.

The intermediate Released and Recycled stages have no dedicated events — side effects on hide / close / show should be expressed as declarative Platform Quirks, and per-session data on recycle is delivered via the WindowManager_Reused IPC payload (see Init Data).

Usage notes for pooled windows:

  • Do NOT set paintWhenInitiallyHidden: false on pooled windows — it suppresses the native ready-to-show event, breaking the pool's fresh-window auto-show path (showMode === 'auto' listens for ready-to-show). It is NOT an acceptable workaround for "show only when content ready" — use showMode: 'manual' + consumer-driven show for that, or rely on the reuse-path Reused payload to ensure the renderer has data before .show() is called.
  • macOS focus / hover / always-on-top workarounds are declarative — see Platform Quirks.