Preset firstact JSONs had 37% of beats bundling narration and dialogue
on the same beat object, while the current engine (Paradigm D prose
splitter) produces strictly one-type-per-beat output. This mismatch
caused the preset card experience to feel different from actual gameplay.
Split 275 mixed beats across 94 files (120 total) into independent
narration→dialogue beat pairs, preserving all images, characters, voices,
and graph references. Beat count: 738→1013.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add characters[i].stepfunVoiceId to all 106 prebaked homepage first-act
JSONs (firstact/ + firstact-portrait/) so cards produce sound when the
server runs StepFun. Generated by scripts/enrich-firstacts-stepfun.mjs —
one TEXT-provider LLM call per character picking from the 32-preset
catalog. voice (Xiaomi reference audio), imageUrl, and scene are untouched;
only the new stepfunVoiceId field is appended.
All 147 characters across both orientation sets are enriched (0 failures).
* feat(home): localize character portrait URLs in prebaked first-act JSONs
Runware CDN URLs expire, breaking character portraits in prebaked story
cards. Download all 144 portraits as static WebP assets and rewrite
first-act JSONs to reference local paths instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore(scripts): add fetch timeout and simplify resize logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Card click flow now serves /home/firstscene/{name}.webp from Vercel static
hosting instead of fetching im.runware.ai/... — those URLs have a finite TTL
and would silently rot. Side benefit: backfilled the 18 stories that never had
a local webp (f14-f29, m14, m29), and refreshed the 44 stale webps left over
from a pre-prebake story batch so they actually match their cover art again.
Scope is scene.imageUrl only; characters[].basePortraitUrl still points at
Runware (painter consumes it server-side as referenceImages, where a local
public path won't resolve).
localize-firstact-images.mjs:
- skip the network when the local webp is already on disk (don't re-encode
what's already correct)
- read imageUrlRemote as a fallback URL when imageUrl is already localized,
so --force can refresh from the original Runware source
- also localize scene.imageUrl alongside the top-level imageUrl
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Regenerate 60 covers (30 male + 30 female) via FLUX with story-specific
prompts, replacing the prior gender-shared set
- Crop covers to 4:5 (960×1200) via sharp attention cover; matches new
homepage card aspectRatio
- Persist all 60 prompts to public/home/prompts.json so the prebake step
can reuse the cover's exact visual anchor (per-card styleGuide) and the
first-act scene visually carries over from the poster the player clicked
- Restore /play?card= prebaked instant-play path on homepage card click
- Add OpenAI-compatible image route in ai-client for non-Runware endpoints
- Hide Next.js dev indicators globally; tweak F-key fullscreen label
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Flatten the pnpm monorepo (apps/web + packages/*) into a single web package at the repo root.
- Move app/lib/components/scripts/public to root; drop apps/web and packages/* wrappers
- Rewrite tsconfig paths (@infiplot/*) to ./lib/*; turbopack.root = __dirname
- Update Vercel (no root-directory) and Cloudflare (pnpm build:cf at root) deploy paths
- Regenerate pnpm-lock.yaml to drop stale workspace importers
- Bump engines.node to >=22 to match wrangler
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>