Claude-skill-registry electron-app

Build Electron desktop applications with IPC, window management, and packaging. Use when working on TitanMirror desktop app or any cross-platform desktop applications.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/electron-app" ~/.claude/skills/majiayu000-claude-skill-registry-electron-app && rm -rf "$T"
manifest: skills/data/electron-app/SKILL.md
source content

🖥️ Electron App Skill

Project Structure

📁 electron-app/
   📁 main/
      📄 main.js         ← Main process
      📄 preload.js      ← Bridge script
   📁 renderer/
      📄 index.html
      📄 renderer.js     ← Renderer process
      📄 styles.css
   📄 package.json

Main Process

const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  mainWindow.loadFile('renderer/index.html');
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

IPC Communication

Preload (Bridge)

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  // Renderer → Main
  sendCommand: (cmd) => ipcRenderer.send('command', cmd),
  
  // Main → Renderer
  onFrame: (callback) => ipcRenderer.on('frame', (_, data) => callback(data)),
  
  // Two-way
  getDeviceInfo: () => ipcRenderer.invoke('get-device-info')
});

Main Process Handlers

// One-way
ipcMain.on('command', (event, cmd) => {
  socket.emit('command', cmd);
});

// Two-way (async)
ipcMain.handle('get-device-info', async () => {
  return await fetchDeviceInfo();
});

// Send to renderer
mainWindow.webContents.send('frame', frameData);

Renderer Usage

// Send command
window.electronAPI.sendCommand({ type: 'click', x: 100, y: 200 });

// Receive frame
window.electronAPI.onFrame((data) => {
  renderFrame(data);
});

// Get data
const info = await window.electronAPI.getDeviceInfo();

Window Controls

// Fullscreen
mainWindow.setFullScreen(true);

// Always on top
mainWindow.setAlwaysOnTop(true);

// Resize
mainWindow.setSize(800, 600);

// Get bounds
const { width, height } = mainWindow.getBounds();

Packaging

package.json

{
  "name": "titan-mirror",
  "main": "main/main.js",
  "scripts": {
    "start": "electron .",
    "build": "electron-builder"
  },
  "build": {
    "appId": "com.example.titanmirror",
    "win": {
      "target": "nsis"
    },
    "mac": {
      "target": "dmg"
    }
  }
}

Build Commands

# Development
npm start

# Production build
npm run build

TitanMirror Integration

// Socket.IO in main process
const io = require('socket.io')(server);

io.on('connection', (socket) => {
  socket.on('frame', (data) => {
    // Forward to renderer
    mainWindow.webContents.send('frame', data);
  });
});

// Renderer receives
window.electronAPI.onFrame((frameData) => {
  const img = new Image();
  img.onload = () => ctx.drawImage(img, 0, 0);
  img.src = 'data:image/jpeg;base64,' + frameData;
});