# Packaging InsightUX as a downloadable installer Turns the cloned-repo-plus-venv workflow into a single Windows installer (`InsightUX-Setup-.exe`) that bundles Python, torch, mediapipe, onnxruntime, opencv, and pywebview — end users need nothing pre-installed. ## One-time setup (per machine you build from) 1. In the project venv: `pip install pyinstaller` 2. Install [Inno Setup 6](https://jrsoftware.org/isdl.php) (free) — provides `ISCC.exe`, the command-line compiler `installer.iss` needs. Not required just to build the app itself, only to build the final installer. 3. (Optional but recommended) Download Microsoft's WebView2 bootstrapper (`MicrosoftEdgeWebview2Setup.exe`) from [Microsoft's evergreen bootstrapper page](https://developer.microsoft.com/microsoft-edge/webview2/) and place it in this `packaging/` folder before compiling the installer — `installer.iss` bundles and silently runs it if present, as a safety net for machines missing the WebView2 Runtime (usually already present on modern Windows 10/11, but not guaranteed on older/locked-down machines). If you skip this, the installer still builds fine, just without that safety net. ## Build From the repo root: ```powershell pyinstaller packaging\InsightUX.spec --noconfirm ``` Output: `dist\InsightUX\` (a folder — `InsightUX.exe` + `_internal\`). Run `dist\InsightUX\InsightUX.exe` directly first to confirm it actually starts before building the installer — much faster to debug a missing-DLL/missing- data-file error at this stage than after wrapping it in an installer. **Expect to iterate on the first build.** mediapipe, torch, and onnxruntime all ship native binaries and non-`.py` data files that PyInstaller's default hooks don't always fully catch — see the comments at the top of `InsightUX.spec` for what's already collected and where to look if the frozen exe fails to start. Then build the installer: ```powershell iscc packaging\installer.iss ``` Output: `packaging\dist_installer\InsightUX-Setup-.exe`. ## Release checklist Every time you ship a code change as an update: 1. Bump `VERSION` in [browser_session.py](../browser_session.py). 2. Bump `AppVersion`/`MyAppVersion` in [installer.iss](installer.iss) to match. 3. Rebuild (`pyinstaller ...` then `iscc ...`). 4. Update [version.json](../version.json) at the repo root — `"latest"` to the new version, `"url"` to the new installer's HF download URL. 5. `git lfs` tracks `*.exe` already (see `.gitattributes`) — add and push the new installer + `version.json` to the `origin` (Hugging Face) remote. That's it — nothing about *publishing* a release is automated. What's automated is the *checking*: every running copy of InsightUX pings `version.json` once at startup (`check_for_update()` in browser_session.py) and shows a clickable "Update available" pill in the toolbar if `"latest"` is newer than its own `VERSION`. Clicking it opens `"url"` in the user's default browser — still a manual download+run, just automated the "is there something new" question. No internet, or any failure fetching the file, is silent (no banner, never an error). ## Why these choices - **Onedir, not onefile** — onefile re-extracts the whole ~1.5-2.5GB bundle to a temp directory on every launch. With torch/mediapipe already this heavy, that's a genuinely bad startup-time hit. Onedir just runs directly from the installed folder. - **Per-user install (`{localappdata}`), not Program Files** — no admin/UAC prompt, and avoids Program Files' write-permission restrictions for `calibration.pkl`/`sessions/`, which the running app writes directly into its own folder (see `RESOURCE_DIR`/`DATA_DIR` in browser_session.py). - **Fixed `AppId` GUID in installer.iss** — this is what makes a new installer upgrade the existing install in place instead of creating a second copy. Never regenerate it. - **torch is bundled** (not excluded) — its fine-tuning step measurably improves calibration accuracy (169-204px mean error vs. 275px without it, per calibrate.py's own numbers). Confirmed with the project owner as worth the larger download.