Skill556 repo starsupdated 11d ago
desktop-app
This Claude Code skill provides a framework selection guide and implementation reference for building cross-platform desktop applications using web technologies. It covers Tauri and Electron with specific recommendations based on project complexity, emphasizing Tauri for lightweight, secure applications and Electron for mature ecosystem integration, including setup instructions and architectural patterns for both frameworks.
Install in Claude Code
Copygit clone --depth 1 https://github.com/popup-studio-ai/bkit-claude-code /tmp/desktop-app && cp -r /tmp/desktop-app/skills/desktop-app ~/.claude/skills/desktop-appThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# Desktop App Development Expertise
## Overview
A guide for developing desktop apps using web technologies (HTML, CSS, JavaScript).
Support Windows, macOS, and Linux simultaneously with a single codebase.
---
## Framework Selection Guide
### Framework Selection by Tier (v1.3.0)
| Framework | Tier | Recommendation | Use Case |
|-----------|------|----------------|----------|
| **Tauri** | Tier 2 | ⭐ Primary | Lightweight (3MB), Rust security |
| **Electron** | Tier 3 | Supported | Mature ecosystem, VS Code-like apps |
> **AI-Native Recommendation**: Tauri
> - 35% YoY growth
> - 20-40MB memory vs Electron's 200-400MB
> - Mobile support (iOS/Android) via Tauri 2.0
> - Rust backend = memory safety
> **Ecosystem Recommendation**: Electron
> - Mature tooling
> - Node.js integration
> - Proven at scale (VS Code, Slack)
### Level-wise Recommendations
```
Starter → Tauri (v2) [Tier 2]
- Simpler setup than Electron
- Smaller output bundles (~3MB vs ~150MB)
Dynamic → Tauri + auto-update [Tier 2]
- Includes server integration, auto-update
- Lower memory footprint
Enterprise → Tauri [Tier 2] or Electron [Tier 3]
- Tauri for performance and security
- Electron for complex Node.js integration
```
---
## Electron Guide
### Project Creation
```bash
# Create with electron-vite (recommended)
npm create @electron-vite/create my-electron-app
cd my-electron-app
# Install dependencies
npm install
# Start development server
npm run dev
```
### Folder Structure
```
my-electron-app/
├── src/
│ ├── main/ # Main process (Node.js)
│ │ └── index.ts # App entry point, window management
│ ├── preload/ # Preload script
│ │ └── index.ts # Renderer↔Main bridge
│ └── renderer/ # Renderer process (Web)
│ ├── src/ # React/Vue code
│ └── index.html # HTML entry point
├── resources/ # App icons, assets
├── electron.vite.config.ts # Build configuration
├── electron-builder.yml # Deployment configuration
└── package.json
```
### Core Concept: Process Separation
```
┌─────────────────────────────────────────────────────┐
│ Electron App │
├─────────────────────────────────────────────────────┤
│ Main Process (Node.js) │
│ - System API access (files, network, etc.) │
│ - Window creation/management │
│ - Menu, tray management │
├─────────────────────────────────────────────────────┤
│ Preload Script (Bridge) │
│ - Safe main↔renderer communication │
│ - Expose only specific APIs │
├─────────────────────────────────────────────────────┤
│ Renderer Process (Chromium) │
│ - Web UI (React, Vue, etc.) │
│ - DOM access │
│ - No direct Node.js API access (security) │
└─────────────────────────────────────────────────────┘
```
### Main Process
```typescript
// src/main/index.ts
import { app, BrowserWindow, ipcMain } from 'electron';
import { join } from 'path';
let mainWindow: BrowserWindow | null = null;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
},
});
// Dev mode: Load Vite server
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
// Production: Load built files
mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
}
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// IPC: Handle requests from renderer
ipcMain.handle('read-file', async (event, filePath) => {
const fs = await import('fs/promises');
return fs.readFile(filePath, 'utf-8');
});
```
### Preload Script
```typescript
// src/preload/index.ts
import { contextBridge, ipcRenderer } from 'electron';
// APIs to safely expose to renderer
contextBridge.exposeInMainWorld('electronAPI', {
// Read file
readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath),
// Save file dialog
saveFile: (content: string) => ipcRenderer.invoke('save-file', content),
// App version
getVersion: () => process.env.npm_package_version,
// Platform
platform: process.platform,
});
// Type definitions (for use in renderer)
declare global {
interface Window {
electronAPI: {
readFile: (path: string) => Promise<string>;
saveFile: (content: string) => Promise<void>;
getVersion: () => string;
platform: NodeJS.Platform;
};
}
}
```
### Renderer Process
```typescript
// src/renderer/src/App.tsx
import { useState } from 'react';
function App() {
const [content, setContent] = useState('');
const handleOpenFile = async () => {
const result = await window.electronAPI.readFile('/path/to/file.txt');
setContent(result);
};
return (
<div className="app">
<h1>My Electron App</h1>
<p>Platform: {window.electronAPI.platform}</p>
<button onClick={handleOpenFile}>Open File</button>
<pre>{content}</pre>
</div>
);
}
export default App;
```
### Creating Menus
```typescript
// src/main/menu.ts
import { Menu, app, shell } from 'electron';
const template: Electron.MenuItemConstructorOptions[] = [
{
label: 'File',
submenu: [
{ label: 'New File', accelerator: 'CmdOrCtrl+N', click: () => {} },
{ label: 'Open', accelerator: 'CmdOrCtrl+O', click: () => {} },
{ type: 'separator' },
{ label: 'Quit', role: 'quit' },
],
},
{
label: 'Edit',
submenu: [
{ label: 'Undo', role: 'undo' },
{ label: 'Redo', role: 'redo' },
{ type: 'separator' },
{ label