Files
infiplot-web/app
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
..