skills/rajavijayach/electrobun-skills/electrobun-window-management

electrobun-window-management

SKILL.md

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
Weekly Installs
32
GitHub Stars
3
First Seen
Feb 21, 2026
Installed on
codex32
cursor31
gemini-cli31
amp31
github-copilot31
kimi-cli31