# armada/ui — the bridge

The instruments PLN actually looks at. One Vite project, several pages, one design
language (**the Ship's Bridge** — `../PRODUCT.md` for the register, `../DESIGN.md` for
the visuals, managed with the `impeccable` skill from `armada/`).

Stack: Vite + React 19 + TS + Tailwind 4 + shadcn conventions, Geist / Geist Mono.
Node ≥ 18 — `nvm use 22`.

## The pages

| URL | Name | What it answers |
|---|---|---|
| `/` | Take Judge | A/B two masters of the same take — which one ships |
| `/judge.html` | Set Judge | one pass down a whole gig, a verdict + comment per track → `decisions.json` |
| `/bounds.html` | Boundary Lab | where exactly does one track become the next |
| `/sextant.html` | Le Sextant | distribution cost/revenue simulation |
| `/kits.html` | **La Cale** | is this sample kit any good — audition, loop, rack up, export Tidal |

Each page is its own Rollup input in `vite.config.ts` and its own `src/<page>.tsx`
entry; shared bits live in `src/components/` and `src/lib/`.

## Running it

```bash
nvm use 22
npm ci
npm run dev          # http://localhost:5173 — also on the LAN (server.host)
npm run build        # tsc -b && vite build → dist/
npm test             # vitest
npm run lint
```

For phone audition and anything that has to work outside dev, serve the build:

```bash
python3 ../serve.py --dir dist --port 8792
```

`serve.py` is Range-capable, which stdlib `http.server` is not — without it, seeking a
90 MB master in `<audio>` does not work at all.

## Audio, and why it is mounted rather than bundled

The audio these pages play is gigabytes and lives outside the repo. `audio-mounts.json`
maps `/audio/<prefix>/...` to real directories, and it is read by **both**
`vite.config.ts` (dev) and `../serve.py` (built), so a URL that works in one works in
the other. A UI that only works under `npm run dev` is a demo, not a tool.

Longest prefix wins; `""` is the fallback mount. Add a prefix rather than a hardcoded
path — the masters move between gigs and the URLs should not.

## Data

Pages fetch generated JSON from `public/`. Some of it is committed (the gig-scoped
`judge-*.json`, `bounds-*.json`); `kits.json` is not — it is a 1.2 MB index of audio
that lives outside the repo, regenerated by

```bash
cd ../../tools/foundry && python3 kitindex.py --all
```

which writes `public/` **and** `dist/` if a build is present, because `npm run build`
copies `public/` exactly once and a page served from `dist/` would otherwise show an
index from before the last cut.
