18 Commits
Author SHA1 Message Date
slaguru666andClaude Opus 4.8 d430ee9574 polish: bolder placeholder art so thumbnails read at small size
Brighter backgrounds, thicker strokes, and large tonal regions (backlight
pool, lit doorway, face fill, water band) so the seed art is legible as
thumbnails instead of near-black cards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 13:18:52 +01:00
slaguru666andClaude Opus 4.8 3bd85dd9c7 fix: generate the missing seed art images (broken thumbnails)
The art manifest referenced /art/*.png files that never existed; the SPA
fallback returned index.html for them, so the library thumbnails rendered
as broken images. Generate 5 atmospheric B&W placeholder scenes
(tools/gen-art-placeholders.mjs, npm run gen:art) into public/art/.
tests/tools/art-assets.test.js ties every manifest entry to a real PNG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 13:14:33 +01:00
slaguru666andClaude Opus 4.8 529911dc12 fix: service worker network-first for the page so deploys reach devices
The cache-first SW never revalidated the HTML, so a device that loaded the
app once kept serving the old bundle forever (new deploys never landed).
Now: network-first for navigations/HTML (fresh on every online load, cached
shell as offline fallback), cache-first only for immutable hashed assets.
Bumped CACHE to director-v2 so the stale v1 cache is purged on activate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 11:42:55 +01:00
slaguru666andClaude Opus 4.8 0ec09290ea fix: iPad layout — clamp rail labels, fill the stage with a run-board
The rail was a fixed 64px bar cramming 3 info cells against 9 chips, so
long beat/trigger labels wrapped and clipped ('bled'), and trays were thin
top strips leaving the iPad screen mostly empty.

- Rail: min-height + 2-line clamp on the beat/trigger labels (no bleed),
  nowrap 'Reached it', slimmer chips, tabular clock.
- Trays now fill the stage as solid panels (inset:0) instead of a strip.
- New <run-board>: the whole run at a glance behind the trays — every beat
  with target time, done beats stamped/dimmed, next beat highlighted, hard
  triggers + drift. Fills the previously-empty stage with the most useful
  GM view.
- @media (min-width:1024px/1366px): larger type, roomier rows, more art
  columns — uses the tablet space.

tests/components/run-board.test.js. 219 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 11:41:03 +01:00
slaguru666andClaude Opus 4.8 1d6e1f4b62 chore: add proxy-network compose for reverse-proxy deploys
docker-compose.proxy.yml runs the container on a shared external 'proxy'
network with no published ports, for hosting behind an existing edge
proxy (Caddy/Traefik/etc.) that routes <sub>.domain -> the-director:80.
This is how it's deployed at apps.timevans.uk.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 11:21:16 +01:00
slaguru666andClaude Opus 4.8 89b89572b5 chore: containerized static deploy (Docker + Caddy) for self-hosting
Serve the built PWA behind your own domain: multi-stage Dockerfile builds
dist/ and serves it with Caddy on :80 (TLS terminated at your Cloudflare
edge). Caddyfile sets no-cache on sw.js/index/manifest and immutable on
hashed assets; SPA fallback to index.html. docker-compose maps :8087.

Verified locally: image builds, container serves 200s with correct cache
headers and SPA fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 11:11:37 +01:00
slaguru666andClaude Opus 4.8 8f807d50b5 feat: placeholder app icons (clock-with-target motif)
Generate the manifest/PWA + native-wrapper icons that were referenced but
missing (icon-192/512 were 404). A reproducible pure-Node generator
(tools/gen-icons.mjs, npm run gen:icons) draws a clock ring with amber
hands + a green target dot in the app palette — no image deps, hand-rolled
PNG encoder with analytic anti-aliasing.

- public/icons/icon-192, -512, -180 (apple-touch); linked in index.html.
- resources/icon.png (1024) + splash.png (2732) for @capacitor/assets.
- tests/tools/icons.test.js ties every manifest icon to a real PNG of the
  declared size. 213 tests pass; icons now serve 200.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 09:29:57 +01:00
slaguru666andClaude Opus 4.8 e79f643409 feat: native iPad wrapper — Capacitor iOS scaffold
Wrap the PWA in a thin native iOS shell (Capacitor 8):
- capacitor.config.json (webDir dist, appId uk.timevans.thedirector,
  iOS safe-area contentInset + theme background).
- @capacitor/{core,cli,ios} as devDependencies (web app keeps zero
  runtime deps); ios:add / ios:sync / ios:open / ios npm scripts.
- main.js skips service-worker registration under Capacitor
  (window.Capacitor.isNativePlatform) — assets are already local.
- iOS <meta> hints in index.html; .gitignore for native build output.
- docs/native-ipad-wrapper.md: full Xcode + CocoaPods build guide,
  incl. the keep-awake plugin note for the wake toggle in WKWebView.

The iOS project generation/build (npx cap add ios → Xcode) needs a Mac
with full Xcode + CocoaPods, which this env lacks; it is documented for
the user to run. tests/tools/capacitor-config.test.js guards the config.
211 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:55:20 +01:00
slaguru666andClaude Opus 4.8 028dcb5e4e feat: online art Generate — configured endpoint, cached into the library
Add the art tray's online Generate: prompt → a GM-configured image-gen
endpoint → new pencil image cached into a persisted, searchable library
(offline thereafter). No endpoint or key is hardcoded — the GM pastes
their gen URL (⚙ field, persisted). Degrades gracefully: unconfigured or
offline → Generate disabled, library search fully works; network is never
load-bearing.

- src/art/generate.js: createArtGenerator (injectable fetch/navigator).
- src/art/library.js: pure addGenerated (dedupe + cap 24).
- art-tray: generate row, endpoint config, generated cache (gmd.art.*).

Tests: generate.test.js, library.test.js, art-tray-generate.test.js;
existing art-tray tests unchanged. 206 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:48:05 +01:00
slaguru666andClaude Opus 4.8 7c6ee3eecc feat: markdown → scenario-data generator (tools/)
Add a deterministic authoring tool that turns a structured scenario
markdown file into a validated scenario-data module:
- tools/scenario-md.js: pure parseScenarioMarkdown + emitScenarioModule
  (frontmatter → meta; ## Timeline/## Clues/## Cast lists → the arrays;
  auto-slugged ids; validated via validateScenario before emit).
- tools/md-to-scenario.mjs: CLI (npm run gen:scenario), file or stdout.
- tools/example-scenario.md: documented format example.

Parses a defined format, not freeform con prose. tools/ is dev-only and
never bundled. tests/tools/scenario-md.test.js round-trips + validates.
187 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:33:28 +01:00
slaguru666andClaude Opus 4.8 6d529c9799 feat: parking-lot note + wake-lock tray tools
Add the last two design-§5 tray tools:
- <parking-lot>: one-tap capture of a thread to return to, stamped with
  the session time, persisted per-scenario (parkingLot store key), 📝 chip.
- src/core/wake-lock.js: injectable Screen Wake Lock controller (keeps the
  tablet awake, re-acquires on foreground, graceful no-op where the API is
  absent). Toggled by a 💡 rail chip that lights when active.

Tests: parking-lot.test.js, wake-lock.test.js, gm-shell-parking.test.js,
gm-shell-wake.test.js. 179 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:15:14 +01:00
slaguru666andClaude Opus 4.8 73a7ea72ad feat: break timer — pause the session clock so breaks don't poison drift
Add <break-timer> tray: pick a duration, Start break pauses the session
clock (reusing clock.pause/resume), End break resumes it — so meal/comfort
break time is excluded from elapsed and drift stays honest. The shell owns
pause/resume + persistence (break store key); the rail shows a on-break
badge and gains a coffee chip. fmtCountdown (M:SS) added to format.js.

tests/components/break-timer.test.js + gm-shell-break.test.js (incl. the
30-play/20-break/10-play drift-honesty case) + fmtCountdown. 159 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:00:17 +01:00
slaguru666andClaude Opus 4.8 49e9dcda46 feat: dedicated BRP dice pack; Day One uses it instead of coc-d100
Add brp-d100 rule-pack (Basic Roleplaying roll-under): fumble on 100,
critical <= skill/20, special <= skill/5, success <= skill — bands
transcribed from the Continuum Day One console's verdict() logic.
Register it and switch day-one meta.system from coc-d100 to brp-d100.

tests/dice/brp-d100.test.js covers the ladder, low-skill rounding, and
the fumble-only-on-100 edge. 147 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:48:28 +01:00
slaguru666andClaude Opus 4.8 9d0e6c7ec4 feat: cast tray — surface scenario.cast rosters in a session
Add <cast-tray>: renders scenario.cast (name + one-line note) with each
NPC secret behind a tap-to-reveal so spoilers stay off-screen at the
table. Wired into the shell as the 'cast' tool with a 👥 rail chip;
reveal state is ephemeral component state. Now the 57 ported NPCs are
visible in a running session.

tests/components/cast-tray.test.js + gm-shell-cast.test.js. 141 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:34:38 +01:00
slaguru666andClaude Opus 4.8 4096291b1d feat: port all six scenarios' NPC cast rosters into the data model
Populate cast[] (kind:'npc') for every Continuum scenario, transcribed
from the gm-utility NPC sources — normalising the two schemas (console
{id,name,ess,sec} and module {name,tag,secret}) into
{ id, name, kind, note, secret? }. 57 NPCs total. Pregens/PCs excluded
(separate character-sheet system).

No cast UI consumer yet — the design reserves scenario.cast for a future
cast dashboard, so this is a data-model port guarded by
tests/scenarios/cast.test.js (shape + unique ids). 131 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:18:52 +01:00
slaguru666andClaude Opus 4.8 ce91b4ce98 feat: port all six scenarios' clue trails into the data model
Populate clues[] for every Continuum scenario (afterimage, day-one,
vain-crown, silvery-moon, chopper, princes-bride), transcribed faithfully
from the gm-utility console sources (CLUES arrays, gbclue GM-brief spines,
and the princes-bride module). The Slice 4 clue safety-net now works
against real content — essential-gap banner, act labels, and fallbacks.

day-one and princes-bride carry the source's own essential/optional split;
the four GM-brief spines mark each tracked clue essential (single curated
list, no optional tier in-source).

tests/scenarios/clues.test.js guards shape, unique ids, and solvability.
129 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:12:39 +01:00
slaguru666andClaude Opus 4.8 b4d797b52d feat: port all six Continuum 2026 slots + real schedule times
Rewrite continuum-2026.js from the authoritative con-desk.html SLOTS
array: six real slots with days/times/systems/players and startsAt set,
so the hub's live-now/up-next follows the real convention clock.

Add five scenario modules (day-one, vain-crown, silvery-moon, chopper,
princes-bride) transcribed from the existing gm-utility console
timelines, and register them so every slot is tappable. Day One (BRP)
borrows the coc-d100 pack; the others map to their own systems.

Corrections found while porting: the slate is six slots (Slot 5 Silvery
Moon was missing) and s6 is 'Get to the Chopper — Another Fine Mess'.

tests/con/continuum-2026.test.js guards slot resolvability, valid dice
packs, and schedule status. 125 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 21:52:53 +01:00
slaguru666andClaude Opus 4.8 b57880d630 feat: Slice 5 — convention hub (live now / up next, deep-links)
Add the convention hub landing screen: a pure schedule engine
(analyzeSchedule) classifies each slot as live/next/done/upcoming from an
injected now; <con-hub> renders the slate + summary and emits open-scenario;
a scenario registry resolves a slot's scenarioId to its module; gm-shell
routes hub<->session (hiding/restoring the Rail) with a home rail chip.

Ships continuum-2026.js with the real slate (only AFTERIMAGE ported;
others scenarioId:null, start-times TBC) and a fully-timed example-con.js
template/fixture. 122 tests pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 21:35:47 +01:00