- TypeScript 86.7%
- HTML 8.3%
- CSS 2.9%
- JavaScript 2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| android | ||
| docs | ||
| e2e | ||
| src | ||
| vendor | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| .stylelintignore | ||
| capacitor.config.ts | ||
| CLAUDE.md | ||
| eslint.config.js | ||
| healie-battle-pixi.html | ||
| healie-ui-v05.html | ||
| index.html | ||
| LICENSES.md | ||
| package.json | ||
| playwright.config.ts | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| PRODUCT.md | ||
| README.md | ||
| stylelint.config.js | ||
| TODO.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
| wrangler.jsonc | ||
Healie
An endless auto-battler healer RPG for Android. You control only the healer of a fantasy party — the one role that can never go AFK or the whole party dies.
The full game design lives in PRODUCT.md; the phased build plan in
TODO.md; the engineering contract (stack, architecture, guardrails) in
CLAUDE.md.
Tech stack
TypeScript (strict) · Vite · PixiJS v8 (WebGPU → WebGL2) · Lottie · Capacitor (Android) · deterministic fixed-timestep sim. Package manager: pnpm. Runtime: Node 24+.
Architecture
One-way data flow across onion-style layers under src/:
sim/— pure game logic. No Pixi, no DOM, nowindow, noMath.random. Deterministic, unit-testable, seeded RNG. (The ESLint config mechanically enforces this purity.)render/— PixiJS scene graph. Reads sim state, draws the board + VFX.ui/— DOM / HTML + CSS + Lottie HUD and menus.data/— tuning curves & content tables (the balance knobs).app/— bootstrap, asset loading, scene routing.
Build / Run / Deploy
| Command | What it does |
|---|---|
pnpm install |
install deps (pnpm only) |
pnpm dev |
Vite dev server — play in browser |
pnpm build |
type-check (tsc --noEmit) then production web build → dist/ |
pnpm test |
unit tests (sim/ must pass headless) |
pnpm test:watch |
unit tests in watch mode |
pnpm coverage |
unit tests with v8 coverage report |
pnpm lint |
ESLint + Prettier (check) + Stylelint |
pnpm format |
Prettier write |
pnpm verify |
lint + test + build (the green-gate) |
Web demo (Cloudflare Pages)
Live at https://healie.pages.dev (Cloudflare Pages project healie, production branch main).
The current build renders the static A·Stone battle board; live auto-battle + smart-cast input land in later commits. The deploy auto-updates as
mainadvances.
Auto-deploy (CI): every push to main runs .forgejo/workflows/deploy.yml on the self-hosted
Forgejo Actions runner (node:24-bookworm): pnpm install --frozen-lockfile → pnpm build →
pnpm run deploy. Requires the repo Forgejo Actions secrets CF_API_TOKEN and CF_ACCOUNT_ID
(both already set).
Manual deploy (from a clean local build):
pnpm build # -> dist/
CLOUDFLARE_API_TOKEN=… CLOUDFLARE_ACCOUNT_ID=… pnpm run deploy
# pnpm run deploy == wrangler pages deploy --branch=main
# target (project name = healie, output dir = dist) comes from wrangler.jsonc
Cloudflare credentials live in ~/.claude/secrets/cloudflare.env (CF_API_TOKEN, CF_ACCOUNT_ID);
map them to the CLOUDFLARE_* env vars wrangler reads. Rollback: wrangler pages deployment list --project-name=healie then redeploy a prior good build, or roll back from the Cloudflare Pages
dashboard.
Android (Capacitor → Google Play)
V1 wraps the exact same web build into a native Android app with Capacitor — a thin WebView
shell, NON-MONETIZED (NO AdMob / Play Billing / analytics plugins; those are the Post-1.0
backlog). Config lives in capacitor.config.ts:
- appId:
eu.treefolk.healie· appName:Healie· webDir:dist - near-black launch/background
#0d0d0f(no white flash),androidScheme: https, no cleartext.
The android/ native project is committed; its build outputs + the synced web-asset copy are
gitignored (and signing keys are never committed).
Prerequisites for the native build (the developer's machine, not CI): a JDK 21+, Android Studio
with the Android SDK, and ANDROID_HOME set. The scaffold and asset sync below need none of that;
only the final assemble/sign/upload (done in Android Studio) does.
# One-time (already done): scaffold the native project
npx cap add android
# Each release: build the web bundle, copy it into android/, open Android Studio
pnpm cap:sync # == pnpm build && cap sync android (copies dist/ into the native project)
pnpm cap:open # == cap open android (Android Studio)
In Android Studio (the developer's manual steps):
- Build → Generate Signed Bundle / APK → Android App Bundle (.aab).
- Create / select a signing keystore (keep it OUTSIDE the repo —
*.keystore/*.jksare gitignored as a safety net; store it in your password manager / secure backup). - Build the release bundle.
- Upload the
.aabto the Google Play Console as a FREE app (slug/titlehealie), with no monetization disclosures (no ads, no IAP in V1).
If
npx cap add androidever needs re-running on a machine without the Android SDK, it still scaffolds the project (config + Gradle wrapper); onlycap:open+ the signed build require the full Android toolchain.
Performance / quality (mid-range Android)
Targets 60fps on mid-range Android: WebGPU with a clean WebGL2 fallback, pooled + hard-capped
particles, no per-frame redraws of static chrome, and no stencil masks in the hot path (portraits
are texture-cropped, cooldown sweeps are geometry). The Settings → Graphics toggles drive a real
auto-degrade (knobs in src/data/perf.ts, applied by
src/app/perf-controller.ts):
- Low-end mode → fewer particles (cap + spray scale), soft glows off, DPR capped at 1.5, FPS capped at 60, antialiasing off.
- Reduce motion → disables screen-shake + struck jitter and further thins particle churn (heal glow + floating numbers stay fully readable), on top of the global no-animation CSS class.