Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-003: Electron-First Desktop Shell

  • Status: Accepted
  • Date: 2026-05-17
  • Owners: Desktop shell maintainers
  • Supersedes (in part): ADR-001 — desktop runtime target selection

Context

ADR-001 established Tauri as the first-party native desktop shell, explicitly rejecting Electron. A functional Tauri shell was built and validated (packages/desktop, now packages/desktop-tauri).

After shipping the Tauri MVP, the following practical concerns emerged:

  • Build and toolchain friction. The Tauri build chain requires Rust, platform-specific system libraries, and a non-trivial CI matrix. Renderer and main-process code cannot share a single TypeScript toolchain—all native IPC commands must be maintained as a matching pair of Rust code and TypeScript bindings.
  • Webview rendering divergence. WKWebView (macOS) and WebView2 (Windows) do not share the same rendering engine as the development browser. CSS, layout, and canvas behaviour diverge silently and have already required several Tauri-specific workarounds in packages/workspace.
  • IPC model. Tauri’s string-serialised Rust IPC adds an extra marshalling layer and makes it harder to share types between the host and the renderer without a separate code-generation step.
  • Plugin host trajectory. The community-plugin host direction described in ADR-001 assumes a JavaScript-capable sidecar process. Electron’s Node.js main process makes that sidecar trivially available as a worker/child process; Tauri would require a separate JS runtime binary alongside the Rust shell.
  • Ecosystem tooling. The packaging, signing, notarisation, and auto-update ecosystem around Electron is more mature and better integrated with TypeScript monorepos.

The conclusion is that Electron is the better long-term host for a TypeScript-native notes app and is better aligned with the plugin host goals of ADR-001.

Update (2026-05-27): The paused Tauri shell (packages/desktop-tauri) and all tauri-desktop runtime code have since been removed from the repository. Electron is now the sole first-party desktop host. Legacy tauri-folder vault profiles are still accepted and migrated to desktop-folder on open.

Decision

Lapis Notes adopts Electron as the first-party native desktop shell.

1. Runtime targets

The approved product targets are:

  • web-pwa — browser-hosted and installable web application (unchanged)
  • electron-desktop — first-party native desktop distribution using Electron

2. Package layout

PackagePurposeStatus
packages/desktop-electronFirst-party Electron shellActive

Root dev:desktop and desktop:build scripts target @lapis-notes/desktop-electron.

3. Desktop host responsibilities (unchanged)

The host-responsibility split from ADR-001 and the existing desktop spec applies to the Electron shell unchanged:

  • @lapis-notes/workspace remains the renderer shell. It owns layout, vault bootstrap, bundled-plugin registration, and sequential boot.
  • @lapis-notes/desktop-electron owns Electron app lifecycle, native window behaviour, IPC command handlers, and desktop-only runtime adapters.
  • @lapis-notes/api remains the runtime kernel. The desktop host adds adapters, not domain services.

4. Native IPC command surface

The Electron shell exposes this native command surface through the shared API bridge:

  • desktop_pick_vault_folder — native folder-picker dialog
  • desktop_fs_* — vault-scoped file operations (exists, stat, read_text, read_binary, write_text, write_binary, list, mkdir, rmdir, remove, copy, rename)
  • desktop_db_load_state / desktop_db_save_state — generated-state persistence outside the vault

Path-safety rules are preserved: .., root, and absolute-subpath escapes are rejected; all operations are scoped beneath the selected vault root.

5. Desktop-neutral API bridge

The shared native desktop bridge in packages/api uses a runtime-neutral interface (NativeDesktopBridge, setNativeDesktopBridge, hasNativeDesktopBridge, etc.) implemented by the Electron shell.

RuntimeTarget includes electron-desktop. VaultStorageKind uses desktop-folder; legacy tauri-folder profiles are accepted and migrated transparently on open.

6. Community-plugin host trajectory

The plugin host direction from ADR-001 is unchanged in intent. The Electron main process takes the role of the JavaScript-capable sidecar. The concrete design of the Electron community-plugin host boundary is captured in Community Plugin Host Boundary.

7. Packaging and signing

Packaging identifiers, icons, signing/notarisation, auto-update policy, and release-channel metadata for the Electron shell are deferred to a separate task (TASK-DESKTOP-013).

Consequences

  • Electron becomes the gating validation target for native desktop work. CI, smoke tests, and first-party documentation describe the Electron shell.
  • The workspace renderer uses electron-desktop as the desktop runtime discriminator and data-desktop-drag-region markers for frameless window dragging.
  • Legacy tauri-folder vault profiles are migrated to desktop-folder on open so existing desktop vault state is preserved.

Alternatives Considered

1. Continue with Tauri and address toolchain friction incrementally

Rejected. The IPC type-safety and sidecar architecture problems are structural, not incidental. Tauri improvements would require ongoing parallel Rust and TypeScript maintenance that is disproportionate to the benefit given the current team size.

2. Target only web-pwa and drop native desktop entirely

Rejected. A native desktop distribution is a first-class product goal. File-system vault access, native menus, OS-level theming integration, and desktop-grade performance under large vaults are not achievable in the browser-only target.

3. Introduce a new desktop framework rather than reverting to Electron

Rejected. Electron’s ecosystem maturity, TypeScript-native main process, and existing Chromium rendering engine alignment with the development browser are decisive advantages. No other framework provides the same combination at this time.

Follow-up

  1. Rename packages/desktop to packages/desktop-tauri and create packages/desktop-electron (done).
  2. Implement the Electron native IPC command surface (done).
  3. Neutralise legacy desktop bridge names to NativeDesktopBridge and related symbols (done).
  4. Add Electron desktop validation and smoke coverage (done).
  5. Choose packaging identifiers, icons, signing/notarisation, and updater policy (TASK-DESKTOP-013).
  6. Add native file watching for local-folder vaults (done).
  7. Design the Electron community-plugin host boundary (completed in Community Plugin Host Boundary); implementation proceeds through the Plugin Runtime items documented on the owning contract pages.
  8. Remove the paused Tauri shell and tauri-desktop runtime code (done, 2026-05-27).