electrobun-window-management
Electrobun Window Management
Advanced patterns for managing windows and views in Electrobun applications.
Multi-Window Applications
Basic Window Manager
import { BrowserWindow } from "electrobun/bun";
class WindowManager {
private windows = new Map<string, BrowserWindow>();
createWindow(id: string, options: any) {
const win = new BrowserWindow(options);
this.windows.set(id, win);
win.on("close", () => {
this.windows.delete(id);
});
return win;
}
getWindow(id: string) {
return this.windows.get(id);
}
getAllWindows() {
return Array.from(this.windows.values());
}
closeWindow(id: string) {
const win = this.windows.get(id);
if (win) {
win.close();
}
}
closeAllWindows() {
this.windows.forEach(win => win.close());
this.windows.clear();
}
}
const windowManager = new WindowManager();
Window Factory Pattern
type WindowType = "main" | "settings" | "editor" | "preview";
interface WindowConfig {
type: WindowType;
url: string;
width: number;
height: number;
frame?: boolean;
parent?: BrowserWindow;
}
class WindowFactory {
private configs: Record<WindowType, Partial<WindowConfig>> = {
main: {
url: "views://main/index.html",
width: 1200,
height: 800,
frame: true,
},
settings: {
url: "views://settings/index.html",
width: 600,
height: 500,
frame: true,
},
editor: {
url: "views://editor/index.html",
width: 1000,
height: 700,
},
preview: {
url: "views://preview/index.html",
width: 800,
height: 600,
frame: false,
}
};
create(type: WindowType, overrides?: Partial<WindowConfig>) {
const config = { ...this.configs[type], ...overrides };
return new BrowserWindow(config);
}
}
const factory = new WindowFactory();
const mainWindow = factory.create("main");
const settingsWindow = factory.create("settings", { width: 700 });
BrowserView
BrowserView allows embedding webviews within a window without creating a separate window.
Basic BrowserView
import { BrowserWindow, BrowserView } from "electrobun/bun";
const win = new BrowserWindow({
title: "Browser App",
width: 1200,
height: 800,
});
const view = new BrowserView({
bounds: { x: 0, y: 60, width: 1200, height: 740 }
});
win.addBrowserView(view);
view.loadURL("https://example.com");
// Update bounds on window resize
win.on("resize", ({ width, height }) => {
view.setBounds({ x: 0, y: 60, width, height: height - 60 });
});
Tab System with BrowserView
class TabManager {
private tabs: Map<string, BrowserView> = new Map();
private activeTab: string | null = null;
private window: BrowserWindow;
constructor(window: BrowserWindow) {
this.window = window;
}
createTab(id: string, url: string) {
const view = new BrowserView({
bounds: this.getViewBounds()
});
view.loadURL(url);
this.tabs.set(id, view);
if (!this.activeTab) {
this.activateTab(id);
}
return view;
}
activateTab(id: string) {
const view = this.tabs.get(id);
if (!view) return;
// Hide current tab
if (this.activeTab) {
const currentView = this.tabs.get(this.activeTab);
if (currentView) {
this.window.removeBrowserView(currentView);
}
}
// Show new tab
this.window.addBrowserView(view);
this.activeTab = id;
}
closeTab(id: string) {
const view = this.tabs.get(id);
if (!view) return;
if (this.activeTab === id) {
this.window.removeBrowserView(view);
this.activeTab = null;
// Activate another tab
const otherTab = Array.from(this.tabs.keys()).find(k => k !== id);
if (otherTab) {
this.activateTab(otherTab);
}
}
this.tabs.delete(id);
}
private getViewBounds() {
const { width, height } = this.window.getBounds();
return { x: 0, y: 60, width, height: height - 60 };
}
updateBounds() {
const bounds = this.getViewBounds();
this.tabs.forEach(view => {
view.setBounds(bounds);
});
}
}
// Usage
const tabManager = new TabManager(mainWindow);
tabManager.createTab("tab1", "https://example.com");
tabManager.createTab("tab2", "https://github.com");
tabManager.activateTab("tab2");
mainWindow.on("resize", () => {
tabManager.updateBounds();
});
Window Lifecycle Management
Window State Persistence
import { paths } from "electrobun/bun";
import { join } from "path";
interface WindowState {
x: number;
y: number;
width: number;
height: number;
maximized: boolean;
}
class WindowStateManager {
private stateFile: string;
constructor(windowId: string) {
this.stateFile = join(paths.userData, `window-state-${windowId}.json`);
}
async save(state: WindowState) {
await Bun.write(this.stateFile, JSON.stringify(state, null, 2));
}
async load(): Promise<WindowState | null> {
try {
const content = await Bun.file(this.stateFile).text();
return JSON.parse(content);
} catch {
return null;
}
}
async restore(window: BrowserWindow) {
const state = await this.load();
if (!state) return false;
window.move({ x: state.x, y: state.y });
window.resize({ width: state.width, height: state.height });
if (state.maximized) {
window.maximize();
}
return true;
}
startTracking(window: BrowserWindow) {
let saveTimeout: Timer;
const saveState = () => {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(async () => {
const bounds = window.getBounds();
await this.save({
x: bounds.x,
y: bounds.y,
width: bounds.width,
height: bounds.height,
maximized: window.isMaximized(),
});
}, 500);
};
window.on("move", saveState);
window.on("resize", saveState);
}
}
// Usage
const stateManager = new WindowStateManager("main");
const win = new BrowserWindow({ width: 1200, height: 800 });
await stateManager.restore(win);
stateManager.startTracking(win);
Window Groups & Parent-Child
class WindowGroup {
private parent: BrowserWindow;
private children: BrowserWindow[] = [];
constructor(parent: BrowserWindow) {
this.parent = parent;
// Close children when parent closes
parent.on("close", () => {
this.closeAllChildren();
});
}
addChild(options: any) {
const child = new BrowserWindow({
...options,
parent: this.parent,
});
this.children.push(child);
child.on("close", () => {
const index = this.children.indexOf(child);
if (index > -1) {
this.children.splice(index, 1);
}
});
return child;
}
closeAllChildren() {
this.children.forEach(child => child.close());
this.children = [];
}
showAll() {
this.parent.show();
this.children.forEach(child => child.show());
}
hideAll() {
this.parent.hide();
this.children.forEach(child => child.hide());
}
}
// Usage
const mainWindow = new BrowserWindow({ width: 1200, height: 800 });
const group = new WindowGroup(mainWindow);
const palette = group.addChild({
url: "views://palette/index.html",
width: 250,
height: 600,
frame: false,
});
const inspector = group.addChild({
url: "views://inspector/index.html",
width: 300,
height: 400,
});
Window Orchestration
Broadcasting to All Windows
class WindowBroadcaster {
private windows: Set<BrowserWindow> = new Set();
register(window: BrowserWindow) {
this.windows.add(window);
window.on("close", () => {
this.windows.delete(window);
});
}
async broadcast(method: string, ...args: any[]) {
const promises = Array.from(this.windows).map(win =>
win.rpc[method](...args).catch(err => {
console.error(`Broadcast to window failed:`, err);
})
);
await Promise.allSettled(promises);
}
}
const broadcaster = new WindowBroadcaster();
// Register windows
broadcaster.register(mainWindow);
broadcaster.register(settingsWindow);
// Broadcast to all
await broadcaster.broadcast("updateTheme", { theme: "dark" });
Window Communication Hub
class WindowHub {
private windows = new Map<string, BrowserWindow>();
register(id: string, window: BrowserWindow) {
this.windows.set(id, window);
// Setup RPC handler for messaging
window.defineRpc({
handlers: {
sendToWindow: async (targetId: string, method: string, ...args: any[]) => {
return this.send(targetId, method, ...args);
}
}
});
}
async send(targetId: string, method: string, ...args: any[]) {
const target = this.windows.get(targetId);
if (!target) {
throw new Error(`Window ${targetId} not found`);
}
return await target.rpc[method](...args);
}
async broadcast(method: string, ...args: any[]) {
const results = new Map();
for (const [id, window] of this.windows) {
try {
const result = await window.rpc[method](...args);
results.set(id, result);
} catch (err) {
results.set(id, { error: err.message });
}
}
return results;
}
}
const hub = new WindowHub();
hub.register("main", mainWindow);
hub.register("editor", editorWindow);
// Send from main to editor
await hub.send("editor", "updateContent", { text: "Hello" });
// Broadcast to all
await hub.broadcast("refreshData");
Floating Windows & Overlays
Always-on-Top Window
const floatingWindow = new BrowserWindow({
url: "views://floating/index.html",
width: 300,
height: 200,
frame: false,
alwaysOnTop: true,
skipTaskbar: true,
});
// Toggle always-on-top
floatingWindow.setAlwaysOnTop(!floatingWindow.isAlwaysOnTop());
Picture-in-Picture Mode
class PictureInPicture {
private pipWindow: BrowserWindow | null = null;
private sourceWindow: BrowserWindow;
constructor(sourceWindow: BrowserWindow) {
this.sourceWindow = sourceWindow;
}
enter(url: string) {
if (this.pipWindow) return;
this.pipWindow = new BrowserWindow({
url,
width: 400,
height: 300,
frame: false,
alwaysOnTop: true,
resizable: true,
});
this.pipWindow.on("close", () => {
this.pipWindow = null;
});
}
exit() {
if (this.pipWindow) {
this.pipWindow.close();
this.pipWindow = null;
}
}
toggle(url: string) {
if (this.pipWindow) {
this.exit();
} else {
this.enter(url);
}
}
}
const pip = new PictureInPicture(mainWindow);
pip.enter("views://video-player/index.html");
Advanced Patterns
Modal Dialogs
function showModal(parent: BrowserWindow, options: any) {
const modal = new BrowserWindow({
...options,
parent,
modal: true,
frame: false,
});
// Disable parent interaction
parent.setEnabled(false);
modal.on("close", () => {
parent.setEnabled(true);
parent.focus();
});
return modal;
}
// Usage
const confirmDialog = showModal(mainWindow, {
url: "views://confirm/index.html",
width: 400,
height: 200,
});
Window Positioning
function centerWindow(window: BrowserWindow) {
const { screen } = require("electrobun/bun");
const display = screen.getPrimaryDisplay();
const { width: screenWidth, height: screenHeight } = display.workArea;
const { width, height } = window.getBounds();
const x = Math.floor((screenWidth - width) / 2);
const y = Math.floor((screenHeight - height) / 2);
window.move({ x, y });
}
function cascadeWindows(windows: BrowserWindow[]) {
const offset = 30;
windows.forEach((win, index) => {
win.move({
x: 100 + (index * offset),
y: 100 + (index * offset)
});
});
}
Resources
For more on Electrobun:
- Core skill:
electrobun- Basic window creation and APIs - RPC patterns:
electrobun-rpc-patterns- Window-to-window RPC - Native UI:
electrobun-native-ui- Menus and tray integration
More from rajavijayach/electrobun-skills
electrobun-native-ui
Native UI integration for Electrobun desktop applications including ApplicationMenu, ContextMenu, system Tray, native dialogs, keyboard shortcuts, and platform-specific UI patterns. This skill covers creating application menus with submenus and accelerators, context menus triggered by right-click, system tray icons with menus, file/folder dialogs, message boxes, notification systems, global keyboard shortcuts, menu item roles, dynamic menu updates, platform-specific menu conventions (macOS menu bar, Windows system menu), drag-and-drop integration, and native theming. Use when implementing application menus, adding system tray functionality, creating context menus, showing file pickers, implementing keyboard shortcuts, displaying notifications or dialogs, or building platform-native UI experiences. Triggers include "menu", "tray icon", "context menu", "file dialog", "shortcuts", "accelerator", "native dialog", "system tray", "notification", "menu bar", or "right-click menu".
62electrobun-distribution
Packaging, code signing, notarization, and distribution for Electrobun desktop applications. This skill covers building production bundles, creating installers and distributable packages, code signing for Windows and macOS, Apple notarization for Gatekeeper, auto-updater implementation, delta updates, update servers, cross-platform build processes, CI/CD integration, app icons and resources, version management, release workflows, Windows SmartScreen requirements, macOS DMG creation, Linux package formats (deb, rpm, AppImage), and distribution best practices. Use when preparing app for production, implementing auto-updates, setting up code signing certificates, troubleshooting distribution issues, creating installers, configuring update servers, building for multiple platforms, or releasing new versions. Triggers include "build", "package", "distribute", "code sign", "notarize", "installer", "auto-update", "release", "production build", "DMG", "updater", "delta update", or "certificate".
52electrobun-rpc-patterns
Advanced type-safe RPC patterns for Electrobun desktop applications. Covers bidirectional main↔webview communication, type safety with TypeScript, error handling and validation, performance optimization, streaming data patterns, batch operations, retry strategies, event-based communication, shared type definitions, RPC middleware, request/response patterns, and complex data synchronization. Use this skill when implementing complex communication between main and webview processes, need type-safe RPC with full IntelliSense, handling large data transfers, implementing real-time updates, building type-safe APIs between processes, debugging RPC issues, optimizing RPC performance, or implementing advanced patterns like streaming, batching, or pub/sub. Triggers include "RPC", "main webview communication", "type-safe RPC", "bidirectional RPC", "RPC performance", "RPC error handling", "shared types", "process communication", or "IPC patterns".
51electrobun-debugging
Development workflow, debugging, and troubleshooting for Electrobun desktop applications. This skill covers debugging the main process (Bun) and webview processes, Chrome DevTools integration, console logging strategies, error handling, performance profiling, memory leak detection, build error troubleshooting, common runtime errors, development environment setup, hot reload configuration, source maps, breakpoint debugging, network inspection, WebView debugging on different platforms, native module debugging, and systematic debugging approaches. Use when encountering build failures, runtime errors, crashes, performance issues, debugging RPC communication, inspecting webview DOM, profiling CPU/memory usage, troubleshooting platform-specific issues, or setting up development workflow. Triggers include "debug", "error", "crash", "troubleshoot", "DevTools", "inspect", "breakpoint", "profiling", "performance issue", "build error", "not working", or "logging".
40