desktop-framework-electron について
Electron デスクトップアプリの開発ガイドです。プロセス構成、安全な IPC、ネイティブ API、パッケージ化や更新を扱い、特に安全な既定値とプロセス間通信の検証に重点を置いています。
主な機能
- 01メイン・レンダラープロセス構成
- 02安全な BrowserWindow 設定
- 03contextBridge による IPC ラップ
- 04要求応答とプッシュ通知
- 05ネイティブ API とディープリンク
- 06パッケージ化と自動更新
活用シーン
- 01クロスプラットフォーム Electron アプリの構築
- 02プロセス間通信の設計と確認
- 03メニュー、トレイ、ファイルダイアログの追加
- 04配布とアプリ更新の設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents-inc/ skills --skill desktop-framework-electronインストール後、Claude Code で/desktop-framework-electronを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.yamlreference.mdexamples/core.mdipc.mdnative-apis.mdpackaging.mdsecurity.md詳細
Electron デスクトップアプリケーション
クイックガイド: Electron アプリは、2 種類のプロセスで動作します。ウィンドウとシステム API を管理する メインプロセス(Node.js)と、ウィンドウごとに 1 つ起動する レンダラープロセス(Chromium)です。両者の通信はすべて、
contextBridgeを使って最小限の型付き API を公開するプリロードスクリプト経由で IPC を利用します。contextIsolationは絶対に無効にしないでください。レンダラーでnodeIntegrationを絶対に有効にしないでください。Electron Forge または Electron Builder でパッケージ化してください。自動更新には、autoUpdater(macOS/Windows では Squirrel)またはすべてのプラットフォームに対応するelectron-updaterを使用してください。
<critical_requirements>
重要:このスキルを使用する前に
(contextIsolation: true(デフォルト)を必ず維持してください。有効範囲を無効にすると、信頼できないレンダラーコードにプリロードの全スコープが公開されます)
(プリロードスクリプトでは必ず contextBridge.exposeInMainWorld() を使用してください。ipcRenderer を直接公開してはいけません)
(どの BrowserWindow でも nodeIntegration: true を有効にしてはいけません。レンダラーに Node.js への完全なアクセス権が付与されるため、重大なセキュリティ脆弱性となります)
(メインプロセスで IPC 経由で受信するすべてのデータを必ず検証し、サニタイズしてください。レンダラーからのメッセージは信頼できない入力として扱ってください)
(リクエストとレスポンスを伴う IPC には、必ず ipcMain.handle() / ipcRenderer.invoke() を使用してください。レンダラーをブロックする sendSync は避けてください)
(nodeIntegration が有効、または contextIsolation が無効な状態でリモート URL を読み込んではいけません。これはリモートサイトにシステムへの完全なアクセス権を与えるのと同じです)
</critical_requirements>
自動検出: Electron、electron、BrowserWindow、ipcMain、ipcRenderer、contextBridge、preload、webPreferences、electron-builder、electron-forge、app.whenReady、electronAPI、mainWindow、autoUpdater、nativeTheme、safeStorage、Tray、Menu、dialog、protocol、shell
使用する場面:
- クロスプラットフォームのデスクトップアプリケーションを構築する
- メインプロセス/レンダラープロセスのアーキテクチャを設定する
- セキュアな IPC 通信パターンを構築する
- ネイティブ OS の機能(トレイ、メニュー、ダイアログ、通知、ファイルシステム)を統合する
- デスクトップアプリケーションをパッケージ化して配布する
- 自動更新機能を実装する
- カスタムプロトコルハンドラー/ディープリンクを登録する
使用しない場面:
- レンダラー用の UI フレームワークを選ぶ(適切なウェブフレームワークのスキルを使用してください)
- レンダラー UI のスタイルを設定する(適切なスタイリングスキルを使用してください)
- メインプロセスに関係しないサーバー側またはバックエンドのロジック
- モバイルアプリケーション(Electron はデスクトップ専用です)
- GUI を必要としない CLI ツール
主なパターン
パターン 1:セキュアな BrowserWindow の作成
すべての BrowserWindow でプリロードスクリプトを使用し、セキュアなデフォルト設定(contextIsolation: true、sandbox: true、nodeIntegration: false)を維持する必要があります。
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
// contextIsolation: true -- default since Electron 12
// sandbox: true -- default since Electron 20
// nodeIntegration: false -- default since Electron 5
},
});
重要な点: セキュリティのデフォルト設定を絶対に上書きしないでください。メインプロセスとレンダラーの間をつなぐ唯一のブリッジがプリロードスクリプトです。詳しくは examples/core.md を参照してください。
パターン 2:contextBridge を使ったプリロード
プリロードスクリプトは、明示的に型を指定した限定的な API をレンダラーに公開します。ipcRenderer を直接公開してはいけません。
// preload.js
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("electronAPI", {
readFile: (filePath) => ipcRenderer.invoke("read-file", filePath),
onUpdateAvailable: (callback) => {
ipcRenderer.on("update-available", (_event, data) => callback(data));
},
});
重要な点: 公開する各メソッドは、単一の IPC チャネルをラップします。レンダラーは IPC の内部実装を知らなくても、window.electronAPI.readFile(path) を呼び出せます。詳しくは examples/core.md を参照してください。
パターン 3:IPC のリクエストとレスポンス(invoke/handle)
非同期の双方向通信には、メインプロセスで ipcMain.handle() を、プリロードで ipcRenderer.invoke() を使用します。
// main process
ipcMain.handle("read-file", async (_event, filePath) => {
const content = await fs.readFile(filePath, "utf-8");
return { success: true, content };
});
重要な点: handle/invoke は Promise を返します。ハンドラー内で必ず filePath を検証してください。レンダラーからの入力を信頼してはいけません。すべての IPC パターンについては examples/ipc.md を参照してください。
パターン 4:メインプロセスからレンダラーへのメッセージ
メインプロセスから webContents.send() を使用し、プリロードではコールバックパターンでリッスンします。
// main: send to specific window
mainWindow.webContents.send("update-progress", { percent: 45 });
// preload: expose listener
onUpdateProgress: (callback) => {
ipcRenderer.on("update-progress", (_event, data) => callback(data));
},
重要な点: レンダラーからメインプロセスのデータを取得することはできません。メインプロセスから送信する必要があります。リスナーは必ず特定のチャネルに限定してください。詳しくは examples/ipc.md を参照してください。
パターン 5:アプリのライフサイクル
メインプロセスは、プラットフォーム固有の規約に従ってアプリのライフサイクルを管理します。
app.whenReady().then(() => {
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});
重要なポイント: macOS アプリはすべてのウィンドウを閉じても終了しません(window-all-closed で終了させてはいけません)。Dock アイコンがクリックされると、activate イベントによってウィンドウが再作成されます。examples/core.md を参照してください。
パターン 6:OS ネイティブとの統合
Electron は、ダイアログ、メニュー、トレイアイコン、通知などのネイティブ API をメインプロセスから利用できるようにします。
const { dialog } = require("electron");
const result = await dialog.showOpenDialog(mainWindow, {
properties: ["openFile", "multiSelections"],
filters: [{ name: "Documents", extensions: ["txt", "md", "json"] }],
});
重要なポイント: mainWindow を第 1 引数として渡すと、ネイティブダイアログはそのウィンドウに対してモーダルになります。examples/native-apis.md を参照してください。
パターン 7:カスタムプロトコル/ディープリンク
ブラウザーやほかのアプリからのディープリンクに対応するため、アプリを myapp:// URL のハンドラーとして登録します。
if (process.defaultApp) {
app.setAsDefaultProtocolClient("myapp", process.execPath, [
path.resolve(process.argv[1]),
]);
} else {
app.setAsDefaultProtocolClient("myapp");
}
重要なポイント: 開発時(process.defaultApp が true の場合)は、引数としてスクリプトのパスを渡す必要があります。macOS では、app オブジェクトの open-url イベントを処理します。Windows/Linux では、second-instance イベントで処理します。examples/native-apis.md を参照してください。
<decision_framework>
判断フレームワーク
IPC パターンの選択
Which IPC pattern?
+-- Renderer needs a response from main?
| +-- YES --> ipcMain.handle() + ipcRenderer.invoke()
+-- Renderer sends data, no response needed?
| +-- YES --> ipcMain.on() + ipcRenderer.send()
+-- Main needs to push data to renderer?
| +-- YES --> webContents.send() + ipcRenderer.on() (in preload)
+-- Two renderers need to communicate?
| +-- YES --> Route through main process (never direct renderer-to-renderer)
+-- High-frequency data transfer (streaming)?
+-- YES --> MessageChannelMain / MessagePort pair
ウィンドウのアーキテクチャ
How many windows?
+-- Single window app?
| +-- One BrowserWindow, one preload script
+-- Multi-window (e.g., preferences, about)?
| +-- Separate BrowserWindow per view, each with its own preload
+-- Frameless / custom title bar?
| +-- frame: false + custom drag regions via CSS (-webkit-app-region: drag)
+-- Persistent background work?
+-- Use a hidden BrowserWindow or utilityProcess (Electron 22+)
</decision_framework>
詳細なリソース:
- examples/core.md - アプリのライフサイクル、BrowserWindow、preload、contextBridge の基礎
- examples/ipc.md - すべての IPC パターン:invoke/handle、send/on、メインプロセスからレンダラープロセスへの通信、MessagePort
- examples/security.md - セキュリティの強化、CSP、権限ハンドラー、安全なデフォルト設定
- examples/native-apis.md - ダイアログ、メニュー、トレイ、通知、プロトコルハンドラー、自動アップデーター
- examples/packaging.md - Electron Forge、Electron Builder、コード署名、配布
- reference.md - API クイックリファレンステーブル、バージョン履歴、セキュリティチェックリスト
<red_flags>
危険なパターン
重大なセキュリティ上の問題:
contextIsolationを無効にする(contextIsolation: false)――preload のグローバルをレンダラープロセスに公開しますnodeIntegration: trueを有効にする――レンダラープロセスに Node.js への完全なアクセス権を与えます(fs、child_process など)- 個々のチャネルをラップせずに、
ipcRendererをcontextBridge経由で直接公開する sandbox: trueを設定せずに、リモート URL や信頼できない URL を読み込む- 本番環境で
webSecurityを無効にする(webSecurity: falseは同一オリジンポリシーを無効にします) - メインプロセスのハンドラーで IPC 引数を検証しない(パストラバーサル、インジェクション攻撃)
- 検証されていない URL に対して
shell.openExternal()を使用する(任意のコマンドが実行される可能性があります)
アーキテクチャ上の問題:
ipcRenderer.sendSync()を使用する――レンダラープロセスをブロックし、UI のフリーズを引き起こします- メインプロセスではなく、レンダラープロセスにビジネスロジックを置く
- レンダラープロセス間で直接通信する(メインプロセスを介さない)
remoteモジュールを使用する(Electron 14 以降で削除済み。セキュリティとパフォーマンスのリスクがありました)- レンダラープロセスから BrowserWindows を作成する
- プラットフォームごとに
window-all-closedを適切に処理しない(macOS アプリは終了させてはいけません)
パッケージング上の問題:
- 本番ビルドに
devDependenciesを含めて配布する(アプリのサイズが肥大化します) - アプリケーションにコード署名しない(macOS で OS の警告が表示され、自動アップデートが失敗します)
node_modulesを整理せずにバンドルする、または ASAR アーカイブを使用しない- 開発環境とパッケージ化環境で異なる絶対パスをハードコードする
- レンダラープロセスのコードで
__dirnameを使用する(サンドボックス化されたレンダラープロセスでは使用できません)
よくある間違い:
app.whenReady()を呼び出し忘れる――readyイベントより前は API を利用できませんactivateイベントでウィンドウを再作成しない(macOS では Dock をクリックしても何も起こりません)<script>タグで読み込まれるレンダラースクリプト内でrequire()を使用する(サンドボックス化されたレンダラープロセスでは使用できません)- 外部コンテンツを読み込む iframe に対して
nodeIntegrationInSubFrames: trueを設定する - レンダラー HTML に適切な
Content-Security-Policyヘッダーを設定しない
</red_flags>
<critical_reminders>
重大な注意事項
(contextIsolation: true(デフォルト)を必ず維持してください。これを無効にすると、信頼されていないレンダラーコードにプリロードのスコープ全体が公開されます)
(プリロードスクリプトでは必ず contextBridge.exposeInMainWorld() を使用してください。ipcRenderer を直接公開してはいけません)
(いずれの BrowserWindow でも nodeIntegration: true を有効にしてはいけません。レンダラーに Node.js への完全なアクセス権が与えられ、重大なセキュリティ上の脆弱性につながります)
(IPC 経由で受信するすべてのデータを、メインプロセスで必ず検証してサニタイズしてください。レンダラーからのメッセージは信頼できない入力として扱ってください)
(リクエストとレスポンスを伴う IPC には、必ず ipcMain.handle() / ipcRenderer.invoke() を使用してください。レンダラーをブロックする sendSync は避けてください)
これらのルールに従わないと、深刻なセキュリティ上の脆弱性や、正常に動作しないデスクトップアプリが発生します。
</critical_reminders>