dev-electron

Electron app development patterns for thin wrapper apps around dev servers. Use when: (1) Building Electron apps as thin wrappers around web apps, (2) Managing dev server processes in Electron, (3) Handling nodenv/anyenv PATH issues in spawned processes, (4) Packaging with electron-builder, (5) Sharing modules across multiple Electron apps (extraResources), (6) Dynamic project root resolution in packaged apps, (7) Opening external links in default browser.

6 stars

Best use case

dev-electron is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Electron app development patterns for thin wrapper apps around dev servers. Use when: (1) Building Electron apps as thin wrappers around web apps, (2) Managing dev server processes in Electron, (3) Handling nodenv/anyenv PATH issues in spawned processes, (4) Packaging with electron-builder, (5) Sharing modules across multiple Electron apps (extraResources), (6) Dynamic project root resolution in packaged apps, (7) Opening external links in default browser.

Teams using dev-electron should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/dev-electron/SKILL.md --create-dirs "https://raw.githubusercontent.com/Takazudo/claude-resources/main/skills/dev-electron/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/dev-electron/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How dev-electron Compares

Feature / Agentdev-electronStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Electron app development patterns for thin wrapper apps around dev servers. Use when: (1) Building Electron apps as thin wrappers around web apps, (2) Managing dev server processes in Electron, (3) Handling nodenv/anyenv PATH issues in spawned processes, (4) Packaging with electron-builder, (5) Sharing modules across multiple Electron apps (extraResources), (6) Dynamic project root resolution in packaged apps, (7) Opening external links in default browser.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Electron Development

## Common Pattern: Thin Wrapper App

Electron as thin wrapper around a dev server (e.g., Vite, Docusaurus):

1. Show splash screen
2. Spawn dev server as background process
3. Wait for server ready
4. Show BrowserWindow pointing to localhost URL
5. Clean up server on quit

See [references/background-process.md](references/background-process.md) for implementation.

### BrowserWindow Setup

Load the dev server URL directly in BrowserWindow (no webview, no tabs):

```javascript
const { BrowserWindow, shell } = require("electron");

function createMainWindow(devServerUrl) {
  const win = new BrowserWindow({
    width: 1200,
    height: 800,
    title: "My App",
    show: false,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
    },
  });

  win.loadURL(devServerUrl);
  win.once("ready-to-show", () => win.show());

  // Open external links in default browser
  const devServerOrigin = new URL(devServerUrl).origin;
  win.webContents.setWindowOpenHandler(({ url }) => {
    try {
      const parsed = new URL(url);
      if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
        return { action: "deny" };
      }
      if (parsed.origin !== devServerOrigin) {
        shell.openExternal(url);
        return { action: "deny" };
      }
    } catch {
      // Invalid URL
    }
    return { action: "deny" };
  });

  return win;
}
```

Key points:

- Use `nodeIntegration: false` + `contextIsolation: true` (secure defaults)
- Standard `{ role: "reload" }` menu items work correctly (they reload the BrowserWindow content directly)
- No need for custom IPC, globalShortcut, or webview tags

### Menu

Use standard Electron menu roles. No custom IPC needed:

```javascript
const template = [
  {
    label: "View",
    submenu: [
      { role: "reload" },
      { role: "forceReload" },
      { role: "toggleDevTools" },
    ],
  },
  {
    label: "Edit",
    submenu: [
      { role: "undo" }, { role: "redo" },
      { type: "separator" },
      { role: "cut" }, { role: "copy" }, { role: "paste" },
      { role: "selectAll" },
    ],
  },
  {
    label: "Window",
    submenu: [
      { role: "minimize" },
      { role: "close" },
    ],
  },
];
```

## Packaging & Build

### electron-builder: Keep as devDependency (DO NOT use pnpm dlx)

**electron-builder has 300+ sub-dependencies.** Using `pnpm dlx` downloads them all on every invocation, making builds extremely slow. Always install it as a devDependency:

```json
{
  "devDependencies": {
    "electron": "^35.7.5",
    "electron-builder": "^26.8.0"
  },
  "scripts": {
    "build": "electron-builder --mac",
    "build:dir": "electron-builder --mac --dir"
  }
}
```

### Shared Modules: Use extraResources, NOT files glob

```json
// WRONG - shared module won't be in the asar
"files": ["main.js", "../../../shared/module/**/*"]

// CORRECT - copies to app's Resources directory
"extraResources": [{ "from": "../../../shared/module", "to": "module" }]
```

Then resolve dynamically in main.js:

```javascript
function getSharedCorePath() {
  if (app.isPackaged) {
    return path.join(process.resourcesPath, 'electron-app-core');
  }
  return path.join(__dirname, '..', '..', '..', 'shared', 'electron-app-core');
}
```

### Dynamic Project Root (Don't Hardcode Paths)

Walk up from `app.getPath("exe")` checking each directory for `package.json` with the expected project name. This is robust against repo moves and directory restructuring — no fragile `..` counting.

```javascript
function findProjectRootFromExePath() {
  let dir = path.dirname(app.getPath("exe"));
  const root = path.parse(dir).root;
  while (dir !== root) {
    if (isProjectRoot(dir)) return dir;
    dir = path.dirname(dir);
  }
  return null;
}
```

See [references/packaging.md](references/packaging.md) for full pattern including `isProjectRoot` helper.

## Critical Pitfalls

### Open External Links in Default Browser (Cmd+Click)

Electron doesn't open links in the system browser by default. Use `setWindowOpenHandler` to intercept Cmd+click and route external URLs to the default browser via `shell.openExternal`. See BrowserWindow Setup above.

Validate URL protocol (allow only `http:` and `https:`) to prevent `javascript:` or other protocol injection.

### Dev Server: Kill Stale Port Before Start

When the app crashes or is force-quit, the old dev server process may survive and hold the port. On next launch the new server can't bind, causing a timeout. Kill any existing process on the port before spawning:

```javascript
const { execSync } = require("child_process");

function killProcessOnPort(port) {
  try {
    const output = execSync(`lsof -ti tcp:${port}`, { encoding: "utf-8" });
    const pids = output.trim().split("\n").filter(Boolean);
    for (const pid of pids) {
      process.kill(Number(pid), "SIGKILL");
    }
  } catch {
    // No process on port - fine
  }
}
```

### Dev Server: Health Check Must Not Require HTTP 200

When the dev server framework uses a non-root `baseUrl` (e.g., Docusaurus with `baseUrl: "/pj/app/doc/"`), the root path `/` returns 404. **Accept any HTTP response** as proof the server is alive:

```javascript
// WRONG - breaks when baseUrl is not "/"
(res) => resolve(res.statusCode === 200)

// CORRECT - any response means server is up
(res) => resolve(res.statusCode > 0)
```

### Dev Server: Default URL Must Include baseUrl

When the framework uses a non-root `baseUrl`, the default URL must include the full path. Otherwise the app opens to a 404 page:

```javascript
// WRONG - opens to 404 when baseUrl is "/pj/app/doc/"
const defaultUrl = "http://localhost:3000";

// CORRECT - include the full baseUrl path
const defaultUrl = "http://localhost:3000/pj/app/doc/";
```

### Dev Server: Avoid Transient Errors During File Regeneration

When regenerating files that a running dev server watches, **write new files before deleting stale ones**. If you delete first, the dev server sees missing files and shows errors.

### nodenv/anyenv PATH Issues

Spawned processes don't inherit version managers. Source shell profile first. See [references/background-process.md](references/background-process.md).

## Detailed References

- **Packaging & build**: [references/packaging.md](references/packaging.md) - pnpm dlx, extraResources, dynamic project root
- **Background process & dev server**: [references/background-process.md](references/background-process.md) - Spawn, wait, cleanup

Related Skills

zudoesa-articlify

6
from Takazudo/claude-resources

Convert conversation context into an esa article via the zudoesa-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write esa article', 'esa記事', 'esaに書いて', 'articlify for esa', or /zudoesa-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zudoesa-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's esa writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's esa style, (2) User says 'apply voice', 'esa voice', 'esa文体で', 'esa風に書いて', '文体を適用', (3) User provides text to transform to esa style. Reads writing-style.md and vocabulary-rule.md from takazudo-esa-writing repo and applies the rules.

zudocg-articlify

6
from Takazudo/claude-resources

Convert conversation context into a CodeGrid article via the zudocg-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write codegrid article', 'CodeGrid記事', 'codegridに書いて', 'articlify for codegrid', or /zudocg-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zudocg-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's CodeGrid writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's CodeGrid style, (2) User says 'apply voice', 'codegrid voice', 'codegrid文体で', 'codegrid風に書いて', '文体を適用', (3) User provides text to transform to CodeGrid style. Reads writing-style.md and vocabulary-rule.md from takazudo-codegrid-writing repo and applies the rules.

zpaper-articlify

6
from Takazudo/claude-resources

Convert conversation context into a zpaper blog article via the zpaper-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write zpaper article', 'zpaper記事', 'zpaperに書いて', 'articlify for zpaper', or /zpaper-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zpaper-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's zpaper blog writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's zpaper style, (2) User says 'apply voice', 'zpaper voice', 'zpaper文体で', 'zpaper風に書いて', 'ブログ文体を適用', (3) User provides text to transform to zpaper style. Reads writing-style.md and vocabulary-rule.md from the zpaper repo and applies the rules.

xlsx

6
from Takazudo/claude-resources

Spreadsheet creation, editing, and analysis. Use when working with .xlsx, .xlsm, .csv, .tsv files for: (1) Creating spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modifying existing spreadsheets while preserving formulas, (4) Data analysis and visualization, (5) Recalculating formulas.

x

6
from Takazudo/claude-resources

Facade for development workflows. Routes on two axes: plan-first vs implement-now (escalates to /big-plan -a when the request needs research / decomposition / has unclear scope — the appended -a makes the plan chain into implementation in-session), then single vs multi on the ready-to-build fast paths (/x-as-pr single-topic, /x-wt-teams multi-topic parallel). Use when: (1) User says '/x' followed by dev instructions, (2) User wants to start development without choosing the workflow skill, (3) User says 'dev', 'implement', or 'build' with a task. Default option: -v (verify-ui). Review-loop (-l) is opt-in — without -l the downstream skill runs a single /deep-review pass. Forwards -a (autonomy/auto-chain) and -m (merge at the end + cleanup + CI watch) through every route; auto-fix of raised findings (-f) and issue-raising (-ri) are downstream defaults, with -nf/--no-fix and -nori/--no-raise-issues as the forwarded opt-outs. -a and -m are orthogonal — full hands-off end-to-end is -a -m.

x-wt-teams

6
from Takazudo/claude-resources

Parallel multi-topic development using git worktrees, base branches, and Claude Code agent teams. Use when: (1) User wants to work on multiple related features in parallel, (2) User mentions 'worktree', 'base branch', 'parallel development', 'split into topics', or 'multi-topic'. FULLY AUTONOMOUS — creates worktrees, spawns teams, coordinates everything. Also supports Super-Epic child mode for [Epic] issues from /big-plan with '**Super-epic:** #N' markers (targets the super-epic base branch instead of main).

x-as-pr

6
from Takazudo/claude-resources

Start a development workflow as a draft PR. Creates a NEW branch from the current branch, empty start commit, draft PR targeting the current branch, then implements. ALWAYS creates a new branch by default — produces a nested PR-on-PR when the current branch already has one. Use when: (1) User says 'dev as pr', (2) User wants a PR-first workflow before coding, (3) User passes -s/--stay to reuse the current branch instead of nesting, (4) User passes a GitHub issue URL to implement, (5) User passes --make-issue/--issue to create an issue first. Logs progress via issue comments when an issue is linked.

watch-ci

6
from Takazudo/claude-resources

Watch GitHub PR CI checks in the background and notify on completion. Use when: (1) User wants to monitor CI/CD status, (2) User says 'watch CI', 'check CI', 'monitor checks', or 'wait for CI', (3) User wants to know when checks pass or fail. Runs a background gh polling shell loop (NOT a subagent — near-zero token cost), sends macOS notification on completion. Also handles merged PRs by watching the target branch CI.

w-update-wording-rule

6
from Takazudo/claude-resources

Add or update wording rules (表記ルール) in the w repo's vocabulary-rule.md files. Use when: (1) User says 'add wording rule', 'update wording rule', '表記ルール追加', (2) User wants to add a kanji/hiragana usage rule, (3) User provides a rule like 'X should be Y' with examples.