- Add electron-updater dependency and implement native desktop integration utilities for dialogs, notifications, auto-update wiring, and window state persistence - Add fusion:// deep-link protocol registration, URL parsing, single-instance handlers, and renderer event forwarding for task/project routes - Add comprehensive Vitest coverage for native integrations and deep-link handling across success, edge, and failure cases - Document native and deep-link API contracts, behavior, and usage details in the desktop package README
@fusion/desktop
Electron desktop shell for Fusion.
This package provides a native Electron wrapper around the existing Fusion dashboard web UI. The desktop shell connects to a running dashboard server and presents native desktop affordances including a system tray and application menu.
Prerequisites
Start the Fusion dashboard server first:
fn dashboard
Then, in another terminal, start the desktop app:
pnpm --filter @fusion/desktop dev
System Tray
- Left-clicking the tray icon toggles the main window visibility.
- Right-click context menu includes:
- Show/Hide Window (contextual based on visibility)
- Pause/Resume Engine (status toggle placeholder; IPC wiring lands in FN-1076)
- Quit Fusion
- Tray tooltip reflects engine status:
Fusion — RunningFusion — PausedFusion — Stopped
- Tray icon is generated from the Fusion four-dot logo.
Application Menu
The desktop shell installs a native menu with standard shortcuts.
- macOS: App, Edit, View, Window, and Help menus.
- Windows/Linux: Edit, View, Window, and Help (no App menu).
- Keyboard shortcuts use Electron
CmdOrCtrlaccelerators for cross-platform behavior. - View menu includes reload, force reload, dev tools toggle, and zoom controls.
Native Integrations
src/native.ts provides desktop-native utilities used by the Electron main process:
- Settings file dialogs
showExportSettingsDialog(parentWindow?)opens a save dialog for JSON exports using a default filename likefusion-settings-YYYY-MM-DD-HHmmss.json.showImportSettingsDialog(parentWindow?)opens a single-file JSON picker.
- Desktop notifications
showDesktopNotification(title, body, options?)wraps ElectronNotificationwith support checks and optional click callback wiring.
- Auto-updater integration
setupAutoUpdater(mainWindow?)configureselectron-updater, checks for updates, and relaysupdate-available/update-downloadedevents to the renderer via IPC.- Failures are logged and treated as non-fatal (important for unsigned/local dev builds).
- Window state persistence
loadWindowState()readswindow-state.jsonfromapp.getPath("userData").saveWindowState(mainWindow)writes bounds/maximized state atomically (.tmp+ rename).DEFAULT_WINDOW_STATEis the fallback (1280x900, not maximized).
Deep Linking
src/deep-link.ts implements fusion:// protocol support.
Supported URL patterns
fusion://task/FN-123→ task deep linkfusion://project/my-app→ project deep linkfusion://task/FN-123/extra→ extra segments are ignoredfusion://project/my%20app→ ID is URL-decoded
Invalid or unsupported URLs (wrong scheme, missing host, unknown host) are ignored.
Single-instance behavior and platform differences
setupDeepLinkHandler(mainWindow)ownsapp.requestSingleInstanceLock().- If no lock is granted, the app quits to avoid duplicate instances.
- macOS: listens to
open-urlevents. - Windows/Linux: listens to
second-instanceargs and extractsfusion://URLs. - Valid parsed deep links are forwarded to the renderer as
mainWindow.webContents.send("deep-link", result).
Cross-Task API Contract (FN-1075 → FN-1076)
FN-1076 depends on these exact exports and names.
src/native.ts
| Export | Type |
|---|---|
showExportSettingsDialog |
(parentWindow?) => Promise<string | null> |
showImportSettingsDialog |
(parentWindow?) => Promise<string | null> |
showDesktopNotification |
(title, body, options?) => void |
setupAutoUpdater |
(mainWindow?) => void |
loadWindowState |
() => Promise<WindowState | null> |
saveWindowState |
(mainWindow) => void |
DEFAULT_WINDOW_STATE |
WindowState |
WindowState |
interface |
src/deep-link.ts
| Export | Type |
|---|---|
registerDeepLinkProtocol |
() => void |
parseDeepLink |
(url: string) => DeepLinkResult | null |
handleDeepLink |
(mainWindow, url: string) => void |
setupDeepLinkHandler |
(mainWindow) => void |
DeepLinkResult |
interface |
Tray Icons
Tray icons are generated from packages/dashboard/app/public/logo.svg.
- Script:
pnpm --filter @fusion/desktop generate:icons - Package-local equivalent (from
packages/desktop):pnpm generate:icons - Generated outputs are committed under
src/icons/:tray-16.pngtray-32.pngtray-48.png
Scripts
pnpm --filter @fusion/desktop dev— run the Electron main process in developmentpnpm --filter @fusion/desktop build— compile TypeScript sourcespnpm --filter @fusion/desktop test— run Vitest suitepnpm --filter @fusion/desktop typecheck— run TypeScript checks without emitting filespnpm --filter @fusion/desktop generate:icons— regenerate tray icon PNG assets from the dashboard logo SVGpnpm --filter @fusion/desktop pack— build distributable package via electron-builderpnpm --filter @fusion/desktop dist— build distribution artifacts without publishing
Environment
FUSION_DASHBOARD_URL— override the default dashboard URL used by the desktop shell (http://localhost:4040)