Desktop launcher for NiChart — wraps the NiChart React SPA and optionally manages a local NiChart FastAPI server via Docker.
Built with Electron 31, electron-vite 2, and React 18.
This repository is currently a work in progress. You are free to use it with the understanding that it will change rapidly and things may break. When it is ready for wider distribution, this README will start with more concise instructions to download, install and run it.
┌──────────────────────────────────────────────────────┐
│ Electron launcher (this repo) │
│ ┌────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ Setup │ │ Dashboard │ │ NiChart │ │
│ │ wizard │ │ (server ctrl)│ │ window │ │
│ └────────────┘ └──────────────┘ └──────┬──────┘ │
│ │ │
│ Local HTTP server (loopback) ←──────────┘ │
│ serves resources/renderer/ (NiChart SPA) │
└──────────────────────────────────────────────────────┘
│ API calls (http)
▼
Cloud / Remote server OR local Docker container
Three operating modes are configured on first launch:
| Mode | What it does |
|---|---|
| Cloud | Connects to the hosted NiChart service (no local setup) |
| Remote | Points at a user-provided API server URL; optional SSH tunnel for HPC clusters |
| Local | Starts a NiChart API Docker container on this machine |
| Tool | Version | Notes |
|---|---|---|
| Node.js | ≥ 20 (v24 confirmed) | Use nvm or nvm-windows |
| yarn | 1.x | npm install -g yarn — npm can also be used |
| Git | any | |
| Docker Desktop | any | Required only for Local mode |
Windows + VS Code note: VS Code sets
ELECTRON_RUN_AS_NODE=1on all child processes. Runningelectron-vitedirectly with this variable set breaks Electron. Theyarn devscript routes throughscripts/dev.mjs, which strips the variable before spawning electron-vite. Always useyarn dev, notnpx electron-vite dev.
# 1. Clone and install
git clone <repo-url>
cd NiChart_Electron
yarn install
# 2. Start the dev server
yarn devThis builds the main process and preload scripts, then starts:
- A Vite HMR dev server for the launcher UI (React)
- The Electron window pointed at that dev server
Hot reload: The launcher UI (Setup wizard, Dashboard) hot-reloads on save.
Changes to src/main/ or src/preload/ require restarting (Ctrl+C, then yarn dev again).
resources/placeholder/ contains a minimal fallback page served when the NiChart
window is opened in dev mode. This lets you test the launcher flow without a full
SPA build.
Build the NiChart React SPA and copy its dist/ output into resources/renderer/:
# In the NiChart SPA repo:
yarn build
# Then copy the output:
cp -r dist/* /path/to/NiChart_Electron/resources/renderer/The SPA will be picked up automatically on next launch. In dev mode the launcher
falls back to the placeholder; change getNiChartRendererBase() in
src/main/index.ts temporarily if you want to test with the real SPA in dev.
The Electron preload (src/preload/nichart.ts) injects the API URL before any SPA
JavaScript runs:
// window.__NICHART_CONFIG__ is set before <script> tags execute
const apiUrl =
(window as any).__NICHART_CONFIG__?.apiUrl ??
import.meta.env.VITE_API_URL ?? // fallback for standalone dev
'http://localhost:8000'Add this to the SPA's API config module. No build-time changes needed when embedding in Electron.
On first launch the Setup wizard guides you through three steps:
- Mode — Cloud / Remote / Local
- Configure — varies by mode:
- Cloud: accept the data-privacy consent
- Remote: enter the API server URL; optionally enable SSH tunnel (host, port, user, key)
- Local: choose a backend (Docker on Windows; Docker/Singularity/Apptainer/SLURM on Linux/Mac), pass prerequisites check, set data directory and port
- Done — settings are saved to the OS user-data directory via
electron-store
Click Settings in the Dashboard header at any time to re-run the wizard.
When the remote API server is not directly reachable (e.g. an HPC login node), the launcher can forward a local port over SSH:
ssh -N -L 127.0.0.1:<localPort>:<remoteApiHost>:<remoteApiPort> <user>@<sshHost>
The tunnel uses your system ssh binary and existing credentials (ssh-agent or
~/.ssh/config). Keys protected by a passphrase require ssh-agent to be running.
Before building, place the NiChart SPA build in resources/renderer/ and set the
production API URL:
// src/shared/constants.ts
export const CLOUD_API_URL = 'https://nichart.cbica.upenn.edu' // real URLAlso confirm the Docker image name in resources/docker/docker-compose.yml.
# Compile TypeScript (output → out/)
yarn build
# Package for the current platform
yarn dist
# Platform-specific
yarn dist:win # Windows → dist/NiChart-Setup-<version>.exe (NSIS)
yarn dist:mac # macOS → dist/NiChart-<version>.dmg
yarn dist:linux # Linux → dist/NiChart-<version>.AppImageCross-platform builds require the target OS or a CI environment. The packaged installer embeds the Electron binary, compiled JS, and the SPA — it does not embed Docker images or the API server.
- Set
CLOUD_API_URLinsrc/shared/constants.ts - Set real Docker image name in
resources/docker/docker-compose.yml - Copy the NiChart SPA build into
resources/renderer/ - Bump
versioninpackage.json -
yarn build && yarn dist:<platform> - Test the installer on a clean machine (Docker Desktop installed, no dev tools)
- Publish installer to GitHub Releases or internal channel
- Push the latest API image to your Docker registry
See DISTRIBUTION.md for detailed notes on the three-layer update
model (Electron app / React SPA / Docker image) and the electron-updater auto-update setup.
src/
main/
index.ts # App lifecycle, SPA HTTP server, window management
ipc.ts # All IPC handlers (server, tunnel, config, prereqs)
server.ts # ServerManager — docker compose start/stop/health
ssh-tunnel.ts # SshTunnelManager — SSH port-forward lifecycle
config.ts # electron-store wrapper (AppConfig persistence)
prerequisites.ts # System checks (docker, singularity, etc.)
preload/
index.ts # contextBridge API for the launcher UI
nichart.ts # Injects window.__NICHART_CONFIG__ into the NiChart window
renderer/src/
pages/
Setup.tsx # First-run wizard (mode → consent/backend/prereqs → configure)
Dashboard.tsx # Main screen (server/tunnel controls, Open NiChart button)
App.tsx # Root — switches between Setup and Dashboard on firstRun flag
shared/
types.ts # AppConfig, ServerStatus, TunnelStatus, etc.
constants.ts # CLOUD_API_URL
resources/
docker/
docker-compose.yml # Bundled compose file (uses Docker Hub image)
placeholder/ # Dev fallback page for the NiChart window
renderer/ # ← NiChart SPA build goes here (git-ignored)
scripts/
dev.mjs # Strips ELECTRON_RUN_AS_NODE before spawning electron-vite
FastAPI-config/
.env.local.template # Reference .env template for the API container
ELECTRON_RUN_AS_NODE / blank window on yarn dev
Always launch with yarn dev, not directly via npx electron-vite dev or
node_modules/.bin/electron-vite dev. See the note under Prerequisites.
ERR_PACKAGE_PATH_NOT_EXPORTED from electron-vite
Occurs when something tries to require.resolve('electron-vite/bin/...').
scripts/dev.mjs resolves the binary path directly — do not change that approach.
Docker containers keep stopping / health check fails
Check yarn dev terminal for docker compose output. Ensure Docker Desktop is
running and WSL2 is available (Windows). The logs panel in the Dashboard also
shows compose stdout/stderr live.
SSH tunnel not connecting
- Check that
ssh <user>@<host>works in a terminal without a passphrase prompt (add the key to ssh-agent first:ssh-add ~/.ssh/id_ed25519) - The SSH log panel in the Dashboard (click the tunnel status chip) shows raw stderr from the SSH process
StrictHostKeyChecking=accept-newis set, so the first connection will accept and cache the host key automatically