Repository navigation
Create shared PWA package for OpenPhysics sims #6
Description
Summary
Most OpenPhysics sims duplicate the same PWA setup: Vite PWA config, manifest defaults, security headers, icon generation, and icon-related dependencies. Create a shared npm package, likely @openphysics/sim-pwa, to centralize that setup while leaving each sim responsible only for its app-specific metadata and source SVG icon.
This should reduce per-sim boilerplate, make future PWA/security header changes easier to roll out, and avoid committing generated icon assets where possible.
Current Repetition
Representative sims currently duplicate:
vite-plugin-pwasetup invite.config.tssecurityHeadersfor dev server and preview server- manifest defaults:
display: "standalone"orientation: "landscape"- standard icon entries
includeAssets: ["favicon.ico", "icons/apple-touch-icon.png"]
- Workbox defaults:
maximumFileSizeToCacheInBytes: 12 * 1024 * 1024- common glob patterns for JS/CSS/HTML/SVG/PNG/WOFF2
- icon-generation script based on
sharpandpng-to-ico - direct dev dependencies on
sharp,png-to-ico,tsx, andvite-plugin-pwa
Most sims already keep the real source asset at:
public/icons/icon.svg
The generated assets can be recreated from that SVG:
public/icons/apple-touch-icon.png
public/icons/icon-192.png
public/icons/icon-512.png
public/favicon.ico
Proposed Package
Create a package such as @openphysics/sim-pwa.
The package should provide:
openPhysicsPWA(options)— wrapsVitePWA(...)with shared OpenPhysics defaults.securityHeaders— shared COOP/COEP/CSP headers used by Vite dev and preview.- optional
openPhysicsSimConfig(options)— a convenience helper that returns common Vite config. openphysics-pwa icons— CLI command that generates PNG/ICO assets from the source SVG.
Safe Boundary
This package should stay at the Vite/PWA/build tooling layer.
It should affect:
vite.config.ts- generated PWA manifest config
- Workbox/service worker config
- icon generation
- dev/preview security headers
It should not affect:
src/main.tssrc/brand.tssrc/splash.tssrc/assert.tssrc/init.ts- SceneryStack runtime startup order
SceneryStack Bootstrap Constraint
SceneryStack setup is sensitive to import order. Each sim must keep this import as the first import in src/main.ts:
import "./brand.js";That preserves the existing bootstrap chain:
brand.ts -> splash.ts -> assert.ts -> init.ts
The shared PWA package should not try to abstract or reorder this startup path.
Package API Sketch
Example target usage in a sim:
import { defineConfig } from "vite";
import { openPhysicsPWA, securityHeaders } from "@openphysics/sim-pwa";
export default defineConfig({
base: "./",
build: {
target: "es2024",
},
server: {
headers: securityHeaders,
},
preview: {
headers: securityHeaders,
},
plugins: [
openPhysicsPWA({
name: "Doppler Effect",
shortName: "Doppler",
description: "An interactive simulation of the Doppler Effect using SceneryStack.",
themeColor: "#2575ba",
}),
],
});For sims with additional offline assets, allow extensions:
openPhysicsPWA({
name: "Wave Composer",
shortName: "WaveComposer",
description: "A real-time voice-analysis simulation.",
themeColor: "#1a1a2e",
extraGlobPatterns: ["**/*.{wav,mp3,ogg}"],
});Or allow a Workbox override:
openPhysicsPWA({
name: "Wave Composer",
shortName: "WaveComposer",
description: "A real-time voice-analysis simulation.",
themeColor: "#1a1a2e",
workbox: {
globPatterns: ["**/*.{js,css,html,svg,png,woff2,wav,mp3,ogg}"],
},
});Manifest Defaults
The package should generate this standard manifest shape:
{
name,
short_name: shortName,
description,
theme_color: themeColor,
background_color: backgroundColor ?? "#000000",
display: "standalone",
orientation: "landscape",
icons: [
{
src: "icons/icon-192.png",
sizes: "192x192",
type: "image/png",
},
{
src: "icons/icon-512.png",
sizes: "512x512",
type: "image/png",
},
{
src: "icons/icon.svg",
sizes: "any",
type: "image/svg+xml",
purpose: "maskable",
},
],
}Icon CLI
The package should expose a bin command:
{
"bin": {
"openphysics-pwa": "./dist/cli.js"
}
}CLI usage:
openphysics-pwa iconsDefault behavior:
- read
public/icons/icon.svg - write
public/icons/apple-touch-icon.pngat 180x180 - write
public/icons/icon-192.pngat 192x192 - write
public/icons/icon-512.pngat 512x512 - write
public/favicon.icowith 16/32/48/64 PNG layers
Potential options:
openphysics-pwa icons --source public/icons/icon.svg
openphysics-pwa icons --public-dir public
openphysics-pwa icons --check--check could fail if generated assets are missing or stale, useful for CI later.
NPM Script Setup
Each sim can replace its local generator script with package-provided commands.
Minimal setup:
{
"scripts": {
"icons": "openphysics-pwa icons",
"build": "npm run icons && tsc && vite build"
},
"devDependencies": {
"@openphysics/sim-pwa": "^0.1.0"
}
}Keep existing dev/start behavior lightweight:
{
"scripts": {
"start": "vite",
"dev": "vite",
"preview": "vite preview"
}
}Recommended full script set after migration:
{
"scripts": {
"start": "vite",
"dev": "vite",
"build": "npm run icons && tsc && vite build",
"preview": "vite preview",
"lint": "biome check .",
"format": "biome format --write .",
"fix": "biome check --write .",
"check": "tsc --noEmit && tsc -p tsconfig.scripts.json --noEmit",
"icons": "openphysics-pwa icons",
"clean": "rm -rf dist",
"prepare": "git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git config core.hooksPath .githooks || true"
}
}If generated icons are not committed, build should run npm run icons first. npm start can remain vite because dev mode can tolerate generating icons manually when needed.
Alternative if dev should always have fresh icons:
{
"scripts": {
"prestart": "npm run icons",
"start": "vite",
"predev": "npm run icons",
"dev": "vite"
}
}However, the simpler default should probably be to generate icons during build, not every dev server start.
Dependency Strategy
The shared package can own:
sharppng-to-icovite-plugin-pwa
Consider using peer dependencies for tooling that should remain aligned across sims:
vitetypescript
This avoids accidentally pulling multiple Vite versions into each sim.
After migration, individual sims should no longer need direct dependencies on:
sharppng-to-ico- possibly
vite-plugin-pwa, depending on package design
They may still need tsx if they have other TypeScript scripts. If generate-icons.ts is the only use, tsx can potentially be removed too.
Migration Plan
- Create
@openphysics/sim-pwapackage. - Implement exported Vite helpers and the
openphysics-pwa iconsCLI. - Migrate
TemplateSingleSimfirst as the reference implementation. - Validate:
npm run iconsnpm run checknpm run buildnpm start
- Migrate one representative real sim, such as
DopplerEffect. - Migrate a special-case sim such as
WaveComposerto validate custom Workbox globs. - Batch migrate the remaining sims.
- Remove duplicated
scripts/generate-icons.tsfiles. - Remove redundant direct dependencies from each sim.
- Update docs in
TemplateSingleSimand shared org guidance.
Acceptance Criteria
- A shared package exists and is usable from a sim repo.
TemplateSingleSimuses the shared package for PWA config and icon generation.- At least one real sim builds and starts after migration.
- SceneryStack bootstrap imports remain unchanged.
- Generated PNG/ICO assets can be recreated from
public/icons/icon.svg. - The package supports sims with extra Workbox asset types.
Activity