| name | electrobun-window-management |
| description | Advanced window and view management patterns for Electrobun desktop applications. This skill covers multi-window architectures, BrowserView for embedded webviews, window lifecycle management, window orchestration, tab systems, and complex window hierarchies. Use this skill when building applications with multiple windows, implementing browser-like tab interfaces, managing parent-child window relationships, creating floating panels or toolbars, implementing picture-in-picture modes, managing window state persistence across sessions, or building applications that require sophisticated window coordination. Triggers include "multiple windows", "tab system", "BrowserView", "window orchestration", "floating window", "child window", "window state", "window manager", "multi-window app", or discussions about complex window management in Electrobun desktop applications. |
| license | MIT |
| metadata | {"author":"Blackboard","version":"1.0.0"} |
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");
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;
if (this.activeTab) {
const currentView = this.tabs.get(this.activeTab);
if (currentView) {
this.window.removeBrowserView(currentView);
}
}
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;
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);
});
}
}
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);
}
}
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;
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());
}
}
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();
broadcaster.register(mainWindow);
broadcaster.register(settingsWindow);
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);
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);
await hub.send("editor", "updateContent", { text: "Hello" });
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,
});
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,
});
parent.setEnabled(false);
modal.on("close", () => {
parent.setEnabled(true);
parent.focus();
});
return modal;
}
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