scmJS docs

Desktop

desktop/ is an Electron shell around the same web bundle. main.ts serves dist/ under app://scmjs/, looks for a StarCraft installation and extracts the game data into the app's data folder, opens maps handed over by the file manager, guards the window's close while a map has unsaved changes, and holds the updater. preload.ts exposes that to the page as window.scmjsDesktop, typed in src/gamedata/desktop.ts.

npm run desktop                 # bundle the main process and run Electron against dist/
SCMJS_DEV_URL=http://localhost:5173 npm run desktop    # against the dev server instead
npm run build:desktop           # web build + main bundle + electron-builder for this OS

npm run build:desktop takes the platform, architectures and targets on the command line; with none it builds this OS with the targets electron-builder.yml lists, which is what CI does:

npm run build:desktop -- win                 # Windows: nsis + zip
npm run build:desktop -- win nsis            # just the installer
npm run build:desktop -- linux AppImage x64 arm64
npm run build:desktop -- mac dmg arm64
npm run build:desktop -- --dir               # unpacked app, no installer: the fast check
npm run build:desktop -- win --skip-web      # repackage the dist/ already on disk
npm run build:desktop -- --skip-plugins      # leave plugins/ alone (no vendoring fetch)

Platforms are win, mac and linux; architectures x64, ia32, arm64, armv7l and universal; any electron-builder target name (nsis, dmg, zip, AppImage, deb, …) applies to every platform named. --skip-web and --skip-main reuse the bundles on disk; --publish <mode> and anything after a bare -- go to electron-builder unchanged. The script warns about cross-builds that need tooling the machine may lack (a macOS installer off a Mac, an NSIS installer without Wine) and runs them anyway; --dir and zip cross-build with nothing installed. Output lands in release/.

What ships. electron-builder.yml packs dist/ and desktop/dist/ only: never node_modules (Vite bundles everything) and never the game data a developer's public/ may hold. Windows gets an NSIS installer and a zip, macOS a dmg per architecture, Linux an AppImage and a .deb. The installers register .scm, .scx and .chk. Of Chromium's own UI locales only those of the editor's languages are included (en-US and ko): the editor's translations are in the bundle, but Chromium reports the system's language to the page only when its locale is there. A new language needs its locale added to electronLanguages. Builds are unsigned: Windows shows SmartScreen's "unrecognized app" screen, and macOS refuses the app until the user clears the quarantine attribute.

What the build adds. THIRD-PARTY-NOTICES.txt in dist/ carries the licence of every runtime dependency and compiled-in plugin, generated from package.json. The desktop build and the container image also carry the runtimes the default plugins would otherwise download on first use (none of today's defaults declares one; eudplib's Pyodide and wheel, about 15 MB, did), so such a plugin needs no network there. scripts/bundle-plugin-runtimes.mjs reads a runtime.json from each pinned default's repository and puts the files in dist/plugin-runtime/<plugin>/<version>/. The hosted editor and the web zip carry none.

The app's data folder is %APPDATA%\scm-js on Windows, ~/.config/scm-js on Linux and ~/Library/Application Support/scm-js on macOS. It holds the extracted game data, the window geometry, and startup.log, the milestones of the last launch. SCMJS_TRACE=1 echoes them to the terminal. When a launch "hangs and then opens", that log tells a slow disk, a virus scanner and a slow first paint apart.

From WSL, copy a Windows build to the Windows file system before running it. Started from \\wsl.localhost\…, a network share, it takes minutes to open. WSLENV passes the variable on to the Windows process:

npm run build:desktop -- win --dir          # release/win-unpacked
cp -r release/win-unpacked /mnt/c/Users/<you>/scmjs-test
SCMJS_TRACE=1 WSLENV=SCMJS_TRACE /mnt/c/Users/<you>/scmjs-test/scmJS.exe

In-app updates#

The desktop app checks GitHub releases at startup (a preference, on by default) and through Help ▸ Check for Updates…, using electron-updater over the latest*.yml files the release workflow uploads beside the installers. Finding one raises a toast with a Download button; nothing downloads or installs unasked, and installing goes through the same unsaved-changes check as closing the window. A nightly build follows the nightlies, a stable build follows stable releases, and a preference lets a stable install follow the nightlies.

Windows, the AppImage and the .deb install updates themselves. macOS can see an update but not install it until the app is code-signed, so it offers the release page. A development run and an unpacked Linux folder report that they cannot check. desktop/updater.ts is the main-process side, src/editor/updates.ts the state machine, tests/updates.test.ts the cases.