# J621 desktop (Electron) An Electron shell around the web build. The main process serves the SPA from the privileged `app://j621` scheme — a real origin, so `localStorage`, OPFS, Web Workers and WebCodecs all behave exactly like they do in Chrome — and the SPA keeps its normal first-run flow: on launch it asks where the backend is (or runs in local mode with no backend at all). The renderer code is not forked: the shell loads `frontend/dist` as built by `npm run build` in `frontend/`. Licensed under the Jake Labs Non-Commercial Software Licence — see `../LICENSE`. ## Layout ``` src/main.ts window, app:// protocol handler, menu, external links src/preload.ts window.j621Desktop bridge electron-builder.yml deb + pacman + nsis packaging ``` The main process attaches a `Referer` to requests for e621 hosts: Chromium sends no referrer from a custom-scheme page, and e621's CDN answers cross-site image loads without one with a 403 (which Chromium then blocks as ORB). API calls work either way. ## Development ```bash # terminal 1: the SPA with its /api proxy (needs the backend for library pages) cd frontend && npm run dev # terminal 2 cd desktop && npm run dev ``` `npm run dev` builds the main process and points the window at `http://localhost:5173` (`J621_DEV_SERVER`). Without the dev server, `npm start` builds both halves and serves the bundled SPA over `app://j621`. ## Builds ```bash npm run dist:linux # release/j621-desktop_0.1.0_amd64.deb + j621-desktop-0.1.0.pkg.tar.zst npm run dist:win # release/J621 Setup 0.1.0.exe (cross-built with wine; untested) npm run dist:all ``` `deploy/build_desktop.sh` wraps the same commands, installs dependencies on first run and prints sizes plus SHA-256 sums for release notes. Nothing is published by it; `deploy/push_desktop.sh` is the one that feeds auto-updates. The Arch package can be installed and removed with pacman: ```bash sudo pacman -U release/j621-desktop-*.pkg.tar.zst sudo pacman -R j621-desktop ``` It installs to `/opt/J621` with a `/usr/bin/j621-desktop` symlink and a `io.j621.desktop` launcher entry (`StartupWMClass` matches the shell's `desktopName`). The Windows installer is a per-user NSIS build, so no admin rights are needed to install or update it — but it is unsigned, so SmartScreen will warn, and it has not been smoke-tested on real Windows. ## Updates The app checks only when asked (**J621 → Check for updates…** in the menu): Linux packages install through pacman/dpkg, which needs administrator rights, and the Windows build is unsigned, so nothing installs silently. The check reads `latest-linux.yml` / `latest.yml` from the feed configured in `electron-builder.yml` (`publish.url`, baked into `app-update.yml`); set `J621_UPDATE_URL` to point a build at another feed (the smoke test uses this). Publishing a release: 1. Bump `version` in `desktop/package.json` — that is what the updater compares. 2. `./deploy/push_desktop.sh --win` builds deb/pacman/NSIS and copies the artifacts plus both channel files into `deploy/data/desktop/`, which the frontend nginx serves read-only at `/desktop/`. 3. Existing installs find the new version on their next manual check. `package-type` in the app resources tells electron-updater whether to run `pacman -U` or `dpkg -i` (both via pkexec/sudo); the per-user NSIS install updates without elevation. ## Smoke test `npm run smoke` builds everything, runs Electron headless through `xvfb-run` and checks that the bundled SPA loads over `app://j621`: the SPA fallback, an asset fetch, `localStorage`, OPFS, WebCodecs and the preload bridge. It exits non-zero on failure. With `J621_UPDATE_URL` set it also checks that the feed reports the expected version. Pointing `J621_DEV_SERVER` at the Vite dev server checks the development path instead of the bundled one. ## Backend CORS A desktop app is cross-origin to the backend, exactly like the frontend-only Docker deployment. Add the shell's origin to the backend: ``` CORS_ALLOWED_ORIGINS=app://j621 ``` The setup screen shows this hint (with the actual origin) when the connection test fails. Without a backend, choose "Continue without a backend" — the e621-facing pages work the same as in the browser.