Installation

There are no prebuilt downloads of the desktop app at the moment, so installing it means building it from the repository. The build is driven by electron-vite, and packaging into an installer by electron-builder; both are already in devDependencies, so there’s nothing to install beyond npm install.

What you’ll need#

  • Node.js 22.12 or later, with npm.
  • Hugo on your PATH, if you want the live preview. The app runs without it; you just won’t get the Hugo server.
  • A Hugo site with an admin/site.yaml in it (admin/cms.yaml is optional, as everywhere else). If yours doesn’t have one yet, running the web dev server against it once (HUGO_ROOT=/path/to/site npm run dev, see Installation) writes both files.

Running from source#

Clone and install as for the web version, then start Electron in development mode:

git clone https://github.com/dmyates/emetcms.git
cd emetcms
npm install
HUGO_ROOT=/path/to/your/hugo/site npm run dev:desktop

dev:desktop runs electron-vite dev, which compiles the main process and preload script, starts a Vite dev server for the renderer and opens an Electron window pointed at it. Changes to the renderer hot-reload like they do in the browser; changes to src/main/ or src/preload/ aren’t picked up until you stop and rerun the command.

With HUGO_ROOT set, the app opens that site as soon as the window appears. Without it you get the splash screen and pick a folder yourself. You should see your site’s sections in the sidebar either way.

Building#

npm run build:desktop

This is electron-vite build, configured in electron.vite.config.ts, and produces three outputs under out/: out/main/ for the main process, out/preload/ for the bridge script and out/renderer/ for the React app. The main and preload builds use externalizeDepsPlugin() so Node built-ins and Electron itself aren’t bundled.

The renderer build is close to the web build but not the same thing. It has its own, simpler chunking (an editor chunk for Tiptap and a codemirror chunk for CodeMirror), it doesn’t inject the Content Security Policy that the web build does and it only includes the local-filesystem Vite plugins when HUGO_ROOT is set. Without HUGO_ROOT the renderer relies entirely on IPC to read the site, which is what you want for a packaged app.

Packaging an installer#

npm run package:desktop

This runs the build above and then electron-builder, which reads its configuration from the build block of package.json. The output lands in build/ (not dist/, which is the web build), as:

Platform Target
macOS .dmg
Windows NSIS installer (.exe)
Linux AppImage

electron-builder packages for the platform you’re running on. To target another one explicitly, run it directly, i.e. npx electron-builder --mac, --win or --linux. Note that macOS builds need a Mac, and Windows builds on Linux need Wine; the electron-builder documentation has the details. The app ID is com.emet.cms and the product name is “Emet CMS”.

The desktop-specific files#

Most of the source is shared with the web version. The parts that only exist for Electron are:

src/main/
  index.ts           window creation, menu, IPC registration, Hugo server lifecycle
  ipc.ts             filesystem, dialog, shell and Hugo IPC handlers
  hugo-server.ts     the HugoServerManager class
  menu.ts            the application menu
  path-validation.ts keeps IPC file access inside the opened site
src/preload/
  index.ts           exposes window.electronAPI via contextBridge
src/shared/
  ipc-types.ts       channel names and types shared by all three

On the renderer side, src/content/backends/electron.ts is the content backend that routes reads and writes through window.electronAPI, and src/hooks/useHugoServer.ts is the hook behind the server log panel.

If the window is blank#

Open the developer tools with View > Toggle Developer Tools and look at the console. The usual causes are a HUGO_ROOT that doesn’t exist, a failed renderer build (check the terminal running electron-vite) or dependencies that need a fresh npm install. If the app opens but the preview pane is empty, that’s the Hugo server rather than the app; see Local Hugo server.

Where next?#

  • Local Hugo server for how the preview works and what to do when it doesn’t.
  • Desktop app for what the app does and doesn’t do compared to the web version.