
Desktop
- 1.9k installs
- 81.3k repo stars
- Updated August 5, 2026
- lobehub/lobehub
desktop guides LobeHub Electron IPC controllers, preload scripts, and window management.
About
The desktop skill guides Electron development for LobeHub with main-renderer architecture. Main process in apps/desktop/src/main handles lifecycle, system APIs, and windows; renderer reuses web code from src/; preload scripts in apps/desktop/src/preload securely expose IPC to the renderer. New features add controllers under apps/desktop/src/main/controllers using ControllerModule and IpcMethod decorators. Agents follow secure preload boundaries, window and menu management patterns, and reuse web components where possible. Use when adding desktop-only capabilities, IPC handlers, or Electron window flows in LobeHub. Electron main-renderer architecture with secure preload IPC bridge Controllers in apps/desktop/src/main/controllers with IpcMethod decorators Renderer reuses web code; preload exposes vetted main APIs Window, menu, and lifecycle management in main process disable-model-invocation requires explicit desktop feature requests desktop guides LobeHub Electron IPC controllers, preload scripts, and window management Controller, preload exposure, and renderer integration following LobeHub desktop patterns User adds LobeHub desktop IPC, Electron window, or preload handler LobeHub.
- Electron main-renderer architecture with secure preload IPC bridge.
- Controllers in apps/desktop/src/main/controllers with IpcMethod decorators.
- Renderer reuses web code; preload exposes vetted main APIs.
- Window, menu, and lifecycle management in main process.
- disable-model-invocation requires explicit desktop feature requests.
Desktop by the numbers
- 1,896 all-time installs (skills.sh)
- +85 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #133 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
desktop capabilities & compatibility
- Capabilities
- ipc controller patterns · preload security · window management · main process lifecycle · web code reuse
- Use cases
- frontend
npx skills add https://github.com/lobehub/lobehub --skill desktopAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.9k |
|---|---|
| repo stars | ★ 81.3k |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 5, 2026 |
| Repository | lobehub/lobehub ↗ |
How do I add a secure desktop-only feature to LobeHub Electron app?
Build LobeHub Electron desktop features with IPC controllers, preload scripts, and main-renderer window management.
Who is it for?
LobeHub developers adding Electron IPC features and window flows.
Skip if: Pure web-only LobeHub features without Electron main process needs.
When should I use this skill?
User adds LobeHub desktop IPC, Electron window, or preload handler.
What you get
Controller, preload exposure, and renderer integration following LobeHub desktop patterns.
- IPC controller modules
- desktop notification handlers
Files
Desktop Development Guide
Architecture Overview
LobeHub desktop is built on Electron with main-renderer architecture:
1. Main Process (apps/desktop/src/main): App lifecycle, system APIs, window management 2. Renderer Process: Reuses web code from src/ 3. Preload Scripts (apps/desktop/src/preload): Securely expose main process to renderer
Adding New Desktop Features
1. Create Controller
Location: apps/desktop/src/main/controllers/
import { ControllerModule, IpcMethod } from '@/controllers';
export default class NewFeatureCtr extends ControllerModule {
static override readonly groupName = 'newFeature';
@IpcMethod()
async doSomething(params: SomeParams): Promise<SomeResult> {
// Implementation
return { success: true };
}
}Register in apps/desktop/src/main/controllers/registry.ts.
2. Define IPC Types
Location: packages/electron-client-ipc/src/types.ts
export interface SomeParams {
/* ... */
}
export interface SomeResult {
success: boolean;
error?: string;
}3. Create Renderer Service
Location: src/services/electron/
import { ensureElectronIpc } from '@/utils/electron/ipc';
const ipc = ensureElectronIpc();
export const newFeatureService = async (params: SomeParams) => {
return ipc.newFeature.doSomething(params);
};4. Implement Store Action
Location: src/store/
5. Add Tests
Location: apps/desktop/src/main/controllers/__tests__/
Detailed Guides
See references/ for specific topics:
- Feature implementation:
references/feature-implementation.md - Local tools workflow:
references/local-tools.md - Menu configuration:
references/menu-config.md - Window management:
references/window-management.md
Best Practices
1. Security: Validate inputs, limit exposed APIs 2. Performance: Use async methods, batch data transfers 3. UX: Add progress indicators, provide error feedback 4. Code organization: Follow existing patterns, add documentation
Desktop Feature Implementation Guide
Architecture Overview
Main Process Renderer Process
┌──────────────────┐ ┌──────────────────┐
│ Controller │◄──IPC───►│ Service Layer │
│ (IPC Handler) │ │ │
└──────────────────┘ └──────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ System APIs │ │ Store Actions │
│ (fs, network) │ │ (UI State) │
└──────────────────┘ └──────────────────┘Step-by-Step Implementation
1. Create Controller
// apps/desktop/src/main/controllers/NotificationCtr.ts
import type {
ShowDesktopNotificationParams,
DesktopNotificationResult,
} from '@lobechat/electron-client-ipc';
import { Notification } from 'electron';
import { ControllerModule, IpcMethod } from '@/controllers';
export default class NotificationCtr extends ControllerModule {
static override readonly groupName = 'notification';
@IpcMethod()
async showDesktopNotification(
params: ShowDesktopNotificationParams,
): Promise<DesktopNotificationResult> {
if (!Notification.isSupported()) {
return { error: 'Notifications not supported', success: false };
}
try {
const notification = new Notification({ body: params.body, title: params.title });
notification.show();
return { success: true };
} catch (error) {
console.error('[NotificationCtr] Failed:', error);
return { error: error instanceof Error ? error.message : 'Unknown error', success: false };
}
}
}2. Define IPC Types
// packages/electron-client-ipc/src/types.ts
export interface ShowDesktopNotificationParams {
title: string;
body: string;
}
export interface DesktopNotificationResult {
success: boolean;
error?: string;
}3. Create Service Layer
// src/services/electron/notificationService.ts
import type { ShowDesktopNotificationParams } from '@lobechat/electron-client-ipc';
import { ensureElectronIpc } from '@/utils/electron/ipc';
const ipc = ensureElectronIpc();
export const notificationService = {
show: (params: ShowDesktopNotificationParams) => ipc.notification.showDesktopNotification(params),
};4. Implement Store Action
// src/store/.../actions.ts
showNotification: async (title: string, body: string) => {
if (!isElectron) return;
const result = await notificationService.show({ title, body });
if (!result.success) {
console.error('Notification failed:', result.error);
}
},Best Practices
1. Security: Validate inputs, limit exposed APIs 2. Performance: Use async methods for heavy operations 3. Error handling: Always return structured results 4. UX: Provide loading states and error feedback
Desktop Local Tools Implementation
Workflow Overview
1. Define tool interface (Manifest) 2. Define related types 3. Implement Store Action 4. Implement Service Layer 5. Implement Controller (IPC Handler) 6. Update Agent documentation
Step 1: Define Tool Interface (Manifest)
Location: src/tools/[tool_category]/index.ts
// src/tools/local-files/index.ts
export const LocalFilesApiName = {
RenameFile: 'renameFile',
MoveFile: 'moveFile',
} as const;
export const LocalFilesManifest = {
api: [
{
name: LocalFilesApiName.RenameFile,
description: 'Rename a local file',
parameters: {
type: 'object',
properties: {
oldPath: { type: 'string', description: 'Current file path' },
newName: { type: 'string', description: 'New file name' },
},
required: ['oldPath', 'newName'],
},
},
],
};Step 2: Define Types
// packages/electron-client-ipc/src/types.ts
export interface RenameLocalFileParams {
oldPath: string;
newName: string;
}
// src/tools/local-files/type.ts
export interface LocalRenameFileState {
success: boolean;
error?: string;
oldPath: string;
newPath: string;
}Step 3: Implement Store Action
// src/store/chat/slices/builtinTool/actions/localFile.ts
renameLocalFile: async (id: string, params: RenameLocalFileParams) => {
const { toggleLocalFileLoading, updatePluginState, internal_updateMessageContent } = get();
toggleLocalFileLoading(id, true);
try {
const result = await localFileService.renameFile(params);
if (result.success) {
updatePluginState(id, { success: true, ...result });
internal_updateMessageContent(id, JSON.stringify({ success: true }));
} else {
updatePluginState(id, { success: false, error: result.error });
internal_updateMessageContent(id, JSON.stringify({ error: result.error }));
}
return result.success;
} catch (e) {
console.error(e);
updatePluginState(id, { success: false, error: e.message });
return false;
} finally {
toggleLocalFileLoading(id, false);
}
},Step 4: Implement Service Layer
// src/services/electron/localFileService.ts
import { ensureElectronIpc } from '@/utils/electron/ipc';
const ipc = ensureElectronIpc();
export const localFileService = {
renameFile: (params: RenameLocalFileParams) => ipc.localFiles.renameFile(params),
};Step 5: Implement Controller
// apps/desktop/src/main/controllers/LocalFileCtr.ts
import * as fs from 'fs/promises';
import * as path from 'path';
import { ControllerModule, IpcMethod } from '@/controllers';
export default class LocalFileCtr extends ControllerModule {
static override readonly groupName = 'localFiles';
@IpcMethod()
async renameFile(params: RenameLocalFileParams) {
const { oldPath, newName } = params;
const newPath = path.join(path.dirname(oldPath), newName);
try {
await fs.rename(oldPath, newPath);
return { success: true, newPath };
} catch (error) {
return { success: false, error: error.message };
}
}
}Step 6: Update Agent Documentation
Location: src/tools/[tool_category]/systemRole.ts
Add tool description to <core_capabilities> and usage guidelines to <tool_usage_guidelines>.
Desktop Menu Configuration Guide
Menu Types
1. App Menu: Top of window (macOS) or title bar (Windows/Linux) 2. Context Menu: Right-click menus 3. Tray Menu: System tray icon menus
File Structure
apps/desktop/src/main/
├── menus/
│ ├── appMenu.ts # App menu config
│ ├── contextMenu.ts # Context menu config
│ └── factory.ts # Menu factory functions
├── controllers/
│ ├── MenuCtr.ts # Menu controller
│ └── TrayMenuCtr.ts # Tray menu controllerApp Menu Configuration
// apps/desktop/src/main/menus/appMenu.ts
import { BrowserWindow, Menu, MenuItemConstructorOptions } from 'electron';
export const createAppMenu = (win: BrowserWindow) => {
const template: MenuItemConstructorOptions[] = [
{
label: 'File',
submenu: [
{
label: 'New',
accelerator: 'CmdOrCtrl+N',
click: () => {
/* ... */
},
},
{ type: 'separator' },
{ role: 'quit' },
],
},
// ...
];
return Menu.buildFromTemplate(template);
};
// Register in MenuCtr.ts
Menu.setApplicationMenu(menu);Context Menu
export const createContextMenu = () => {
const template = [
{ label: 'Copy', role: 'copy' },
{ label: 'Paste', role: 'paste' },
];
return Menu.buildFromTemplate(template);
};
// Show on right-click
const menu = createContextMenu();
menu.popup();Tray Menu
// TrayMenuCtr.ts
this.tray = new Tray(trayIconPath);
const contextMenu = Menu.buildFromTemplate([
{ label: 'Show Window', click: this.showMainWindow },
{ type: 'separator' },
{ label: 'Quit', click: () => app.quit() },
]);
this.tray.setContextMenu(contextMenu);i18n Support
import { i18n } from '../locales';
const template = [
{
label: i18n.t('menu.file'),
submenu: [{ label: i18n.t('menu.new'), click: createNew }],
},
];Best Practices
1. Use standard roles (role: 'copy') for native behavior 2. Use CmdOrCtrl for cross-platform shortcuts 3. Use { type: 'separator' } to group related items 4. Handle platform differences with process.platform
if (process.platform === 'darwin') {
template.unshift({ role: 'appMenu' });
}Desktop Window Management Guide
Window Management Overview
1. Window creation and configuration 2. Window state management (size, position, maximize) 3. Multi-window coordination 4. Window event handling
File Structure
apps/desktop/src/main/
├── appBrowsers.ts # Core window management
├── controllers/
│ └── BrowserWindowsCtr.ts # Window controller
└── modules/
└── browserWindowManager.ts # Window manager moduleWindow Creation
export const createMainWindow = () => {
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 600,
minHeight: 400,
webPreferences: {
preload: path.join(__dirname, '../preload/index.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
if (isDev) {
mainWindow.loadURL('http://localhost:3000');
} else {
mainWindow.loadFile(path.join(__dirname, '../../renderer/index.html'));
}
return mainWindow;
};Window State Persistence
const saveWindowState = (window: BrowserWindow) => {
if (!window.isMinimized() && !window.isMaximized()) {
const [x, y] = window.getPosition();
const [width, height] = window.getSize();
settings.set('windowState', { x, y, width, height });
}
};
const restoreWindowState = (window: BrowserWindow) => {
const state = settings.get('windowState');
if (state) {
window.setBounds({ x: state.x, y: state.y, width: state.width, height: state.height });
}
};
window.on('close', () => saveWindowState(window));Multi-Window Management
export class WindowManager {
private windows: Map<string, BrowserWindow> = new Map();
createWindow(id: string, options: BrowserWindowConstructorOptions) {
const window = new BrowserWindow(options);
this.windows.set(id, window);
window.on('closed', () => this.windows.delete(id));
return window;
}
getWindow(id: string) {
return this.windows.get(id);
}
}Window IPC Controller
// apps/desktop/src/main/controllers/BrowserWindowsCtr.ts
export default class BrowserWindowsCtr extends ControllerModule {
static override readonly groupName = 'windows';
@IpcMethod()
minimizeWindow() {
BrowserWindow.getFocusedWindow()?.minimize();
return { success: true };
}
@IpcMethod()
maximizeWindow() {
const win = BrowserWindow.getFocusedWindow();
win?.isMaximized() ? win.restore() : win?.maximize();
return { success: true };
}
}Renderer Service
// src/services/electron/windowService.ts
import { ensureElectronIpc } from '@/utils/electron/ipc';
const ipc = ensureElectronIpc();
export const windowService = {
minimize: () => ipc.windows.minimizeWindow(),
maximize: () => ipc.windows.maximizeWindow(),
close: () => ipc.windows.closeWindow(),
};Frameless Window
const window = new BrowserWindow({
frame: false,
titleBarStyle: 'hidden',
});.titlebar {
-webkit-app-region: drag;
}
.titlebar-button {
-webkit-app-region: no-drag;
}Best Practices
1. Use show: false initially, show after content loads 2. Always set secure webPreferences 3. Handle webContents.on('crashed') for recovery 4. Clean up resources on window.on('closed')
Related skills
Forks & variants (1)
Desktop has 1 known copy in the catalog totaling 1.5k installs. They canonicalize to this original listing.
- lobehub - 1.5k installs
How it compares
Pick desktop over generic frontend skills when the task requires Electron main-renderer IPC and OS-level APIs in a desktop AI agent shell.
FAQ
Where do controllers live?
apps/desktop/src/main/controllers/ with ControllerModule and IpcMethod.
How is security maintained?
Preload scripts in apps/desktop/src/preload expose only vetted IPC to renderer.
Can renderer reuse web code?
Yes. Renderer process reuses web code from src/ per architecture overview.
Is Desktop safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.