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

Desktop Shell

Lapis Notes supports two host shells:

  • a web/PWA host built directly from @lapis-notes/workspace
  • an Electron desktop host that wraps the same renderer/runtime packages

The decision to adopt Electron as the first-party native desktop shell is recorded in ADR-003: Electron-First Desktop Shell. The approved target architecture for the plugin boundary across those hosts is recorded in ADR-001: Plugin Runtime Rework (desktop runtime target superseded in part by ADR-003) and refined in Community Plugin Host Boundary.

Current state in the repo

  • packages/workspace is the shared renderer shell.
  • packages/web is the installable browser/PWA host around that shared shell.
  • packages/desktop-electron is the first-party Electron desktop host package. It owns Electron app lifecycle, native window behaviour, IPC command handlers, and desktop-only runtime adapters. That host-owned window policy applies to secondary app windows as well as the initial window, so Electron child windows opened through the shared workspace popout path inherit the same shell chrome, preload, and shell-metric configuration as the main window. Those popouts do not boot a second shared runtime; instead, the main app-host window remains the single renderer runtime and focused popout windows route native menu actions and inbound app URLs back through that owner window.

Approved boundary

The accepted shell split is:

  1. packages/workspace remains the shared renderer shell across all runtime targets.
  2. The Electron desktop shell owns native runtime metadata, native capability bridging, and the future community-plugin host boundary via the Node.js main process.
  3. Core domain behavior stays in packages/api, packages/workspace, or first-party plugins unless a concern is truly host-specific.

Plugin host implications

Today, community plugins still execute inside the active renderer/runtime process. The approved direction does not change the on-disk plugin format, but it does move shell-specific isolation and native capability brokering into the host layer:

  • web/PWA keeps a constrained renderer-hosted community-plugin path
  • Electron desktop gains the stronger host boundary for community plugins via Node.js worker/child-process capabilities

Those runtime-specific behaviors are split into implementation slices on the owning package and contract pages, with Plugin Runtime items summarized in Plugin Runtime.

Shell reference shape

The Electron desktop shell follows a standard Electron + Vite + Svelte pattern:

  • a renderer package with a fixed Vite dev port (1421) and static build output
  • an Electron main process (src-electron/main.ts) that creates BrowserWindow, registers IPC handlers, and drives native menus
  • a context-isolated preload script (src-electron/preload.ts) that exposes the native bridge to the renderer via contextBridge.exposeInMainWorld
  • the renderer entry (src/main.ts) reads the preload bridge and calls setNativeDesktopBridge() before calling mountWorkspaceApp()

Lapis borrows this shape while keeping the shared @lapis-notes/workspace renderer bootstrap in place.