Commit Graph

68 Commits

Author SHA1 Message Date
yuanzonghao 3fa3da5378 chore(play): remove session-id readout and decorative footer mark
The session-id slice shown in the play header was an opaque timestamp
that reads as noise to players. The footer's "Ⅰ · Ⅰ" was a leftover
decorative mark after its sibling controls were moved above the canvas.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 16:00:16 +08:00
DESKTOP-I1T6TF3\Q b5f73d8082 fix(play): scene image renders as 1px sliver while CDN bytes still arrive
When the Runware CDN download was slow (~10-20s over VPN / strict
networks, vs. the optimistic <2s the existing comment assumed), the
preload's 8s timeout fired and setImageUrl committed before the bytes
were actually decoded. The rendered <img> has w-auto h-auto and no
intrinsic aspect-ratio source — until the image loads the layout
collapses to roughly 1px tall, giving the "等了很久 → 一根线 → 突然
出图" jank.

Two compounding fixes:

  app/play/page.tsx       IMAGE_PRELOAD_TIMEOUT_MS  8000 → 20000.
                          Real CDN+decode usually finishes well before
                          this; pushing the ceiling out just stops the
                          window where we commit a half-loaded URL.

  components/PlayCanvas.tsx  Add width={1792} height={1024} HTML attrs
                          to the scene <img>. Doesn't affect rendered
                          size (still driven by w-auto h-auto and the
                          maxWidth/maxHeight in sizeStyle); the
                          browser uses them purely as an intrinsic
                          aspect-ratio source, so the placeholder box
                          reserves a 16:9-ish frame even mid-download.

Together: slow networks now mostly wait through preload; on the rare
genuine timeout the layout still holds shape instead of collapsing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 07:24:42 +08:00
DESKTOP-I1T6TF3\Q ea861b7c99 feat(web): style modal — custom prompt + per-preset override, inline edit
Two coordinated additions to the 绘画风格 modal so the user can shape
the styleGuide that ultimately feeds every painter/director agent,
without ever mutating the source-of-truth STYLE_MAP:

1. New "自定义" entry sits right under "自动" — opens an inline
   textarea where the user can write a free-form styleGuide (mix of
   Chinese / English, sent verbatim to the image model). Stored as
   in-memory state on HomePage (customStyleGuide), so refresh clears
   it — fits the "one-shot session" semantics of this UI.

2. Every preset card now exposes a small pencil on the right of its
   prompt area. Clicking it inlines a textarea pre-filled with the
   current effective prompt (override if any, else STYLE_MAP value).
   Saving writes to styleOverrides[name] — a separate in-memory
   record keyed by preset name. STYLE_MAP is never written to.

start() selects the styleGuide with this priority:
  customStyleGuide (when 自动→自定义)
  > styleOverrides[artStyle]
  > STYLE_MAP[artStyle]
  > STYLE_MAP[DEFAULT_STYLE]

UX polish in the same change:
- 标题永远只读 (only the prompt is editable)
- 只读 prompt 行去掉边框/底色,回归纯文字 + 右上铅笔
- 「自动」项无 prompt 可编辑,标题下直接放一行说明
- 编辑态 textarea 用 ember 边框作为"正在编辑"视觉反馈
- 「保存并选用」一并 onPick + close;「还原默认」清除该预设的 override
- 搜索框同时匹配标题/原名/prompt 内容
- 移除「自由输入」标签 (now visually redundant with the pencil affordance)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 07:24:42 +08:00
Zonghao Yuan a6825f0292 Merge pull request #20 from zonghaoyuan/docs/readme-2col-screenshots
docs: replace README screenshots with 14-image 2-column gallery
2026-06-03 13:37:24 +08:00
Zonghao Yuan a333b21759 feat(web): add favicon (IP monogram SVG) (#18)
站点此前没有 favicon,浏览器显示默认地球图标。新增 app/icon.svg:
站点同款 Cormorant Garamond 衬线的「IP」字母组合 —— I 直立 (clay #2D1810)、
P 斜体 (ember #D97A2E),米白圆角底,呼应 README 的 wordmark。字形以矢量轮廓
内嵌(从 Cormorant wght 600 / italic 400 实例化抽取),不依赖 webfont,各处渲染一致。
Next.js 16 见到 app/icon.svg 会自动注入 <link rel="icon">,无需改动 layout.tsx。

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 11:23:34 +08:00
DESKTOP-I1T6TF3\Q 4d46bd0664 docs: replace README screenshots with 14-image 2-column gallery
Drop the old 9-image 3x3 grid (4/5/a4/c3/c5/c7/d2/f2/f5.webp) and bring
in 14 new stills as 1.webp..14.webp, laid out as 7 rows of 2 columns at
width=420. Source PNGs (1920x1080 for 1-8, 1200x680 for 9-14) are
resized to fit inside 1200x680 and saved as q=85 WebP — 70-150KB each.

All three README locales (zh/en/ja) share the same paths so a single
asset swap refreshes every edition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 06:53:46 +08:00
Zonghao Yuan 95cfe24065 Merge pull request #19 from zonghaoyuan/staging
chore: promote staging to main (favicon + home revamp)
2026-06-03 11:27:16 +08:00
DESKTOP-I1T6TF3\Q 1bcea098d5 chore(web): swap 数据幽灵 and 极简杀机 positions in male DISPLAY_ORDER
m14 (极简杀机) is currently a 14.7KB placeholder while m18 (数据幽灵)
got a real curated cover this round — promote 数据幽灵 into the front
row and demote 极简杀机 back to its original neighborhood so the visible
首屏 only shows finished art.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 04:13:51 +08:00
DESKTOP-I1T6TF3\Q c90929ce4b feat(web): pin curated male cards to the front + row-first grid layout
Two coupled changes so the user's preferred male cards (复古未来梦,
社团存亡日, 黄昏归途, 极简杀机, 辐射新娘, 霓虹义体, 月光下的约定,
花魁的刀) actually appear in the visual front row:

1. Add a DISPLAY_ORDER indirection. STORIES, covers (m{i}.webp),
   prebaked first-acts (firstact/m{i}.json) and prompts.json are all
   keyed on the original array index — renaming them would touch
   dozens of static assets. DISPLAY_ORDER instead lets the homepage
   iterate cards in a curated order while still resolving each card's
   assets via its original index. Editing one line re-shuffles the
   gallery.

2. Switch the gallery wrapper from CSS multi-column (columns-N) to
   grid (grid-cols-N). columns fills column-first (top-of-col-1, then
   bottom-of-col-1, then top-of-col-2...) so the first eight entries
   of DISPLAY_ORDER ended up stacked down the leftmost column instead
   of across the top row. Grid fills row-first, which is what "visual
   front row" actually means. Cards are already fixed at aspect-ratio
   4/5 so row heights stay uniform — no masonry effect lost.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 04:11:26 +08:00
DESKTOP-I1T6TF3\Q cd7619265c chore(web): swap in 6 curated male covers
Replace m7 / m8 / m9 / m11 / m13 / m18 with hand-picked images from
the offline reroll set. Source files at 686×1200 (m7/m8/m9/m11) were
attention-cropped to the existing 960×1200 4:5 target via sharp's
fit:'cover' + position:'attention' — same pipeline as the bed4dc5
cover batch, so the new images blend into the masonry without visible
aspect-ratio drift. m13 (复古未来梦) and m18 (数据幽灵) were already
at 960×1200 and copied straight through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 04:11:26 +08:00
DESKTOP-I1T6TF3\Q cbabc54273 chore(engine): log worldSetting and storyBible at session start
Two lines in startSession: the full worldSetting being fed to the
Architect, and the resulting logline/genreTags/synopsis it produced.
Cheap to keep — fires once per session — and makes it possible to tell
at a glance whether a "story unrelated to my input" report is a frontend
transport bug, a worldSetting layout problem, or the LLM ignoring the
seed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 03:51:58 +08:00
DESKTOP-I1T6TF3\Q d241300ed6 fix(web): fall back to current Typewriter phrase + frontload it in worldSetting
Two related fixes so the home start button actually reflects what the
user sees:

1. Lift the Typewriter's current phrase index up to HomePage so start()
   can read which example is on screen right now. When the textarea is
   empty, start() now substitutes that phrase as the user's story seed —
   "what you see is what you play", instead of the previous behavior
   where an empty input produced a generic worldSetting with no plot
   direction and the model invented something unrelated.

2. Restructure the worldSetting string so the user prompt (or the
   chosen Typewriter phrase) sits at the top, alone, wrapped in a
   strong directive ("必须以此为剧情主线,不要偏离"). Before, the seed
   was a single line sandwiched between the gender/style/pace boilerplate
   and the generic "edit with dramatic tension" tail, which the Architect
   tended to skim past when expanding the bible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 03:51:51 +08:00
DESKTOP-I1T6TF3\Q eb9b875454 fix(web): use existing STYLE_MAP key for home 「自动」 fallback
After bed4dc5 renamed style keys to include the (Image N参考) suffix,
the home start() still resolved 「自动」 against the legacy bare name
「京阿尼细腻日常」, leaving styleGuide undefined and tripping the
/api/start required-field check on the default click.

Fall back to "Galgame CG 梦幻光影" — a key that actually exists in
STYLE_MAP — so the default path resolves cleanly without changing the
behavior of explicitly selected styles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 03:37:49 +08:00
DESKTOP-I1T6TF3\Q bed4dc5a8f feat(web): gender-differentiated 4:5 covers + per-card styleGuide prebake
- 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>
2026-06-03 02:26:35 +08:00
DESKTOP-I1T6TF3\Q 820a5f7e87 feat(web): refactor home preset cards to 16:9 poster style with titles and tags below cover 2026-06-03 02:25:02 +08:00
Zonghao Yuan 8ca818e4d7 Merge pull request #16 from zonghaoyuan/staging
Merge staging into main
2026-06-03 01:19:14 +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 639201cd38 docs: replace How it works mermaid with localized SVG diagrams (#14)
The auto-laid-out Mermaid flowchart looked rough. Replace it with a
hand-built Anthropic-style diagram: color-coded role cards, a dashed
scene-generation group, and the speculative pre-generation loop.

Each SVG bakes in a dark background so it renders consistently whether
the viewer's GitHub theme is light or dark (theme is per-viewer, not
per-repo). Ship one localized SVG per README (zh/en/ja) to preserve the
existing per-language diagrams, and strip claude.ai artifact residue
(onclick / var() / context-stroke).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 01:08:08 +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 9543c3dba1 docs: note Cloudflare deploy needs Workers Paid Plan
The scene pipeline needs more CPU time than the Workers Free 10ms cap
allows, so Cloudflare deploys require Workers Paid. The old "pick
whichever you prefer" implied false cost parity with Vercel (free
Hobby works), so recommend the one-click Vercel deploy for personal
use. Applied to all three READMEs (zh/en/ja).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 00:39:17 +08:00
Zonghao Yuan ee2c29ae63 Merge pull request #11 from zonghaoyuan/merge-staging-to-main
Merge staging into main (#10 的冲突已解决)
2026-06-03 00:07:43 +08:00
yuanzonghao 4725b2cf72 Merge staging into main (#10)
将 staging 全部进展合入 main:Cloudflare 部署、README 重构(中文默认 +
hero header + 版块重排 + 日文截图 + 切换器修复)、LICENSE 修复(恢复 AGPL-3.0
识别)、engine 点击标注迁移至浏览器 Canvas。冲突全部取 staging 版,移除已
废弃的 README.zh-CN.md 与 annotate.ts;合并后 main 树与 staging 完全一致。
2026-06-03 00:00:34 +08:00
yuanzonghao cd9e9012bf docs: point 简体中文 switcher to repo homepage
The relative `README.md` link resolved to GitHub's blob file view instead
of the repo landing page. Use the absolute repo URL so the switcher returns
to the homepage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 22:29:35 +08:00
yuanzonghao 6038216751 docs: reorder README sections, simplify Live Demo badge, add JP screenshots
- Move "How it works" above "Team & Vision" so the technical deep-dive
  follows the screenshots while reader interest is highest
- Simplify the Live Demo badge to a single segment (drop the domain; the
  badge still links to infiplot.com)
- Add the missing Screenshots section to the Japanese README (i18n gap:
  it was added to zh/en but never backported to ja)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 22:29:35 +08:00
Zonghao Yuan d263437756 Merge pull request #9 from zonghaoyuan/feat/cloudflare-migration
feat: add Cloudflare Workers deployment alongside Vercel
2026-06-02 22:14:52 +08:00
yuanzonghao 203e63edc2 fix: server-side payload cap + cleaner image abort
Addresses Copilot review on PR #9:

- /api/vision: add MAX_ANNOTATED_BYTES (3 MB) cap on annotatedImageBase64,
  plus an explicit type/non-empty check. Browser annotator resizes to 768
  wide (typically 200-800 KB base64), so 3 MB rejects abusive direct-API
  payloads that would otherwise inflate upstream vision LLM costs.
- annotateClient: replace `img.src = ""` on timeout with removeAttribute
  to avoid the legacy browser behavior of treating empty src as a
  navigation to the current document URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 22:13:40 +08:00
yuanzonghao 72331bb865 feat: add Cloudflare Workers deployment alongside Vercel
InfiPlot now deploys to either Vercel or Cloudflare Workers — both
targets are first-class. The project is fully stateless (sessions live
on the client), so the Cloudflare side needs only Workers + Workers
Assets and zero D1/KV/R2.

- apps/web/wrangler.jsonc — nodejs_compat, Assets binding, 60s CPU
  limit (Workers Paid required; matches vercel.json maxDuration). I/O
  wait does not count against this budget — fits the LLM-bound
  workload that's most of the runtime.
- apps/web/open-next.config.ts — minimal defineCloudflareConfig (no
  cache needed since the engine is stateless).
- apps/web/package.json — added build:cf / preview:cf / deploy:cf via
  @opennextjs/cloudflare + wrangler (both devDeps); sharp moved from
  dependencies to devDependencies (only used by the manual
  optimize-home-images.mjs / localize-firstact-images.mjs scripts now).
- .gitignore — .open-next, .wrangler, .dev.vars.
- READMEs (3 langs) — Deploy to Cloudflare button next to Vercel,
  plus a Cloudflare section in the env-var setup (wrangler secret put
  + Cloudflare Access for staging access control).

Verified: pnpm typecheck + pnpm build (Vercel path) + pnpm build:cf
(OpenNext bundle: worker 4 KB, server 24 MB, assets 32 MB / 186
files — all within Workers limits) + pnpm preview:cf with the full
play loop (start → scene → background click → CORS-clean Canvas
annotation via Runware CDN → vision LLM → insert-beat) all green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 21:47:03 +08:00
yuanzonghao 346d5359d4 refactor(engine): move click annotation from sharp to browser Canvas
The vision pipeline used sharp to draw a click marker on the scene image
server-side (engine/src/annotate.ts) and to render the MOCK_IMAGE
placeholder PNG (engine/src/mockImage.ts). Both moved off the runtime:

- annotateClick → apps/web/lib/annotateClient.ts (Canvas 2D in the
  browser; toDataURL → raw PNG base64 forwarded to /api/vision). Saves
  a server-side image re-fetch per click and frees the engine from
  sharp's native binding (which doesn't run on Cloudflare Workers).
- mockImageDataUri → self-describing SVG data URI (no rendering needed).

VisionRequest contract changes: prevImageUrl + click → annotatedImageBase64.
Server forwards the bytes straight to the vision LLM as image_url.

sharp is removed from packages/engine entirely and from next.config.ts's
serverExternalPackages. apps/web/package.json + lockfile cleanup ships
in the follow-up Cloudflare deployment commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 21:46:45 +08:00
yuanzonghao dd8b60c06b docs: move one-click deploy section directly below Live Demo
Surfaces the deploy entry point higher on the page so it isn't buried
near the bottom. The section keeps its inline link to the Configuration
guide, so the deploy flow is unaffected by the reorder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 20:55:23 +08:00
yuanzonghao 236f5494c1 docs: add hero header to READMEs and make Chinese the default
- Add centered hero block: SVG wordmark banner, short tagline, and
  project-stat badges (stars/watchers/forks/issues + Live Demo, License,
  LINUX DO forum backlink)
- Swap default README to Chinese (targeting CN developers); English
  moves to README.en.md, Japanese stays README.ja.md
- Add SVG wordmark banner at docs/banner.svg
- Cross-link language switchers and fix per-language deploy envLink anchors

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 20:55:23 +08:00
yuanzonghao ee3a614d26 style(web): tidy the homepage QQ group caption
Drops the fa-qq penguin icon and the "扫码加入,或搜索群号" call-to-action
in favor of a plain "QQ群号:575404333" label — the QR right above already
implies scanning, and the column header names the group.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 19:32:37 +08:00
yuanzonghao abb6be5cdd feat(web): add QQ beta-community group to homepage + READMEs
Fills the long-empty "内 测 用 户 群" placeholder (was "群二维码 /
邀请链接(待补充)") on the homepage contact grid with the real QQ
group QR (group ID 575404333) plus a scan-or-search line.

Mirrors it across all three READMEs as a scan-to-join block right
after the contact line, rendered from apps/web/public/qq-group.webp
(760×760 QR-only crop with a white quiet zone, ~45KB).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 19:26:54 +08:00
DESKTOP-I1T6TF3\Q 3e132ce28b docs: drop the click-to-enlarge caption from README screenshots
GitHub markdown can't host an in-page lightbox or prev/next carousel —
all <script> is stripped server-side, so a clickable thumbnail can only
ever open the raw image in a new page. The hint line was misleading
about that interaction, so just remove it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 17:31:32 +08:00
DESKTOP-I1T6TF3\Q bce686a7eb docs: make README screenshots clickable to view full size
Wraps each <img> in an <a href="..."> linking to the same path so GitHub
opens the full-resolution image on click instead of just showing the
inline thumbnail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 17:24:44 +08:00
DESKTOP-I1T6TF3\Q d93c16d836 feat(web): 红果-style homepage + instant-play prebaked first acts
Rewrites all 64 homepage cards (32 男性向 + 32 女性向) as short-drama hook
stories (战神归来 / 重生分手前夜 / 系统选妃 / 穿成乙游男配 / 末世异能 / 民国
谍战 / 修真渡劫 …) and regenerates each cover via FLUX in its assigned art
style (12 styles spread across 64 cards) at 832×1024 ≈4:5.

Click-to-play path: cards now jump straight to /play?card=<name> and hydrate
Session from /home/firstact/<name>.json — the engine pipeline (Architect +
Writer + CharacterDesigner + Painter) has been pre-run for 44/64 cards. The
remaining 20 (m14/m29/f14..f31) are pending an LLM credit top-up; their
clicks fall through to live /api/start for now.

Runware-hosted first-scene images are downloaded into /home/firstscene/
and the JSONs are rewritten to point at the local webp, so click → first
image is bounded by local-disk decode (~100ms) instead of CDN round-trip.

Scripts:
- scripts/generate-home-images.mjs  — rewrites all 64 cover prompts, per-card
  styles baked into prompts, 832×1024 dims to match StoryCard aspect
- scripts/prebake-firstacts.mjs     — POST /api/start × 64 with concurrency
  4, saves StartResponse to public/home/firstact/<name>.json
- scripts/localize-firstact-images.mjs — downloads each prebaked imageUrl
  to public/home/firstscene/<name>.webp (q80, ≤1600px) and rewrites JSON

README: adds Screenshots section (3×3 gallery) to README.md / README.zh-CN.md,
9 in-game shots compressed to docs/screenshots/*.webp (7.5MB → 680KB).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 17:20:34 +08:00
DESKTOP-I1T6TF3\Q 9ae91dd3ed feat(play): hug-canvas action buttons, unified mute, enlarged back-link
UI layout (PlayCanvas + play/page.tsx):
- "F · 全 · 屏" button (renamed from 演 · 示 to match what users
  actually mean by F) floats above the canvas, right-aligned, via a
  new `aboveCanvas` ReactNode slot that lives on the relative
  inline-block image wrapper at `bottom-full right-0`. It hugs the
  actual image right edge regardless of aspect ratio.
- "有 · 声 / 静 · 音" button mirrors that on the left via a new
  `aboveCanvasLeft` slot.
- Both slots also render inside the loading placeholder so the two
  controls appear from frame one, before the scene image arrives.
- InfiPlot back-link grows from 15px to 22/26px (mobile/desktop) with
  a slightly larger arrow, matching the brand'\''s presence on the
  homepage hero.
- Canvas-bottom metadata row (image dims on left, tutorial hint on
  right) dropped. The "—" placeholder and "···" loading state looked
  like stray punctuation; users found them noisy.
- Footer collapses to a single centered "Ⅰ · Ⅰ" mark.

Audio gating logic (play/page.tsx):
- Collapse the two-flag audio gate into one source of truth. The
  homepage "语音配音" choice no longer lives in a separate
  `audioEnabledRef` flag that gates `fetchBeatAudio` independently
  of the in-page mute state. Instead the `muted` useState lazy
  initializer reads `sessionStorage["infiplot:custom"].audioEnabled`
  and projects it inversely (audioEnabled=false → muted=true) so
  the 静音/有声 button correctly reflects the homepage selection
  from the first frame. The in-page toggle remains the source of
  truth from then on (persisted to localStorage:infiplot:muted).
- This fixes a visible disconnect where picking "关闭" on the
  homepage left the play page showing 有声 because the in-page
  state had no link to the homepage choice.
- The sessionStorage read uses the renamed key "infiplot:custom"
  (the infiplot rename PR changed it from yume:custom on the home
  side but the play side hadn'\''t been updated to match).

No new TTS quota is ever burned while muted: fetchBeatAudio'\''s
mutedRef.current early-return is the only path to /api/beat-audio
and is checked before the fetch fires; mute transitions also abort
in-flight requests.
2026-06-02 15:42:49 +08:00
Zonghao Yuan cffe4da4ca docs: streamline 3 READMEs and fix EN language switcher (#6)
Slim overview across EN/zh/JA, drop badges/blockquote/contributing, trim LICENSE header; fix the English switcher to point at the repo homepage instead of the GitHub site root.
2026-06-02 15:33:08 +08:00
DESKTOP-I1T6TF3\Q 6da87df73a feat(web): home story-card polish + play page back-link rebrand
Home (apps/web/app/page.tsx):
- StoryCard locked to uniform aspectRatio "4 / 5". The previous
  "placeholder 4/5 → naturalRatio after onLoad" flow coupled card
  height to lazy-load order: cards still below the fold sat at the
  placeholder ratio while above-the-fold cards snapped to their
  image's actual ratio (1.6 landscape vs 0.75 portrait vs 1.23
  squarish), so the gallery looked inconsistent until a hard refresh
  re-decoded everything from cache synchronously. Fixed ratio +
  object-cover removes the coupling.
- StoryCard hover overlay collapsed from two sibling layers
  (backdrop-blur + mask-image + dark gradient sibling) into one
  element with a pure rgba(0,0,0,…) linear-gradient and an opacity
  transition. Chromium does not animate backdrop-filter cleanly when
  combined with mask-image on an empty element — the first hover
  frame shows a full rectangular blur before the mask kicks in, then
  snaps to the feathered shape ("矩形磨砂 → 渐变磨砂"). One layer,
  one transitioning property, no compositing race.

Play (apps/web/app/play/page.tsx):
- Header back-link "云梦" → "InfiPlot" using the same serif + italic
  ember "Plot" treatment as the homepage wordmark. Resolved against
  the parallel plain-text rebrand already on infiplot/staging by
  keeping the styled version for brand consistency.
2026-06-02 14:42:26 +08:00
Zonghao Yuan 588b668d14 Merge staging into main (#3)
* feat(engine): Architect agent + cross-scene StoryState coherence

Add a dedicated Architect LLM call at session start that expands the terse
world/style prompt into a persistent story bible (logline, genre, second-
person protagonist, cast, engineered opening hook). The bible seeds a
StoryState the Writer reads and patches every scene, carried + merged
across cuts (applyStoryStatePatch) so the story keeps a spine from beat
one instead of jumping between scenes.

- prompts: inject web-novel / short-drama / galgame craft into Writer +
  Architect; Writer emits storyStatePatch to update the running bible
- director: parallelize voice + non-entry portraits with the Painter
  (only entry-beat portraits block paint) to offset Architect latency
- architect: chat/parse guarded so a malformed response never aborts start
- types: StoryState / StoryStatePatch; required on Start/SceneResponse

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: add AGPL-3.0 license, README i18n, and TTS accuracy fix (#2)

* docs: add AGPL-3.0 license, README i18n, and TTS accuracy fix

- LICENSE: add GNU AGPL v3 with InfiPlot copyright notice
- README.md: rewrite for open-source project, fix TTS description
  (TTS uses MiMo's own protocol, not OpenAI-compatible)
- README.zh-CN.md: add Simplified Chinese translation
- README.ja.md: add Japanese translation
- package.json: change license from UNLICENSED to AGPL-3.0-only

* fix: address Copilot review — .env.example TTS comment, zh-CN formatting

- .env.example: clarify TTS uses MiMo's own protocol, not OpenAI-compatible
- README.md: 'land paper after paper' → 'publish paper after paper'
- README.zh-CN.md: add spaces around '5 月', fix code formatting
  for model names (deepseek-v4-flash)

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 13:41:37 +08:00
Zonghao Yuan 9a3511f220 docs: add AGPL-3.0 license, README i18n, and TTS accuracy fix (#2)
* docs: add AGPL-3.0 license, README i18n, and TTS accuracy fix

- LICENSE: add GNU AGPL v3 with InfiPlot copyright notice
- README.md: rewrite for open-source project, fix TTS description
  (TTS uses MiMo's own protocol, not OpenAI-compatible)
- README.zh-CN.md: add Simplified Chinese translation
- README.ja.md: add Japanese translation
- package.json: change license from UNLICENSED to AGPL-3.0-only

* fix: address Copilot review — .env.example TTS comment, zh-CN formatting

- .env.example: clarify TTS uses MiMo's own protocol, not OpenAI-compatible
- README.md: 'land paper after paper' → 'publish paper after paper'
- README.zh-CN.md: add spaces around '5 月', fix code formatting
  for model names (deepseek-v4-flash)
2026-06-02 13:39:54 +08:00
Zonghao Yuan 70d0927a3e Merge pull request #1 from zonghaoyuan/feature/story-harness-opt
feat(engine): Architect agent + cross-scene StoryState coherence
2026-06-02 13:24:46 +08:00
yuanzonghao 15ce03a912 feat(engine): Architect agent + cross-scene StoryState coherence
Add a dedicated Architect LLM call at session start that expands the terse
world/style prompt into a persistent story bible (logline, genre, second-
person protagonist, cast, engineered opening hook). The bible seeds a
StoryState the Writer reads and patches every scene, carried + merged
across cuts (applyStoryStatePatch) so the story keeps a spine from beat
one instead of jumping between scenes.

- prompts: inject web-novel / short-drama / galgame craft into Writer +
  Architect; Writer emits storyStatePatch to update the running bible
- director: parallelize voice + non-entry portraits with the Painter
  (only entry-beat portraits block paint) to offset Architect latency
- architect: chat/parse guarded so a malformed response never aborts start
- types: StoryState / StoryStatePatch; required on Start/SceneResponse

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 11:44:55 +08:00
Zonghao Yuan 16707cc255 Merge pull request #10 from zonghaoyuan/rename/infiplot
chore: complete @yume → @infiplot rename (post-PR#9)
2026-06-02 09:30:23 +08:00
yuanzonghao 8eda27f241 chore: complete @yume → @infiplot rename (post-PR#9)
PR #9 已完成首页和 layout 的视觉品牌迁移,此 commit 补齐剩余的
技术性改名 —— workspace 包名、source import、localStorage 键、
CSS keyframe、内部 header logo、.env.example、README。

- @yume/* → @infiplot/* (6 package.json + 17 imports + lockfile)
- localStorage/sessionStorage: yume:* → infiplot:*
  (含 PR #9 新增的 yume:hintClosed)
- CSS keyframe yume-ripple → infiplot-ripple
- new/play 页面 header logo "云梦" → "InfiPlot"
- 代码注释中的「云梦」style 形容词删除(layout.tsx, page.tsx)
- 根 package.json name + description(描述跟齐 staging
  "AI 实时交互剧情游戏")
- README: tagline / Vercel deploy URL / 目录树 / engine 描述

保留:prompts.ts 的 LLM 体裁术语「视觉小说/galgame」、CustomForm
placeholder 的「视觉小说画风」(图像模型识别的风格名词)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 09:27:00 +08:00
Zonghao Yuan 5d0a5bb756 Merge pull request #9 from zonghaoyuan/feature/infiplot-low-fi-homepage
feat(web): InfiPlot low-fi homepage with AI-generated cards + gender-…
2026-06-02 01:30:29 +08:00
yuanzonghao 54fda1914c fix(web): gate play-page un-mute prefetch on actual mute transitions
On mount the mute effect fired alongside the scene effect (both call
prefetchSceneAudio), so the initial /api/beat-audio batch was dispatched
twice — the first set aborted mid-flight. Track the previous muted value
in a ref and only re-prefetch on a real transition, leaving the mount-time
synthesis to the scene effect. Addresses Copilot review on PR #9.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 01:29:33 +08:00
yuanzonghao 8f94d3aa65 feat(web): editorial homepage rework — flat 2×30 stories, autosize input
Revises the InfiPlot homepage from the initial prototype pass.

Stories data model
- Replaces the artificial 7-hero + 16-gallery split with a flat
  per-gender model: 30 preset stories each for 男性向 / 女性向.
- Renames assets hero*/gallery* → m{0..29} / f{0..29}; same index
  shares aspect ratio across genders so the gender crossfade never
  jumps card height.
- Fills in the missing 女性向 set and expands both genders to 30.

Cards
- StoryCard measures aspect ratio at runtime from the loaded image
  (onLoad → naturalWidth/Height), fixing the frosted-caption band
  reflow on lazy image load. Drops ready/fallback props; single
  masonry map over STORIES[gender].

Hero input
- Single-line <input> → auto-growing <textarea> (rows=1, resize-none)
  so long prompts and long card seeds are fully visible. Enter submits,
  Shift+Enter inserts a newline.
- lining-nums on the input so digits sit on the baseline instead of
  Cormorant's default old-style figures.

Typography / styles
- layout.tsx: editorial fonts (Cormorant Garamond + Inter via
  --font-serif / --font-sans) + Font Awesome; drops Patrick Hand /
  Noto Sans SC and the hand-drawn SVG jitter filters.
- globals.css trimmed to the editorial base (paper grain, hairline,
  num, ripple); play/page.tsx font/style follow-up.

Scripts
- generate-home-images.mjs reworked into a flat 2×30 idempotent
  Runware FLUX.2 generator.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 01:14:49 +08:00
DESKTOP-I1T6TF3\Q 136ceff69f feat(web): InfiPlot low-fi homepage with AI-generated cards + gender-reactive hero + audio toggle fix
Rebuilds the landing page from the prototype: 1900px scale-to-fit hero with
hand-drawn SVG-jitter frames, typewriter input + start button, 5 horizontal
collapsible category selectors (with style-picker modal), 7 scattered hero
cards over a 16-card masonry gallery, and project intro panel.

Each card is filled with a Runware FLUX.2 image, pre-generated and stored as
WebP (~2 MB total for 30 cards). Hero card content + image switches by
性向 (男性向 / 女性向); gallery stays shared.

Hover overlay on every card shows title + outline in a bottom-up dark
gradient, matching the prior homepage's interaction style.

Bug fixes uncovered by tracing the form-state → engine pipeline:
- 「语音配音:关闭」was previously stuffed into styleGuide (consumed only by
  FLUX, ignored by TTS). Now serialized as audioEnabled boolean in the
  sessionStorage payload; play page's fetchBeatAudio early-returns when
  false, so no /api/beat-audio request fires.
- 「绘画风格:自动」used to pass the literal Chinese phrase "由模型根据
  prompt 自动判断画风" to FLUX, which painted it as text. Now maps to the
  二次元/galgame default prompt.

Adds reusable scripts under apps/web/scripts/:
- generate-home-images.mjs — Runware FLUX.2 idempotent batch generator
- optimize-home-images.mjs — sharp WebP downscale + recompress

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 17:08:55 +08:00
Zonghao Yuan 774f3734fd Merge pull request #8 from zonghaoyuan/feature/vercel-deploy-prep
feat: Vercel Hobby deploy readiness — image URLs, jsonrepair, DeepSeek
2026-06-01 16:31:54 +08:00
yuanzonghao 42a09c42f8 fix: address Copilot review — SSRF validation + log truncation
- annotate.ts: add assertSafeUrl() to reject non-https/data URLs and
  private/reserved IPs (SSRF prevention); cap response body to 10 MB
- jsonParser.ts: truncate raw model output in error log to first 800
  chars to avoid flooding logs / leaking sensitive content
2026-06-01 16:29:08 +08:00