本文へ移動
cccskills
無料GitHub で公開

desktop-multiwindow-electron

Multi-window management, WebContentsView, BaseWindow, window lifecycle, inter-window communication, state persistence

インストール方法を見る

含まれるファイル(5)

  • SKILL.md16.0 KB
  • examples/core.md16.1 KB
  • examples/inter-window-communication.md7.7 KB
  • metadata.yaml632 B
  • reference.md7.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Electron Multi-Window Patterns

Quick Guide: Use BrowserWindow for single-view windows. Use BaseWindow + WebContentsView for multi-view layouts (tabs, split panes, panels). BrowserView is deprecated since Electron 30 -- migrate to WebContentsView. Track windows with a Map<string, BrowserWindow> registry. Communicate between windows via the main process or MessagePort for direct renderer-to-renderer channels. Persist window bounds manually or use the upcoming windowStatePersistence API. Always close webContents explicitly when using BaseWindow -- unlike BrowserWindow, it does not auto-cleanup.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST close webContents explicitly when destroying a BaseWindow -- it does not auto-cleanup like BrowserWindow, causing memory leaks)

(You MUST use WebContentsView instead of BrowserView -- BrowserView is deprecated since Electron 30)

(You MUST route all inter-window communication through the main process or MessagePort -- never access another window's renderer directly)

(You MUST validate that saved window bounds are on a visible display before restoring -- monitors may disconnect between sessions)

</critical_requirements>


Auto-detection: multi-window, BaseWindow, WebContentsView, BrowserView migration, contentView, addChildView, removeChildView, parent window, child window, modal window, window registry, MessagePort, MessageChannelMain, window state persistence, screen API, workArea, getAllDisplays, split view, tabs, panels, window lifecycle, ready-to-show, window-all-closed

When to use:

  • Creating multi-view layouts (tabs, split panes, embedded panels) with BaseWindow + WebContentsView
  • Managing multiple BrowserWindow instances with a window registry
  • Migrating from deprecated BrowserView to WebContentsView
  • Setting up parent/child or modal windows
  • Communicating between windows (via main process relay or MessagePort)
  • Persisting and restoring window position, size, and display state
  • Placing windows on specific monitors using the screen API

When NOT to use:

  • Single-window apps with one view (BrowserWindow is sufficient on its own)
  • Choosing a UI framework for the renderer
  • IPC patterns between main and a single renderer (basic IPC is outside multi-window scope)
  • Styling or layout within a single renderer

Key patterns covered:

  • BaseWindow + WebContentsView for multi-view layouts
  • BrowserView to WebContentsView migration
  • Window lifecycle events (ready-to-show, close, closed)
  • Parent/child and modal windows
  • Window registry with Map-based tracking
  • Inter-window communication via main process and MessagePort
  • Window state persistence (bounds, maximized, fullscreen)
  • Multi-monitor placement with screen API

<philosophy>

Philosophy

Electron's window model has two tiers. BrowserWindow is the simple path: one window, one web view, automatic lifecycle management. BaseWindow + WebContentsView is the flexible path: one window shell containing multiple independently managed web views, each with its own renderer process and preload script.

The key architectural decision: use BrowserWindow for single-view windows, BaseWindow for multi-view layouts. BaseWindow trades convenience for control -- you manage view lifecycle, bounds, and cleanup explicitly.

When to use BaseWindow + WebContentsView:

  • Tab bars, split editors, preview panels, embedded browser views
  • Any layout where multiple independent web pages share one OS window
  • Applications migrating from deprecated BrowserView

When NOT to use BaseWindow:

  • Single-view windows (BrowserWindow is simpler and handles cleanup automatically)
  • Windows that only need a toolbar or status bar (a single BrowserWindow with HTML layout is sufficient)
</philosophy>
<patterns>

Core Patterns

Pattern 1: BaseWindow with WebContentsView

BaseWindow is the window shell; WebContentsView instances are the content. Each view has its own renderer process and preload script.

const { BaseWindow, WebContentsView } = require("electron");

const win = new BaseWindow({ width: 1200, height: 800 });

const sidebar = new WebContentsView({
  webPreferences: { preload: path.join(__dirname, "preload.js") },
});
const main = new WebContentsView({
  webPreferences: { preload: path.join(__dirname, "preload.js") },
});

win.contentView.addChildView(sidebar);
win.contentView.addChildView(main);

const SIDEBAR_WIDTH = 250;
sidebar.setBounds({ x: 0, y: 0, width: SIDEBAR_WIDTH, height: 800 });
main.setBounds({ x: SIDEBAR_WIDTH, y: 0, width: 950, height: 800 });

sidebar.webContents.loadFile("sidebar.html");
main.webContents.loadFile("main.html");

Key point: Each WebContentsView needs its own webPreferences and preload script. BaseWindow has no webContents of its own. See examples/core.md for complete split-view and tab examples with resize handling.


Pattern 2: BrowserView to WebContentsView Migration

BrowserView is deprecated since Electron 30. Migration is straightforward -- constructors have the same shape.

Deprecated (BrowserView)Replacement (WebContentsView)
new BrowserView(opts)new WebContentsView(opts)
win.addBrowserView(view)win.contentView.addChildView(view)
win.removeBrowserView(view)win.contentView.removeChildView(view)
win.getBrowserViews()win.contentView.children
win.setTopBrowserView(view)win.contentView.addChildView(view) (re-adding reorders)
view.setAutoResize({ vertical: true })Manual resize via win.on("resize", ...) + setBounds()

Gotcha: WebContentsView defaults to a white background; BrowserView defaulted to transparent. Set view.setBackgroundColor("#00000000") for transparency.

See examples/core.md for the complete migration pattern with auto-resize replacement.


Pattern 3: Window Lifecycle Events

Window events fire in a predictable order. Use ready-to-show to prevent visual flash, close to intercept (confirmations, state saving), closed for final cleanup.

const win = new BrowserWindow({ show: false });

// Prevent white flash -- show only after first paint
win.once("ready-to-show", () => {
  win.show();
});

// Intercept close for unsaved changes
win.on("close", (event) => {
  if (hasUnsavedChanges()) {
    event.preventDefault();
    promptSaveDialog(win);
  }
});

// Final cleanup after window is gone
win.on("closed", () => {
  windowRegistry.delete(win.id);
});

Gotcha: ready-to-show fires on BrowserWindow (which owns a webContents) but NOT on BaseWindow (which has no webContents). For BaseWindow, listen on the individual WebContentsView's webContents instead: view.webContents.once("ready-to-show", ...).

See examples/core.md for the full lifecycle sequence and BaseWindow workaround.


Pattern 4: Parent/Child and Modal Windows

Child windows always appear above their parent. Modal windows additionally disable the parent until closed.

const parent = new BrowserWindow({ width: 800, height: 600 });

// Child window: always on top of parent, non-blocking
const child = new BrowserWindow({
  parent,
  width: 400,
  height: 300,
});

// Modal window: blocks parent interaction
const modal = new BrowserWindow({
  parent,
  modal: true,
  show: false,
  width: 500,
  height: 400,
});
modal.once("ready-to-show", () => modal.show());

Platform behavior: On macOS, modal child windows display as sheets attached to the parent. On Windows/Linux, they display as separate windows with the parent disabled.

See examples/core.md for confirmation dialogs and settings windows.


Pattern 5: Window Registry

Track all open windows with a Map for reliable lookup, messaging, and cleanup.

const windowRegistry = new Map();

function createWindow(id, options) {
  const win = new BrowserWindow(options);
  windowRegistry.set(id, win);

  win.on("closed", () => {
    windowRegistry.delete(id);
  });

  return win;
}

// Find and focus a window by ID
function focusWindow(id) {
  const win = windowRegistry.get(id);
  if (!win) return;
  if (win.isMinimized()) win.restore();
  win.focus();
}

Key point: Use string IDs (not window objects) as keys. Clean up on closed event. The registry enables "show existing or create new" patterns for settings, about, and preferences windows.

See examples/core.md for the full singleton window pattern.


Pattern 6: Inter-Window Communication

Two patterns: main process relay for simple messages, MessagePort for direct high-frequency renderer-to-renderer channels.

// Pattern A: Main process relay
ipcMain.on("message-to-window", (_event, targetId, channel, data) => {
  const target = windowRegistry.get(targetId);
  if (target) target.webContents.send(channel, data);
});

// Pattern B: MessagePort -- direct renderer-to-renderer
const { MessageChannelMain } = require("electron");
const { port1, port2 } = new MessageChannelMain();

window1.webContents.postMessage("port", null, [port1]);
window2.webContents.postMessage("port", null, [port2]);

Key point: Main process relay is simpler but adds latency. MessagePort creates a direct channel after initial setup. Use postMessage (not send) to transfer ports.

See examples/inter-window-communication.md for complete examples of both patterns.


Pattern 7: Window State Persistence

Save and restore window bounds, maximized state, and display information across sessions.

function saveWindowState(win, stateFile) {
  const bounds = win.getBounds();
  const state = {
    bounds,
    isMaximized: win.isMaximized(),
    isFullScreen: win.isFullScreen(),
    displayId: screen.getDisplayMatching(bounds).id,
  };
  fs.writeFileSync(stateFile, JSON.stringify(state));
}

Key point: Always validate restored bounds against current displays -- a monitor may have been disconnected. Fall back to the primary display's work area if the saved display is unavailable.

See examples/core.md for the complete save/restore cycle with multi-monitor validation.


Pattern 8: Multi-Monitor Placement

Use the screen API to enumerate displays, find work areas, and place windows on specific monitors.

const { screen } = require("electron");

const displays = screen.getAllDisplays();
const externalDisplay = displays.find(
  (d) => d.bounds.x !== 0 || d.bounds.y !== 0,
);

if (externalDisplay) {
  const win = new BrowserWindow({
    x: externalDisplay.bounds.x,
    y: externalDisplay.bounds.y,
    width: 800,
    height: 600,
  });
}

Key point: Use workArea (not bounds) to avoid placing windows behind taskbars/docks. Listen for display-added, display-removed, and display-metrics-changed events to react to monitor changes at runtime.

See examples/core.md for display enumeration and safe placement.

</patterns>

<decision_framework>

Decision Framework

Window Type Selection

How many web views does this window need?
+-- One full-size view?
|   +-- BrowserWindow (simpler, automatic lifecycle)
+-- Multiple views (tabs, split pane, sidebar + content)?
|   +-- BaseWindow + WebContentsView
+-- Frameless window with custom layout?
    +-- One view? -> BrowserWindow with frame: false
    +-- Multiple views? -> BaseWindow with frame: false

Inter-Window Communication

How should windows communicate?
+-- Simple, infrequent messages?
|   +-- Main process relay (ipcMain/webContents.send)
+-- High-frequency or streaming data?
|   +-- MessagePort (direct renderer-to-renderer after setup)
+-- Shared state across windows?
    +-- Main process as single source of truth, push updates via IPC

Window Relationship

What is the relationship between windows?
+-- Independent (editor, browser tabs)?
|   +-- Separate BrowserWindow instances, window registry
+-- Always above parent (inspector, palette)?
|   +-- Child window: { parent: parentWin }
+-- Blocks parent (save dialog, settings confirmation)?
    +-- Modal window: { parent: parentWin, modal: true }

</decision_framework>


Detailed resources:


<red_flags>

RED FLAGS

Critical Issues:

  • Not closing webContents when destroying a BaseWindow -- causes memory leaks (BrowserWindow auto-cleans, BaseWindow does not)
  • Using deprecated BrowserView instead of WebContentsView -- deprecated since Electron 30
  • Direct renderer-to-renderer communication bypassing the main process -- violates process isolation
  • Restoring window bounds without checking if the target display still exists -- window appears off-screen

Architecture Issues:

  • Using BaseWindow for single-view windows -- unnecessary complexity, use BrowserWindow
  • Using BrowserView.setAutoResize() patterns with WebContentsView -- no equivalent exists, use manual resize listeners
  • Storing BrowserWindow objects as Map values without cleaning up on closed -- stale references
  • Creating child windows from the renderer process -- always create from main

Common Mistakes:

  • Expecting ready-to-show on BaseWindow -- it fires on BrowserWindow only; for BaseWindow, listen on view.webContents
  • Forgetting that WebContentsView defaults to white background (BrowserView defaulted to transparent) -- set "#00000000" explicitly
  • Using ipcRenderer.send() to transfer MessagePort -- only postMessage() can transfer ports
  • Placing windows using display.bounds instead of display.workArea -- window ends up behind taskbar/dock
  • Not handling display-removed event -- window references a disconnected monitor

Gotchas & Edge Cases:

  • Re-adding a child view with addChildView() moves it to the top of the z-order -- this is intentional, not a bug
  • setBounds() coordinates are relative to the parent view, not the screen
  • On macOS, modal windows display as sheets attached to the parent window
  • win.getBounds() returns the outer frame bounds on some platforms -- content area may differ
  • Each WebContentsView runs its own renderer process -- resource usage scales linearly with view count
  • MessagePortMain requires calling .start() before messages are delivered -- they queue until then

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST close webContents explicitly when destroying a BaseWindow -- it does not auto-cleanup like BrowserWindow, causing memory leaks)

(You MUST use WebContentsView instead of BrowserView -- BrowserView is deprecated since Electron 30)

(You MUST route all inter-window communication through the main process or MessagePort -- never access another window's renderer directly)

(You MUST validate that saved window bounds are on a visible display before restoring -- monitors may disconnect between sessions)

Failure to follow these rules will cause memory leaks, deprecated API warnings, broken inter-process communication, or off-screen windows.

</critical_reminders>

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

agents-inc のスキルをすべて見る

このスキルの問題を報告する