Commit Graph

5 Commits

Author SHA1 Message Date
DESKTOP-I1T6TF3\Q b805b1d9c2 fix(play): scene image renders progressively from top → CF Worker proxy
Symptom: in Chrome on certain networks the scene <img> renders row-by-row
from top to bottom — "层层加载" — instead of appearing atomically.

Root cause (confirmed via DevTools):
  - Chrome opportunistically opens HTTP/3 (QUIC) to im.runware.ai.
  - QUIC streams to Runware sometimes error mid-transfer:
      net::ERR_QUIC_PROTOCOL_ERROR
    HTTP-level status stays 200 (response headers received), but bytes are
    truncated. The browser paints whatever PNG bytes it has so far → visible
    row-by-row decode.
  - The earlier preloadImage()+decode() trick can't fix this — neither
    HTTP-cache reuse nor sync decode helps when the bytes themselves were
    never fully delivered.

Two-tier fix:

1. Client: fetch → Blob → URL.createObjectURL() (app/play/page.tsx)
     - <img src> only ever points to a blob: URL whose bytes are 100%
       resident in the JS heap. No network-backed src = no possibility of
       progressive paint.
     - Module-level blobUrlCache keys by original URL so speculative
       prefetch + the eventual commit share one fetch.
     - Old blobs are URL.revokeObjectURL()'d on scene swap + unmount to
       release memory.

2. Network: optional Cloudflare Worker proxy (worker/)
     - Browser ↔ Worker is HTTP/2 over CF edge (extremely stable).
     - Worker ↔ Runware is a server-to-server fetch (no QUIC fragility,
       Cloudflare's backbone handles transit).
     - Worker buffers the full upstream response → client never sees a
       half-stream.
     - Bonus: CF edge cache (cacheEverything, 1y TTL) on Runware UUIDs;
       Access-Control-Allow-Origin: * so client fetch() can't hit CORS.
     - Hardened: only proxies im.runware.ai, only GET/HEAD/OPTIONS, all
       other hosts/methods → 403/405.

Wired via NEXT_PUBLIC_IMAGE_PROXY_URL (inlined at build). Empty → no proxy
→ direct fetch (which still uses the blob path, just exposed to QUIC).

──────────────────────────────────────────────────────────────────────
Deploy steps (one-time, do this AFTER pulling this commit):

  1. Install wrangler globally:
       npm i -g wrangler

  2. Log in to Cloudflare (opens browser for OAuth):
       wrangler login

  3. From the worker/ directory, deploy:
       cd worker
       wrangler deploy

     wrangler will print the deployed URL, e.g.
       https://infiplot-image-proxy.<your-cf-username>.workers.dev

  4. Paste that URL into .env.local for local dev:
       NEXT_PUBLIC_IMAGE_PROXY_URL=https://infiplot-image-proxy.<...>.workers.dev
     …and into Vercel project settings (Environment Variables) for prod.
     NEXT_PUBLIC_ vars are inlined at build time, so the URL bakes into
     the bundle on the next deploy/dev-server restart.

  5. Restart dev server (pnpm dev) so the new env baked in. Generate a
     scene; Network tab should show requests going to *.workers.dev
     instead of im.runware.ai, no ERR_QUIC_PROTOCOL_ERROR, image renders
     atomically.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 22:50:48 +08:00
Zonghao Yuan 6ddbe7d377 feat: add privacy-friendly Umami page-view analytics (#15)
Cookieless, env-gated page-view tracking via Umami. The <Analytics />
component injects the script only when NEXT_PUBLIC_UMAMI_SRC and
NEXT_PUBLIC_UMAMI_WEBSITE_ID are both set, so local dev and forks send
nothing to our instance. Adds .env.example docs (section 6) and a
homepage footer privacy disclosure. No Cookie consent banner needed.
2026-06-03 01:14:55 +08:00
Zonghao Yuan dc5ecd60f6 refactor: flatten monorepo to single web package (#12)
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>
2026-06-03 00:55:45 +08:00
yuanzonghao 9cedfa66e4 feat: prefetch, vision split, provider adapter, UI polish
Engine
- Split /api/vision out from /api/interact so client can drive
  prefetch + cache lookup independently of click interpretation
- Image client switched to chat-completions+modalities API (OpenRouter/
  provider style), supporting markdown image URL responses
- annotateClick now resizes to 768w before composite to keep vision
  payloads small and avoid CDN timeouts
- Prompts updated to mention "JSON" in user messages (required by
  Gemini's strict JSON mode)
- Shared fetchWithRetry helper: 2 retries for chat/image, 0 for vision
  (with 60s hard timeout)

Client
- Parallel prefetch of all three choice branches on each new frame
- Effect deliberately excludes phase from deps so user-click doesn't
  abort in-flight prefetches
- Cache hit/miss/free-form fallback handled in handleClick
- PlayCanvas reads img naturalWidth/Height and adapts container to
  whatever aspect AI returns (no more cropped third choice)
- max-width raised to 560px, max-height calc(100dvh - 200px)

Misc
- README env-path corrected to apps/web/.env.local
- users.md: BGM/TTS idea note
- .env.example moved into apps/web alongside next config

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:38:03 +08:00
yuanzonghao cbd95bbea2 Initial commit: AI-driven visual novel scaffold
- Monorepo (pnpm workspace): apps/web + packages/{types,ai-client,engine}
- Next.js 16 web app with three-stage AI orchestration
- Three independently configurable providers: text LLM, image generator, vision model
- Warm minimalist editorial UI design
- One-click Vercel deploy ready

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:29:58 +08:00