
Desktop Apps
- 88 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
desktop-apps is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- desktop-apps
- AI & Agent Building
- AI-coding skill
Desktop Apps by the numbers
- 88 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #4,935 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill desktop-appsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 88 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Desktop Application Development
Overview
Building cross-platform desktop applications using web technologies with Electron and Tauri.
---
Electron
Main Process
// main.ts
import { app, BrowserWindow, ipcMain, dialog, Menu } from 'electron';
import path from 'path';
let mainWindow: BrowserWindow | null = null;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
titleBarStyle: 'hiddenInset', // macOS
frame: process.platform !== 'darwin',
});
// Load the app
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:3000');
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}
// Window events
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// App lifecycle
app.whenReady().then(() => {
createWindow();
createMenu();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// IPC handlers
ipcMain.handle('dialog:openFile', async () => {
const result = await dialog.showOpenDialog(mainWindow!, {
properties: ['openFile'],
filters: [
{ name: 'Documents', extensions: ['txt', 'md', 'json'] },
],
});
if (!result.canceled && result.filePaths.length > 0) {
return result.filePaths[0];
}
return null;
});
ipcMain.handle('dialog:saveFile', async (_, content: string) => {
const result = await dialog.showSaveDialog(mainWindow!, {
filters: [{ name: 'JSON', extensions: ['json'] }],
});
if (!result.canceled && result.filePath) {
await fs.writeFile(result.filePath, content);
return result.filePath;
}
return null;
});
ipcMain.handle('app:getVersion', () => app.getVersion());
// Auto-updater
import { autoUpdater } from 'electron-updater';
autoUpdater.checkForUpdatesAndNotify();
autoUpdater.on('update-available', () => {
mainWindow?.webContents.send('update-available');
});
autoUpdater.on('update-downloaded', () => {
mainWindow?.webContents.send('update-downloaded');
});
ipcMain.handle('app:installUpdate', () => {
autoUpdater.quitAndInstall();
});Preload Script
// preload.ts
import { contextBridge, ipcRenderer } from 'electron';
// Expose safe APIs to renderer
contextBridge.exposeInMainWorld('electronAPI', {
// File operations
openFile: () => ipcRenderer.invoke('dialog:openFile'),
saveFile: (content: string) => ipcRenderer.invoke('dialog:saveFile', content),
readFile: (path: string) => ipcRenderer.invoke('fs:readFile', path),
writeFile: (path: string, content: string) =>
ipcRenderer.invoke('fs:writeFile', path, content),
// App info
getVersion: () => ipcRenderer.invoke('app:getVersion'),
getPlatform: () => process.platform,
// Updates
installUpdate: () => ipcRenderer.invoke('app:installUpdate'),
onUpdateAvailable: (callback: () => void) => {
ipcRenderer.on('update-available', callback);
return () => ipcRenderer.removeListener('update-available', callback);
},
onUpdateDownloaded: (callback: () => void) => {
ipcRenderer.on('update-downloaded', callback);
return () => ipcRenderer.removeListener('update-downloaded', callback);
},
// Window controls
minimize: () => ipcRenderer.send('window:minimize'),
maximize: () => ipcRenderer.send('window:maximize'),
close: () => ipcRenderer.send('window:close'),
// Native notifications
showNotification: (title: string, body: string) =>
ipcRenderer.invoke('notification:show', title, body),
});
// TypeScript types for renderer
declare global {
interface Window {
electronAPI: {
openFile: () => Promise<string | null>;
saveFile: (content: string) => Promise<string | null>;
readFile: (path: string) => Promise<string>;
writeFile: (path: string, content: string) => Promise<void>;
getVersion: () => Promise<string>;
getPlatform: () => string;
installUpdate: () => Promise<void>;
onUpdateAvailable: (callback: () => void) => () => void;
onUpdateDownloaded: (callback: () => void) => () => void;
minimize: () => void;
maximize: () => void;
close: () => void;
showNotification: (title: string, body: string) => Promise<void>;
};
}
}Renderer (React)
// App.tsx
function App() {
const [updateAvailable, setUpdateAvailable] = useState(false);
const [updateReady, setUpdateReady] = useState(false);
useEffect(() => {
const removeAvailable = window.electronAPI.onUpdateAvailable(() => {
setUpdateAvailable(true);
});
const removeDownloaded = window.electronAPI.onUpdateDownloaded(() => {
setUpdateReady(true);
});
return () => {
removeAvailable();
removeDownloaded();
};
}, []);
const handleOpenFile = async () => {
const filePath = await window.electronAPI.openFile();
if (filePath) {
const content = await window.electronAPI.readFile(filePath);
// Handle file content
}
};
const handleSaveFile = async () => {
const content = JSON.stringify(data, null, 2);
await window.electronAPI.saveFile(content);
};
return (
<div className="app">
{/* Custom title bar for frameless window */}
<TitleBar />
<main>
<button onClick={handleOpenFile}>Open File</button>
<button onClick={handleSaveFile}>Save File</button>
{updateReady && (
<button onClick={() => window.electronAPI.installUpdate()}>
Install Update & Restart
</button>
)}
</main>
</div>
);
}
// Custom title bar component
function TitleBar() {
const platform = window.electronAPI.getPlatform();
return (
<div className="title-bar" style={{ WebkitAppRegion: 'drag' }}>
<span className="title">My App</span>
{platform !== 'darwin' && (
<div className="window-controls" style={{ WebkitAppRegion: 'no-drag' }}>
<button onClick={() => window.electronAPI.minimize()}>−</button>
<button onClick={() => window.electronAPI.maximize()}>□</button>
<button onClick={() => window.electronAPI.close()}>×</button>
</div>
)}
</div>
);
}---
Tauri
Rust Backend
// src-tauri/src/main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{CustomMenuItem, Menu, MenuItem, Submenu};
use std::fs;
// Commands callable from frontend
#[tauri::command]
fn read_file(path: String) -> Result<String, String> {
fs::read_to_string(&path).map_err(|e| e.to_string())
}
#[tauri::command]
fn write_file(path: String, content: String) -> Result<(), String> {
fs::write(&path, &content).map_err(|e| e.to_string())
}
#[tauri::command]
fn get_app_version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[tauri::command]
async fn perform_heavy_task(input: String) -> Result<String, String> {
// Run CPU-intensive work in background
tokio::task::spawn_blocking(move || {
// Heavy computation here
format!("Processed: {}", input)
})
.await
.map_err(|e| e.to_string())
}
fn main() {
let menu = Menu::new()
.add_submenu(Submenu::new(
"File",
Menu::new()
.add_item(CustomMenuItem::new("open", "Open").accelerator("CmdOrCtrl+O"))
.add_item(CustomMenuItem::new("save", "Save").accelerator("CmdOrCtrl+S"))
.add_native_item(MenuItem::Separator)
.add_native_item(MenuItem::Quit),
))
.add_submenu(Submenu::new(
"Edit",
Menu::new()
.add_native_item(MenuItem::Undo)
.add_native_item(MenuItem::Redo)
.add_native_item(MenuItem::Separator)
.add_native_item(MenuItem::Cut)
.add_native_item(MenuItem::Copy)
.add_native_item(MenuItem::Paste),
));
tauri::Builder::default()
.menu(menu)
.on_menu_event(|event| {
match event.menu_item_id() {
"open" => {
event.window().emit("menu-open", {}).unwrap();
}
"save" => {
event.window().emit("menu-save", {}).unwrap();
}
_ => {}
}
})
.invoke_handler(tauri::generate_handler![
read_file,
write_file,
get_app_version,
perform_heavy_task,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Tauri Configuration
// src-tauri/tauri.conf.json
{
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:3000",
"distDir": "../dist"
},
"package": {
"productName": "My App",
"version": "1.0.0"
},
"tauri": {
"allowlist": {
"all": false,
"dialog": {
"all": true
},
"fs": {
"all": true,
"scope": ["$DOCUMENT/*", "$DOWNLOAD/*"]
},
"shell": {
"open": true
},
"notification": {
"all": true
}
},
"bundle": {
"active": true,
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/icon.icns",
"icons/icon.ico"
],
"identifier": "com.example.myapp",
"targets": "all"
},
"windows": [
{
"title": "My App",
"width": 1200,
"height": 800,
"minWidth": 800,
"minHeight": 600,
"resizable": true,
"fullscreen": false
}
],
"updater": {
"active": true,
"endpoints": ["https://releases.example.com/{{target}}/{{current_version}}"],
"pubkey": "YOUR_PUBLIC_KEY"
}
}
}Frontend Integration
// Using Tauri APIs
import { invoke } from '@tauri-apps/api/tauri';
import { open, save } from '@tauri-apps/api/dialog';
import { readTextFile, writeTextFile } from '@tauri-apps/api/fs';
import { sendNotification } from '@tauri-apps/api/notification';
import { listen } from '@tauri-apps/api/event';
// Call Rust commands
async function readFile(path: string): Promise<string> {
return invoke('read_file', { path });
}
async function writeFile(path: string, content: string): Promise<void> {
return invoke('write_file', { path, content });
}
// Use Tauri dialog
async function openFileDialog() {
const selected = await open({
multiple: false,
filters: [{ name: 'Documents', extensions: ['txt', 'md', 'json'] }],
});
if (selected && typeof selected === 'string') {
const content = await readTextFile(selected);
return { path: selected, content };
}
return null;
}
async function saveFileDialog(content: string) {
const filePath = await save({
filters: [{ name: 'JSON', extensions: ['json'] }],
});
if (filePath) {
await writeTextFile(filePath, content);
return filePath;
}
return null;
}
// Listen for menu events
listen('menu-open', async () => {
const file = await openFileDialog();
if (file) {
// Handle file
}
});
// Send notification
async function notify(title: string, body: string) {
await sendNotification({ title, body });
}---
Local Storage & Database
// SQLite with better-sqlite3 (Electron)
import Database from 'better-sqlite3';
const db = new Database('app.db');
// Initialize schema
db.exec(`
CREATE TABLE IF NOT EXISTS documents (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// CRUD operations
const insertDoc = db.prepare(
'INSERT INTO documents (id, title, content) VALUES (?, ?, ?)'
);
const getDoc = db.prepare('SELECT * FROM documents WHERE id = ?');
const getAllDocs = db.prepare('SELECT * FROM documents ORDER BY updated_at DESC');
const updateDoc = db.prepare(
'UPDATE documents SET title = ?, content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?'
);
const deleteDoc = db.prepare('DELETE FROM documents WHERE id = ?');
// Usage
insertDoc.run(uuid(), 'New Document', '');
const doc = getDoc.get('doc-id');
const docs = getAllDocs.all();---
Related Skills
- [[frontend]] - Web technologies
- [[system-design]] - Application architecture
- [[devops-cicd]] - Desktop app distribution
/**
* Electron Main Process Template
* Usage: Copy to src/main/index.ts
*
* Features:
* - Window management
* - IPC communication
* - System tray
* - Auto-updater
* - Security best practices
*/
import { app, BrowserWindow, ipcMain, Menu, Tray, shell, nativeTheme } from 'electron';
import { autoUpdater } from 'electron-updater';
import * as path from 'path';
import * as fs from 'fs';
// ===========================================
// Configuration
// ===========================================
const isDev = !app.isPackaged;
const isMac = process.platform === 'darwin';
interface AppConfig {
width: number;
height: number;
minWidth: number;
minHeight: number;
}
const config: AppConfig = {
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
};
// ===========================================
// Window Management
// ===========================================
let mainWindow: BrowserWindow | null = null;
let tray: Tray | null = null;
function createWindow(): void {
mainWindow = new BrowserWindow({
width: config.width,
height: config.height,
minWidth: config.minWidth,
minHeight: config.minHeight,
show: false, // Show when ready
frame: true, // Set false for custom titlebar
titleBarStyle: isMac ? 'hiddenInset' : 'default',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
sandbox: true,
webSecurity: true,
},
});
// Load content
if (isDev) {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
}
// Show when ready to prevent visual flash
mainWindow.once('ready-to-show', () => {
mainWindow?.show();
});
// Handle external links
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
return { action: 'deny' };
});
// Window events
mainWindow.on('closed', () => {
mainWindow = null;
});
mainWindow.on('close', (event) => {
// Minimize to tray instead of closing (optional)
if (tray && !app.isQuitting) {
event.preventDefault();
mainWindow?.hide();
}
});
}
// ===========================================
// System Tray
// ===========================================
function createTray(): void {
const iconPath = path.join(__dirname, '../assets/tray-icon.png');
if (fs.existsSync(iconPath)) {
tray = new Tray(iconPath);
const contextMenu = Menu.buildFromTemplate([
{ label: 'Show App', click: () => mainWindow?.show() },
{ type: 'separator' },
{ label: 'Quit', click: () => app.quit() },
]);
tray.setToolTip('My App');
tray.setContextMenu(contextMenu);
tray.on('click', () => {
mainWindow?.isVisible() ? mainWindow.hide() : mainWindow?.show();
});
}
}
// ===========================================
// Application Menu
// ===========================================
function createMenu(): void {
const template: Electron.MenuItemConstructorOptions[] = [
...(isMac
? [
{
label: app.name,
submenu: [
{ role: 'about' as const },
{ type: 'separator' as const },
{ role: 'services' as const },
{ type: 'separator' as const },
{ role: 'hide' as const },
{ role: 'hideOthers' as const },
{ role: 'unhide' as const },
{ type: 'separator' as const },
{ role: 'quit' as const },
],
},
]
: []),
{
label: 'File',
submenu: [
{ label: 'New', accelerator: 'CmdOrCtrl+N', click: () => sendToRenderer('menu:new') },
{ label: 'Open', accelerator: 'CmdOrCtrl+O', click: () => sendToRenderer('menu:open') },
{ label: 'Save', accelerator: 'CmdOrCtrl+S', click: () => sendToRenderer('menu:save') },
{ type: 'separator' },
isMac ? { role: 'close' } : { role: 'quit' },
],
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
{ role: 'selectAll' },
],
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
],
},
{
label: 'Window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
...(isMac
? [{ type: 'separator' as const }, { role: 'front' as const }]
: [{ role: 'close' as const }]),
],
},
{
role: 'help',
submenu: [
{
label: 'Documentation',
click: () => shell.openExternal('https://example.com/docs'),
},
{
label: 'Report Issue',
click: () => shell.openExternal('https://github.com/org/repo/issues'),
},
],
},
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
// ===========================================
// IPC Handlers
// ===========================================
function setupIPC(): void {
// Get app info
ipcMain.handle('app:info', () => ({
name: app.name,
version: app.getVersion(),
platform: process.platform,
isDev,
}));
// Get/set store value
ipcMain.handle('store:get', (_event, key: string) => {
// Implement with electron-store
return null;
});
ipcMain.handle('store:set', (_event, key: string, value: unknown) => {
// Implement with electron-store
return true;
});
// File operations
ipcMain.handle('fs:read', async (_event, filePath: string) => {
return fs.promises.readFile(filePath, 'utf-8');
});
ipcMain.handle('fs:write', async (_event, filePath: string, content: string) => {
await fs.promises.writeFile(filePath, content, 'utf-8');
return true;
});
// Dialog operations
ipcMain.handle('dialog:open', async (_event, options: Electron.OpenDialogOptions) => {
const { dialog } = await import('electron');
return dialog.showOpenDialog(mainWindow!, options);
});
ipcMain.handle('dialog:save', async (_event, options: Electron.SaveDialogOptions) => {
const { dialog } = await import('electron');
return dialog.showSaveDialog(mainWindow!, options);
});
// Theme
ipcMain.handle('theme:get', () => nativeTheme.themeSource);
ipcMain.handle('theme:set', (_event, mode: 'system' | 'light' | 'dark') => {
nativeTheme.themeSource = mode;
return mode;
});
}
function sendToRenderer(channel: string, ...args: unknown[]): void {
mainWindow?.webContents.send(channel, ...args);
}
// ===========================================
// Auto Updater
// ===========================================
function setupAutoUpdater(): void {
if (isDev) return;
autoUpdater.checkForUpdatesAndNotify();
autoUpdater.on('update-available', () => {
sendToRenderer('update:available');
});
autoUpdater.on('update-downloaded', () => {
sendToRenderer('update:downloaded');
});
ipcMain.handle('update:install', () => {
autoUpdater.quitAndInstall();
});
}
// ===========================================
// App Lifecycle
// ===========================================
// Prevent multiple instances
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on('second-instance', () => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
});
app.whenReady().then(() => {
createWindow();
createMenu();
createTray();
setupIPC();
setupAutoUpdater();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
}
app.on('window-all-closed', () => {
if (!isMac) {
app.quit();
}
});
app.on('before-quit', () => {
(app as any).isQuitting = true;
});
// Security: Disable navigation to external URLs
app.on('web-contents-created', (_event, contents) => {
contents.on('will-navigate', (event, url) => {
const parsed = new URL(url);
if (parsed.origin !== 'http://localhost:5173' && !url.startsWith('file://')) {
event.preventDefault();
}
});
});
export { mainWindow };
Desktop Apps Templates
Configuration templates for cross-platform desktop applications.
Files
| Template | Purpose |
|---|---|
electron-main.ts | Electron main process |
tauri.conf.json | Tauri configuration |
Framework Comparison
| Feature | Electron | Tauri |
|---|---|---|
| Language | JavaScript | Rust + JS |
| Bundle Size | ~150MB | ~3MB |
| Memory | Higher | Lower |
| Native APIs | Via Node.js | Rust + Webview |
| Maturity | High | Growing |
Electron Setup
Quick Start
# Create project
npm create electron-vite@latest my-app
# Copy template
cp templates/electron-main.ts src/main/index.ts
# Install dependencies
npm install electron-updater
npm install -D electron electron-builder
# Run
npm run devFeatures
| Feature | Implementation |
|---|---|
| Window Management | BrowserWindow |
| System Tray | Tray + Menu |
| IPC | ipcMain/ipcRenderer |
| Auto Update | electron-updater |
| Security | CSP, sandboxing |
IPC Channels
// Main process
ipcMain.handle('app:info', () => ({ version: '1.0.0' }));
// Preload (contextBridge)
contextBridge.exposeInMainWorld('api', {
getInfo: () => ipcRenderer.invoke('app:info'),
});
// Renderer
const info = await window.api.getInfo();Preload Script
// preload.ts
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('api', {
// App
getInfo: () => ipcRenderer.invoke('app:info'),
// Store
store: {
get: (key: string) => ipcRenderer.invoke('store:get', key),
set: (key: string, value: unknown) => ipcRenderer.invoke('store:set', key, value),
},
// File system
fs: {
read: (path: string) => ipcRenderer.invoke('fs:read', path),
write: (path: string, content: string) => ipcRenderer.invoke('fs:write', path, content),
},
// Dialogs
dialog: {
open: (options: any) => ipcRenderer.invoke('dialog:open', options),
save: (options: any) => ipcRenderer.invoke('dialog:save', options),
},
// Events from main
on: (channel: string, callback: Function) => {
ipcRenderer.on(channel, (_event, ...args) => callback(...args));
},
});Tauri Setup
Quick Start
# Create project
npm create tauri-app@latest
# Copy configuration
cp templates/tauri.conf.json src-tauri/tauri.conf.json
# Run
npm run tauri dev
# Build
npm run tauri buildKey Configuration
| Section | Purpose |
|---|---|
allowlist | Enabled APIs |
bundle | App metadata, icons |
security.csp | Content Security Policy |
updater | Auto-update settings |
windows | Window properties |
Allowlist APIs
{
"allowlist": {
"fs": { "readFile": true, "scope": ["$APP/*"] },
"dialog": { "open": true, "save": true },
"shell": { "open": true },
"clipboard": { "all": true }
}
}Tauri Commands (Rust)
// src-tauri/src/main.rs
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Calling from Frontend
import { invoke } from '@tauri-apps/api/tauri';
const greeting = await invoke('greet', { name: 'World' });Project Structure
Electron
my-app/
├── package.json
├── electron-builder.yml
├── src/
│ ├── main/
│ │ ├── index.ts # Main process
│ │ └── preload.ts # Preload script
│ └── renderer/ # React/Vue/etc
├── resources/
│ └── icons/
└── dist/Tauri
my-app/
├── package.json
├── src/ # Frontend
├── src-tauri/
│ ├── Cargo.toml
│ ├── tauri.conf.json
│ ├── icons/
│ └── src/
│ └── main.rs
└── dist/Distribution
Electron Builder
# electron-builder.yml
appId: com.example.myapp
productName: My App
directories:
output: release
mac:
target: [dmg, zip]
category: public.app-category.developer-tools
win:
target: [nsis, portable]
linux:
target: [AppImage, deb]Tauri Build
# All platforms
npm run tauri build
# Specific target
npm run tauri build -- --target x86_64-apple-darwinAuto Updates
Electron
import { autoUpdater } from 'electron-updater';
autoUpdater.checkForUpdatesAndNotify();Tauri
Configure updater in tauri.conf.json with your update server endpoint.
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:5173",
"distDir": "../dist"
},
"package": {
"productName": "My App",
"version": "1.0.0"
},
"tauri": {
"allowlist": {
"all": false,
"shell": {
"all": false,
"open": true
},
"dialog": {
"all": true,
"ask": true,
"confirm": true,
"message": true,
"open": true,
"save": true
},
"fs": {
"all": false,
"readFile": true,
"writeFile": true,
"readDir": true,
"createDir": true,
"removeDir": true,
"removeFile": true,
"renameFile": true,
"exists": true,
"scope": ["$APP/*", "$DOCUMENT/*", "$DOWNLOAD/*"]
},
"path": {
"all": true
},
"os": {
"all": true
},
"process": {
"all": false,
"exit": true,
"relaunch": true
},
"globalShortcut": {
"all": true
},
"clipboard": {
"all": true,
"writeText": true,
"readText": true
},
"notification": {
"all": true
},
"window": {
"all": false,
"close": true,
"hide": true,
"show": true,
"maximize": true,
"minimize": true,
"unmaximize": true,
"unminimize": true,
"startDragging": true,
"setTitle": true,
"setFullscreen": true,
"setFocus": true
},
"http": {
"all": false,
"request": true,
"scope": ["https://api.example.com/*"]
}
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "Copyright © 2024 Your Company",
"deb": {
"depends": []
},
"externalBin": [],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"identifier": "com.example.myapp",
"longDescription": "A cross-platform desktop application built with Tauri.",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null,
"minimumSystemVersion": "10.15"
},
"resources": [],
"shortDescription": "My App Description",
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"wix": null
}
},
"security": {
"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost data:; script-src 'self'; style-src 'self' 'unsafe-inline'"
},
"updater": {
"active": true,
"endpoints": ["https://releases.example.com/{{target}}/{{arch}}/{{current_version}}"],
"dialog": true,
"pubkey": "YOUR_PUBLIC_KEY_HERE"
},
"windows": [
{
"fullscreen": false,
"height": 800,
"width": 1200,
"minHeight": 600,
"minWidth": 800,
"resizable": true,
"title": "My App",
"center": true,
"decorations": true,
"fileDropEnabled": true,
"transparent": false
}
],
"systemTray": {
"iconPath": "icons/tray.png",
"iconAsTemplate": true,
"menuOnLeftClick": false
}
}
}
Related skills
AI & Agent Buildingagents