Closes the gap from the previous handoff: `npm run dist:all` now
produces Linux .deb/AppImage/snap, Windows NSIS/portable/zip, and
macOS dmg/zip artifacts. `.github/workflows/release.yml` builds all
three on tag push (v*) and attaches the union to a GitHub Release.
Bug fix in the same commit (would have broken any packaged build):
* `build.files` was `["src/**/*", ...]` — shipped `src/renderer/`
.tsx dev source into the asar. Replaced with a whitelist
(`src/main/**/*`, `src/preload.js`, `src/plugins/**/*`,
`package.json`) and moved the built renderer to
`build.extraResources` (`dist/renderer` -> `renderer/`).
* `src/main/window/index.js` now uses `process.resourcesPath` when
`app.isPackaged` is true, plus a friendly error if the renderer is
missing. Verified by extracting the .AppImage and launching the
binary — main process logs
`Production mode — loading .../resources/renderer/index.html`.
New in `package.json` build config:
* `mac.target`: dmg + zip for x64 and arm64; `darkModeSupport: true`,
`hardenedRuntime: false`, `gatekeeperAssess: false`,
`entitlements: null`. `mac.identity: null` retained (unsigned).
* `publish`: GitHub Releases provider. CI overrides with
`--publish=never`.
Known limitations (intentional follow-ups, not blockers):
* No `assets/icon.icns` — macOS app uses the default Electron icon.
This box lacks `iconutil`/`png2icns`; generating a proper .icns is
a one-line follow-up.
* No code signing — Windows SmartScreen will warn, macOS Gatekeeper
will quarantine. `CSC_LINK_BASE64`/`CSC_KEY_PASSWORD` secrets are
already wired in the Windows job for when the cert is added.
* No auto-update — user decision. The `publish` block writes the
metadata that a future `electron-updater` integration will consume.
Verified on this Linux box:
* `npm run build:renderer` then `npm run build:linux` — produced
AppImage (291M), deb (220M), snap (245M).
* `resources/app.asar` has 0 `src/renderer/*` entries (was the
pre-fix bug). 44 .tsx files remain in asar — all from
`node_modules/@hookform/resolvers` and `node_modules/immer` test
fixtures, not our code.
* Extracted `squashfs-root/resources/renderer/index.html` exists and
is what `window/index.js` loads.
* Binary runs for >12s without crashing — `loadFile` resolves
`process.resourcesPath/renderer/index.html` correctly.
Tests: 306/306 pass. No regressions.
Plan doc: docs/plans/2026-06-06-packaging-all-platforms.md (the
task-by-task execution guide I followed; included for traceability).