К основному содержанию
скилл CX Codex

electron-dev — скилл для Claude Code: Electron с React и Vite

Скилл для сборки десктопных приложений на Electron с React, TypeScript и Vite: IPC, трей, PTY-терминалы, упаковка.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add jamditis/claude-skills-journalism --skill electron-dev -a claude-code
Открыть на GitHub

Что делает electron-dev

Скилл описывает рабочие приёмы для Electron-приложений на React и TypeScript. Внутри — готовые шаблоны кода: настройка BrowserWindow с contextIsolation и sandbox, безопасный мост через contextBridge, обработчики ipcMain.handle и отправка событий в рендерер.

Отдельные разделы про системный трей с Tray и Menu, глобальные горячие клавиши с проверкой конфликтов, встроенный терминал на node-pty и запись звука через MediaRecorder. Есть структура проекта с разделением electron/ и src/, а также заметки про Electron Fuses и проверку целостности ASAR — это опциональные переключатели, которые включают вручную для продакшена.

Учтите: примеры рассчитаны на Electron 28+, где contextIsolation и sandbox уже включены по умолчанию. Если ваш проект на более старой версии, поведение будет другим — это повод проверить конфиг перед тем, как копировать код.

Какие задачи решает

  • настроить безопасный IPC между main и renderer
  • добавить иконку в системный трей с меню
  • встроить терминал на node-pty в окно приложения
  • записать звук с микрофона и отправить на транскрипцию
  • собрать структуру проекта Electron + React + Vite

Примеры запросов агенту

  • Помоги настроить IPC между main и renderer по паттерну из скилла electron-dev
  • Добавь системный трей с меню Show и Quit, как в electron-dev
  • Сделай встроенный терминал на node-pty с обработкой resize

Как установить electron-dev

1Через Skills CLI (Vercel)

npx skills add jamditis/claude-skills-journalism --skill electron-dev -a claude-code

2Вручную: положить файлы

~/.claude/skills/electron-dev/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/electron-dev/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о electron-dev

Как установить electron-dev в Claude?

Выполните npx skills add jamditis/claude-skills-journalism --skill electron-dev -a claude-code. Скилл появится в ~/.claude/skills/electron-dev/SKILL.md.

Что делает electron-dev?

Даёт шаблоны и практики для Electron-приложений на React, TypeScript и Vite: IPC, окна и трей, PTY-терминалы, запись звука, упаковка.

Для какой версии Electron подходит скилл?

Примеры рассчитаны на Electron 28+, где contextIsolation и sandbox включены по умолчанию. На более старых версиях их нужно задавать явно.

Исходный текст

SKILL.md · MIT
---
name: electron-dev
description: Electron desktop apps with React, TypeScript, and Vite. Use for IPC, window/tray, PTY terminals, WebRTC, and packaging.
---

# Electron desktop development

Patterns and practices for building production-quality Electron applications with React and TypeScript.

## Security baseline (Electron 30+)

Electron's defaults have hardened over the past several releases. As of Electron 28+, `contextIsolation: true` and `sandbox: true` are the defaults for new BrowserWindow instances, most security advice from older guides assumed you had to opt in. You don't anymore; you have to opt OUT, and you should not.

Set explicitly anyway, so a config drift never weakens the security model:

```javascript
const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: true,        // default since 12, mandatory for any prod app
    sandbox: true,                  // default since 28; renderer runs sandboxed
    nodeIntegration: false,         // never enable in renderer
    webSecurity: true,              // never disable
    preload: path.join(__dirname, 'preload.cjs')
  }
});
```

Validate every IPC message in main. Don't trust the renderer.

### Electron Fuses + ASAR integrity

Electron Fuses are package-time toggles baked into the binary. The two relevant for security distribution:

- `EnableEmbeddedAsarIntegrityValidation`, verifies the app.asar hash at runtime against a hash embedded in the binary. Defends against attackers swapping the asar contents post-install.
- `OnlyLoadAppFromAsar`, refuses to load app code from anywhere except the validated asar.

These are **opt-in**, not default. Enable both for production. Requires `@electron/asar` 3.1.0+ to generate the asar with embeddable integrity. electron-builder configures this via `electronFuses` in the build config; `@electron/fuses` does it programmatically.

CVE-2023-44402 (ASAR integrity bypass via filetype confusion) was the canonical motivation here, without integrity + only-load-from-asar, an attacker who can modify app files can swap behavior silently.

### Common renderer-side risks

- **Preload script confusion**, only expose narrow, typed surfaces via `contextBridge.exposeInMainWorld`. Never re-export `ipcRenderer` itself; expose specific methods that map to specific channels.
- **`file://` IPC and navigation**, restrict navigation with `webContents.on('will-navigate', e => e.preventDefault())` for windows that shouldn't change URL. Deny `setWindowOpenHandler` requests by default; allow-list specific origins.
- **`shell.openExternal` with user input**, validate the URL scheme before opening. An attacker-controlled `file://` or `javascript:` URL hands them code execution.

## Architecture patterns

### Project structure
```
app/
├── electron/
│   ├── main.cjs              # Main process (CommonJS required)
│   ├── preload.cjs           # Context bridge for secure IPC
│   └── server.cjs            # Optional: WebSocket/HTTP server
├── src/
│   ├── components/           # React components
│   ├── services/             # Business logic (API clients, Firebase)
│   ├── utils/                # Utilities (audio, formatting)
│   ├── types.ts              # TypeScript interfaces
│   ├── App.tsx               # Root component
│   └── index.tsx             # React entry
├── assets/                   # Icons, sounds, images
├── package.json
├── vite.config.ts
└── electron-builder.yml      # Build configuration
```

### IPC communication pattern

**Main process (main.cjs):**
```javascript
const { ipcMain } = require('electron');

// Handle async requests from renderer
ipcMain.handle('action-name', async (event, args) => {
  try {
    const result = await someAsyncOperation(args);
    return { success: true, data: result };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Send data to renderer
mainWindow.webContents.send('event-name', data);
```

**Preload script (preload.cjs):**
```javascript
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electron', {
  actionName: (args) => ipcRenderer.invoke('action-name', args),
  onEventName: (callback) => {
    const handler = (event, data) => callback(data);
    ipcRenderer.on('event-name', handler);
    return () => ipcRenderer.removeListener('event-name', handler);
  }
});
```

**Renderer (React):**
```typescript
const result = await window.electron.actionName(args);

useEffect(() => {
  return window.electron.onEventName((data) => {
    setState(data);
  });
}, []);
```

## System tray integration

```javascript
const { Tray, Menu, nativeImage } = require('electron');

let tray = null;

function createTray() {
  const icon = nativeImage.createFromPath(path.join(__dirname, '../assets/tray-icon.png'));
  tray = new Tray(icon.resize({ width: 16, height: 16 }));

  tray.setToolTip('App Name');
  tray.setContextMenu(Menu.buildFromTemplate([
    { label: 'Show', click: () => mainWindow.show() },
    { label: 'Quit', click: () => app.quit() }
  ]));

  tray.on('click', () => {
    mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
  });
}

// Hide to tray instead of closing
mainWindow.on('close', (event) => {
  if (!app.isQuitting) {
    event.preventDefault();
    mainWindow.hide();
  }
});
```

## Global shortcuts

```javascript
const { globalShortcut } = require('electron');

app.whenReady().then(() => {
  // Register with conflict detection
  const registered = globalShortcut.register('Alt+S', () => {
    mainWindow.webContents.send('shortcut-triggered', 'toggle-recording');
  });

  if (!registered) {
    console.error('Shortcut registration failed - conflict detected');
  }
});

app.on('will-quit', () => {
  globalShortcut.unregisterAll();
});
```

## PTY terminal integration (node-pty)

```javascript
const pty = require('node-pty');

const shell = process.platform === 'win32' ? 'powershell.exe' : process.env.SHELL || '/bin/bash';

const ptyProcess = pty.spawn(shell, [], {
  name: 'xterm-256color',
  cols: 80,
  rows: 24,
  cwd: process.env.HOME,
  env: process.env
});

ptyProcess.onData((data) => {
  mainWindow.webContents.send('terminal-data', { tabId, data });
});

ipcMain.on('terminal-write', (event, { tabId, data }) => {
  ptyProcess.write(data);
});

ipcMain.on('terminal-resize', (event, { tabId, cols, rows }) => {
  ptyProcess.resize(cols, rows);
});
```

## Audio recording workflow

```typescript
// Request microphone access
const stream = await navigator.mediaDevices.getUserMedia({
  audio: {
    echoCancellation: true,
    noiseSuppression: true,
    autoGainControl: true
  }
});

// Record audio
const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
const chunks: Blob[] = [];

mediaRecorder.ondataavailable = (e) => chunks.push(e.data);
mediaRecorder.onstop = async () => {
  const blob = new Blob(chunks, { type: 'audio/webm' });
  const base64 = await blobToBase64(blob);
  // Send to transcription API
};

mediaRecorder.start();
// Later: mediaRecorder.stop();
```

## WebRTC patterns (PeerJS)

```typescript
import Peer from 'peerjs';

const peer = new Peer(userId, {
  host: 'peerjs-server.com',
  port: 443,
  secure: true
});

// Answer incoming calls
peer.on('call', (call) => {
  call.answer(localStream);
  call.on('stream', (remoteStream) => {
    audioElement.srcObject = remoteStream;
  });
});

// Make outgoing calls
const call = peer.call(remoteUserId, localStream);
call.on('stream', (remoteStream) => {
  audioElement.srcObject = remoteStream;
});

// Screen sharing via replaceTrack (no renegotiation)
const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: true });
const videoTrack = screenStream.getVideoTracks()[0];
const sender = peerConnection.getSenders().find(s => s.track?.kind === 'video');
await sender.replaceTrack(videoTrack);
```

## Build configuration (electron-builder.yml)

```yaml
appId: com.yourname.appname
productName: AppName
directories:
  output: release

win:
  target:
    - target: nsis
      arch: [x64]
  icon: assets/icon.ico

nsis:
  oneClick: false
  allowToChangeInstallationDirectory: true
  installerIcon: assets/icon.ico
  uninstallerIcon: assets/icon.ico

mac:
  target:
    - target: dmg
      arch: [x64, arm64]
  icon: assets/icon.icns
  hardenedRuntime: true
  gatekeeperAssess: false
  entitlements: build/entitlements.mac.plist
  entitlementsInherit: build/entitlements.mac.plist
  notarize:
    teamId: YOUR_APPLE_TEAM_ID

linux:
  target:
    - target: AppImage
      arch: [x64]
  icon: assets/icon.png

publish:
  provider: github
  owner: username
  repo: repo-name

extraResources:
  - from: "node_modules/node-pty/build/Release/"
    to: "node-pty/"
    filter: ["*.node"]
```

**macOS notarization** is required for distribution outside the App Store; Gatekeeper blocks unnotarized apps on first launch. Set the env vars `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, and `APPLE_TEAM_ID` (or use an App Store Connect API key) before running `npm run package`. electron-builder ≥ 24.13 handles notarization natively via the `mac.notarize` field; older versions require the `electron-notarize` afterSign hook.

For Windows, code signing with an EV cert is increasingly necessary to avoid SmartScreen warnings. electron-builder reads `CSC_LINK` (PFX) and `CSC_KEY_PASSWORD` env vars.

## Common pitfalls

**Stale closures in callbacks:**
```typescript
// Problem: State is stale in async callbacks
const [state, setState] = useState(initialValue);
peer.on('call', () => {
  console.log(state); // Always shows initialValue
});

// Solution: Use refs for async callback access
const stateRef = useRef(state);
useEffect(() => { stateRef.current = state; }, [state]);
peer.on('call', () => {
  console.log(stateRef.current); // Current value
});
```

**Context isolation security:**
- Never expose `ipcRenderer` directly to renderer
- Always use `contextBridge.exposeInMainWorld()`
- Validate all IPC arguments in main process
- Use TypeScript interfaces for IPC contracts

**BrowserView is deprecated, use WebContentsView:**

`BrowserView` was deprecated in Electron 30 (April 2024) and the underlying implementation has been replaced. `BrowserView` still works as a compatibility shim over `WebContentsView`, but new code should target `WebContentsView` directly. The constructors take the same `webPreferences` shape, so the migration is mostly mechanical. The differences worth knowing:

- `WebContentsView` is added via `win.contentView.addChildView(view)` instead of `win.addBrowserView(view)`
- Sizing is via `view.setBounds({x, y, width, height})`, no `setAutoResize`. You wire your own resize handlers if you want auto-resize.
- Z-order is the order of `addChildView` calls; `removeChildView` then re-`addChildView` to bring forward.

```javascript
const { WebContentsView } = require('electron');

const view = new WebContentsView({
  webPreferences: { contextIsolation: true, sandbox: true }
});
view.webContents.loadURL('https://example.com');
mainWindow.contentView.addChildView(view);
view.setBounds({ x: 0, y: 80, width: 800, height: 520 });
```

See the official [BrowserView → WebContentsView migration guide](https://www.electronjs.org/blog/migrate-to-webcontentsview) for edge cases (popups, devtools, focus management).

**Cross-platform shell detection:**
```javascript
const shell = process.platform === 'win32'
  ? 'powershell.exe'
  : process.env.SHELL || '/bin/bash';

const shellArgs = process.platform === 'win32'
  ? ['-NoLogo']
  : [];
```

## Development workflow

```bash
# Development (hot reload)
npm run electron:dev

# Production build
npm run electron:build

# Run built app locally
npx electron dist/

# Package for distribution
npm run package
```

© jamditis · MIT · jamditis/claude-skills-journalism

Похожие скиллы

Всё по теме «Фронтенд и UI» →
electron-best-practices agent-skills Скилл ведёт ИИ-агента по разработке Electron-приложений на React: безопасность, типизированный IPC, сборка и подпись. Пригодится, когда агент пишет код main, preload или renderer. CX Codex +1 820установок electron-development Mindrally Скилл задаёт агенту правила разработки десктопных приложений на Electron: разделение процессов, IPC, безопасность окон. CX Codex +1 1 300установок electron-pro VoltAgent Community Субагент для создания приложений на Electron 27+. Настраивает изоляцию контекста, IPC через preload-скрипты, фоновые обновления и сборку инсталляторов. 376установок electron sanjeed5 Правило для Cursor задаёт команде единый стиль разработки на Electron: контекстная изоляция, безопасный contextBridge, IPC через invoke и кросс-платформенные пути. ★ 3 579звёзд репо electron-builder pedronauck Настраивает сборку десктопных приложений на Electron под Windows, macOS и Linux. Помогает с подписью кода, выпуском релизов и автоматическими обновлениями. CX Codex +1 392установок clerk-tanstack-patterns clerk Подсказывает агенту, как подключить Clerk к TanStack React Start: защита маршрутов, createServerFn, лоадеры и middleware. CX Codex +1 15 500установок

Из того же набора8

web-scraping Задаёт правила этичного сбора данных со страниц, обработку кодов 403 и каскадный парсинг с перебором библиотек. CX Codex +1 6 100установок academic-writing Скилл помогает выстраивать структуру научных статей по схеме IMRaD, формулировать вопросы по критериям FINER и составлять поисковые запросы для баз данных. CX Codex +1 2 900установок page-monitoring Отслеживает изменения на веб-страницах, фиксирует удаление контента и помогает настроить мониторинг через Python или сторонние сервисы. CX Codex +1 604установок fact-check-workflow Скилл задаёт порядок проверки утверждений: от выписки цитат до оценки и фиксации доказательств. Пригодится, когда текст уходит в публикацию. CX Codex +1 582установок pdf-design Собирает PDF-отчёты и заявки на грант из HTML: превью по ходу правок, фирменные цвета, готовый файл на выходе. CX Codex +1 504установок source-verification Скилл внедряет метод SIFT для проверки фактов, документов, изображений и синтетических медиа. Помогает журналистам и исследователям вести проверяемый след доказательств. CX Codex +1 425установок social-media-intelligence Скилл для отслеживания распространения сюжетов, поиска скоординированных кампаний и проверки аккаунтов. Пригодится в журналистских расследованиях. CX Codex +1 331установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой