Back to Squad Table
SOURCE / PINNED RELEASE

Made of little things.

Squad Table

Release
d36201f8ed4f…
Author-recorded commit
7b9bb681d6ba…
License
LICENSE
Author’s source reference
nostr://npub1sj2vketepqw9v6fmge5fdzuj7atj9sv6qv2jss05n2z6rl64lcfqumdsnt/wss%3A%2F%2Fgit.napplet.soy%2F/n-c043b0c0f1d

Archive hash verified: 8c0604b31f00cc5a…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

# Squad Table: design brief

**Job:** an online table for squad-level WWII hex wargames such as Advanced
Squad Leader. It enforces the rules in stages, aiming at full ASL. The turn
sequence is locked, and each phase has an Actions menu. Movement, line of
sight, fire, morale, rally and close combat are automated by a rules engine
(`src/rules/`). Every number it uses is in `src/rules/data.ts`, so players can
check them against their own charts.

## Flows

- **Online table.** A signed-in player creates a table (scenario and side) and
  shares a code. The opponent opens it and takes the other seat. Anyone with the
  code can watch and chat.
- **Async and live in one model.** Every change is an action appended to the
  table's log in the `tables` backend module (`backend/`). Dice are rolled by
  the module, never by the client. Closing the app and coming back days later
  replays the log. When both players are online, a WebRTC room per table sends
  "refresh now" hints and pointer outlines, so moves appear in about a second
  and a half.
- **Hotseat.** The same reducer with the log kept in NAP storage. Use it when the
  host has no backend, or for solo setup.
- **Tools.** Drag stacks (mouse), or pick counters and then Move and tap a
  destination (touch). Flip, rotate facing, restack, relabel, off-board and
  casualty boxes, phase tracker, DR/dr with a reason, LOS thread with a
  hex-by-hex terrain report, undo of your own latest change, and chat.

## Content and IP

Maps, scenarios, counter artwork and all text are original. Unit stat lines are
plain editable labels, and players can relabel any counter to match their unit
tables. The app avoids MMP's trademarks and copies no rulebook text.

## Visual direction: "war-room table"

- **Character:** a dark felt table under a lamp with a printed paper map on
  it. It suits a slow, deliberate game played over a physical-feeling surface.
- **Main surface:** cream paper map with muted cartography. Woods are clustered
  tree crowns, grain is hatched ochre, orchards are dotted rows, and buildings
  are rooftops (brown wood, grey hatched stone). Hills use tan level tints with
  brown contour strokes. Roads are pale curved bands, hedges dark green and
  walls dashed grey hexside strokes. Counters are chunky square chits with a
  drop shadow, nation-coloured faces (field grey, olive, khaki), NATO-style
  role boxes and bold stat lines. A broken side shows a dark-red sash. Markers
  use tone colours: fire red, movement yellow, state cream, terrain charcoal.
- **Interface:** olive field-kit panels, brass for selection, active state and
  primary actions, condensed stencil-like system fonts, and oxblood dice
  buttons. Selection is a brass hex outline, the last move a dashed arrow in
  the mover's colour, and LOS a yellow thread with highlighted hexes.
- **Theme policy:** app-owned. Host theme data is not applied, because counter
  and nation colours carry game meaning.
- **Sizes:** desktop has a side panel. Below 760 px the panel becomes a bottom
  sheet and a stack strip appears. Below 260×200 a notice asks for a larger
  frame and shows the turn, phase and last roll. Touch drags pan; mouse drags
  move stacks.
- **Cover:** `preview.png` is a mid-game hotseat state with an LOS thread and
  dice (1200×750).

## Domains

All optional, with fallbacks:

| Domain | Use | Fallback when absent |
| --- | --- | --- |
| `cvm` | online tables | hotseat only |
| `identity` | seat ownership | watching only |
| `webrtc` | live hints | polling every 4 s |
| `storage` | table list and hotseat saves | not remembered |
| `config` | map coordinates, pointer sharing, auto-live | defaults |

`relay` and `outbox` are not used.

## Known gaps

- **Concealment is not hidden from the opponent.** "?" markers are visible
  counters. The log is public to anyone with the code, so hidden setup needs a
  future design.
- **Public online tables need provider admission.** The backend provider uses
  an allowlist, so this creator must be admitted, then run
  `soyli backend deploy backend/backend.json`. Local preview works without it.
- **Touch is not tested on real phones.** Touch pan, pinch and tap were not
  exercised on a real device, only through layout screenshots at phone sizes.