electron-ipc

📁 seanchiuai/multishot 📅 Jan 28, 2026
1
总安装量
1
周安装量
#50550
全站排名
安装命令
npx skills add https://github.com/seanchiuai/multishot --skill electron-ipc

Agent 安装分布

mcpjam 1
claude-code 1
kilo 1
windsurf 1
zencoder 1
crush 1

Skill 文档

Electron IPC & UI (MVP)

Files

  • src/main/index.ts – Main process, BrowserWindow config
  • src/main/ipc.ts – IPC handlers
  • src/preload/index.ts – Context bridge API
  • src/renderer/styles.css – Global styles (Tailwind + custom)
  • src/renderer/components/ – React UI components

Preload API (window.api)

interface API {
  // Invoke handlers (renderer → main)
  startRun(prompt: string, credentials: Credentials): Promise<void>
  selectWinner(agentId: string): Promise<void>
  cancelRun(): Promise<void>
  previewAll(): Promise<PreviewResult[]>

  // Event listeners (main → renderer)
  onAgentOutput(cb: (data: { agentId: string; chunk: string }) => void): () => void
  onAgentStatus(cb: (data: { agentId: string; status: AgentStatus }) => void): () => void
  onError(cb: (error: string) => void): () => void
}

interface Credentials {
  daytonaApiKey: string
  claudeOAuthToken?: string
  anthropicApiKey?: string
}

interface PreviewResult {
  agentId: string
  url: string | null
  error?: string
}

type AgentStatus = 'idle' | 'running' | 'completed' | 'failed'

IPC Communication

Main → Renderer Events

sendToRenderer('agent-output', { agentId, chunk })  // Terminal data
sendToRenderer('agent-status', { agentId, status }) // Status change
sendToRenderer('error', errorMessage)               // Error string

Renderer → Main Handlers

ipcMain.handle('start-run', async (_event, { prompt, credentials }) => { ... })
ipcMain.handle('select-winner', async (_event, { agentId }) => { ... })
ipcMain.handle('cancel-run', async () => { ... })
ipcMain.handle('preview-all', async () => { ... })

Output Buffering

const buffer = new OutputBuffer((agentId, data) => {
  sendToRenderer('agent-output', { agentId, chunk: data })
})

buffer.append(agentId, chunk)  // Add to buffer
buffer.flush(agentId)          // Force flush
buffer.flushAll()              // Flush all agents

BrowserWindow Configuration

Current config in src/main/index.ts:

const mainWindow = new BrowserWindow({
  width: 1400,
  height: 900,
  minWidth: 1000,
  minHeight: 700,
  show: false,
  autoHideMenuBar: true,
  backgroundColor: '#171717',
  webPreferences: {
    preload: join(__dirname, '../preload/index.js'),
    sandbox: false,
    contextIsolation: true,
    nodeIntegration: false
  }
})

UI Beautification Options

Custom Title Bar (Frameless Window)

// Hidden title bar with traffic lights (macOS)
const win = new BrowserWindow({
  titleBarStyle: 'hidden',
  trafficLightPosition: { x: 15, y: 15 }
})

// Hidden with custom overlay (cross-platform)
const win = new BrowserWindow({
  titleBarStyle: 'hidden',
  titleBarOverlay: {
    color: '#171717',
    symbolColor: '#ffffff',
    height: 40
  }
})

// Custom traffic light behavior (macOS)
const win = new BrowserWindow({
  titleBarStyle: 'hiddenInset'  // Inset traffic lights
})
const win = new BrowserWindow({
  titleBarStyle: 'customButtonsOnHover'  // Show on hover only
})

Platform Vibrancy Effects

// macOS Vibrancy
win.setVibrancy('sidebar')  // Options: titlebar, sidebar, window, hud, etc.
win.setVibrancy('under-window')  // Translucent background

// Windows 11 Mica/Acrylic (22H2+)
const win = new BrowserWindow({
  backgroundColor: '#00000000',  // Transparent for effects
  // ...
})
win.setBackgroundMaterial('mica')    // Long-lived windows
win.setBackgroundMaterial('acrylic') // Transient/popup windows

Transparent Window

const win = new BrowserWindow({
  frame: false,
  transparent: true,
  resizable: false,  // Required when transparent
  backgroundColor: '#00000000'
})

Window Controls Overlay API

Exposes native controls area to web content:

const win = new BrowserWindow({
  titleBarStyle: 'hidden',
  titleBarOverlay: true  // Enable CSS env() variables
})

In CSS, use safe area insets:

.titlebar {
  padding-top: env(titlebar-area-y, 0);
  padding-left: env(titlebar-area-x, 0);
  height: env(titlebar-area-height, 40px);
}

/* Draggable region */
.drag-region {
  -webkit-app-region: drag;
}

.no-drag {
  -webkit-app-region: no-drag;
}

Tailwind CSS Patterns

Current setup uses Tailwind with dark theme. Key patterns:

Color Palette (neutral-based)

bg-neutral-900  /* Main background #171717 */
bg-neutral-800  /* Cards, inputs */
bg-neutral-700  /* Borders, dividers */
text-neutral-100  /* Primary text */
text-neutral-400  /* Secondary text */
text-neutral-500  /* Muted text, placeholders */

Status Colors

/* Running */
bg-yellow-500/20 text-yellow-400 animate-pulse

/* Completed */
bg-green-500/20 text-green-400

/* Failed */
bg-red-500/20 text-red-400

/* Winner highlight */
border-green-500 bg-green-500/5

Interactive Elements

/* Buttons - Primary */
bg-blue-600 hover:bg-blue-500 disabled:bg-neutral-700

/* Buttons - Danger */
bg-red-600/20 text-red-400 hover:bg-red-600/30

/* Inputs */
bg-neutral-800 border-neutral-700 focus:ring-2 focus:ring-blue-500

/* Cards */
rounded-lg border border-neutral-700 bg-neutral-800

Useful Effects for Beautification

/* Glassmorphism */
backdrop-blur-md bg-white/10

/* Subtle gradients */
bg-gradient-to-br from-neutral-800 to-neutral-900

/* Glow effects */
shadow-lg shadow-blue-500/20

/* Smooth transitions */
transition-all duration-200

/* Hover lift */
hover:-translate-y-0.5 hover:shadow-lg

xterm.js Theme

Current terminal theme in Terminal.tsx:

const terminal = new XTerm({
  theme: {
    background: '#1e1e1e',
    foreground: '#d4d4d4',
    cursor: '#d4d4d4',
    selectionBackground: '#264f78',
    black: '#1e1e1e',
    red: '#f44747',
    green: '#6a9955',
    yellow: '#dcdcaa',
    blue: '#569cd6',
    magenta: '#c586c0',
    cyan: '#4ec9b0',
    white: '#d4d4d4'
  },
  fontSize: 12,
  fontFamily: 'Menlo, Monaco, "Courier New", monospace',
  scrollback: 10000,
  cursorBlink: false,
  disableStdin: true
})

Alternative themes to consider:

// Dracula
theme: {
  background: '#282a36',
  foreground: '#f8f8f2',
  cursor: '#f8f8f2',
  red: '#ff5555',
  green: '#50fa7b',
  yellow: '#f1fa8c',
  blue: '#bd93f9',
  magenta: '#ff79c6',
  cyan: '#8be9fd'
}

// One Dark
theme: {
  background: '#282c34',
  foreground: '#abb2bf',
  cursor: '#528bff',
  red: '#e06c75',
  green: '#98c379',
  yellow: '#e5c07b',
  blue: '#61afef',
  magenta: '#c678dd',
  cyan: '#56b6c2'
}

Component Architecture

App.tsx                    # State: phase, credentials, agents, winner
├── SetupWizard.tsx        # Credentials input form
└── GridView.tsx           # Header bar + 2x2 grid
    └── AgentCard.tsx      # Card with status badge + terminal
        └── Terminal.tsx   # xterm.js instance

App Phases

  1. setup – Show SetupWizard
  2. ready – Show prompt input + idle grid
  3. running – Agents executing, cancel available
  4. completed – All done, select winner

Cleanup

Always return unsubscribe function and call on unmount to prevent memory leaks:

useEffect(() => {
  const unsub = window.api.onAgentOutput(({ agentId, chunk }) => {
    terminals[agentId].write(chunk)
  })
  return () => unsub()
}, [])