feat: refresh creation config and visual assets

This commit is contained in:
2026-05-20 14:02:36 +08:00
parent 83e92fc3c4
commit ef09a23c35
509 changed files with 19470 additions and 43 deletions
+14
View File
@@ -371,6 +371,12 @@
- 决策:热身关全流程直接接入 `useMocapInput`,通过本地 mocap WebSocket `/stream` 消费 `general.body.center_norm` 身体中心、`actions/action/gesture/gestures/event/name/type` 动作名,以及 `hands[]``leftHand/rightHand``left_hand/right_hand` 手部坐标;位置步骤由身体中心推进,`wave_greeting``wave_left_hand``wave_right_hand``jump_once` 由 mocap 手势/轨迹推进。浏览器摄像头只作为背景层,动作数据源状态优先展示,键鼠仍作为本地调试兜底。
- 影响范围:`src/services/useMocapInput.ts``src/components/child-motion-demo/ChildMotionWarmupDemo.tsx`、对应单测与热身关技术文档。
- 验证方式:执行 `npx vitest run src/services/useMocapInput.test.ts src/components/child-motion-demo/ChildMotionWarmupDemo.test.tsx src/components/child-motion-demo/childMotionWarmupModel.test.ts src/services/child-motion-demo/childMotionDebugInput.test.ts src/routing/appRoutes.test.ts``npx eslint ...``npm run typecheck``npm run check:encoding`,并确认 `http://127.0.0.1:8876/stream` WebSocket 可握手、`http://127.0.0.1:3000/child-motion-demo` 可访问。
## 2026-05-18 寓教于乐频道补充热身关入口
- 背景:用户希望在发现页的寓教于乐板块里直接看到热身关入口,而不是只依赖独立直达路由。
- 决策:`child-motion-demo` 作为寓教于乐频道的独立卡片展示,点击后直接进入 `/child-motion-demo`;该入口与 `宝贝爱画` 并列,仍复用现有独立热身关路由,不新增新的创作模板或运行态壳层。
- 影响范围:`src/components/rpg-entry/RpgEntryHomeView.tsx``src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
- 验证方式:执行入口回归测试、`npm run typecheck``npm run check:encoding`,并在发现页的寓教于乐频道确认热身关卡卡片可点击进入 `/child-motion-demo`
- 关联文档:`docs/technical/CHILD_MOTION_DEMO_WARMUP_IMPLEMENTATION_SPEC_2026-05-09.md`
## 2026-05-09 GPT-image-2 图片生成统一迁移到 VectorEngine
@@ -615,6 +621,14 @@
- 验证方式:开发前优先阅读 `CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`;旧 `server-node`、Express、PostgreSQL、Go 方向只允许作为迁移参考。
- 关联文档:`docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md``AGENTS.md`
## 2026-05-18 寓教于乐电视端入口概念图采用横屏乐园地图方案
- 背景:寓教于乐板块需要面向电视端 / 横屏大屏的一组图形化入口概念图,既要像儿童乐园地图,又要和现有绘本插画风一致。
- 决策:概念探索优先采用横屏乐园地图结构,推荐顺序为环形乐园岛、展开绘本地图、云朵空中岛、草地舞台地图;生成时优先复用 `public/child-motion-demo/picture-book-grass-stage.png` 作为风格参考,输出仅保留在 `output/imagegen/` 概念目录中,不直接进入正式资源目录。
- 影响范围:寓教于乐板块视觉探索、后续前端入口设计、`scripts/generate-edutainment-tv-map-concepts.mjs`、相关设计文档。
- 验证方式:概念图需保持无文字、无真实品牌 IP、无暗色科技风,并与现有草地绘本资源在配色和笔触上保持一致。
- 关联文档:`docs/design/【前端体验】寓教于乐电视端乐园地图入口概念图-2026-05-18.md`
## 2026-04-28/29 server-rs DDD 分层与契约矩阵冻结
- 背景:server-rs 模块多、上下文多,需防止领域规则、SpacetimeDB 表、HTTP BFF、前端临时逻辑互相污染。
+26 -2
View File
@@ -171,6 +171,14 @@
- 验证:生成文件落在 `public/branding/taonier-logo-*/`,用 Pillow 检查图片尺寸和非空;执行 `node --check scripts/generate-taonier-logo-concepts.mjs``npm run check:encoding``git diff --check`
- 关联:`scripts/generate-taonier-logo-concepts.mjs``docs/design/TAONIER_BRAND_LOGO_CONCEPTS_2026-05-13.md`
## image2 dry-run 带参考图时不要直接打印 data URL
- 现象:使用 VectorEngine `gpt-image-2-all` 生成带参考图的概念图时,如果 dry-run 直接打印完整请求体,参考图会被转成超长 `data:image/png;base64,...`,终端日志会被数百万字符淹没。
- 原因:生成请求支持 `image` 数组传入 data URL 参考图;dry-run 如果复用 live 请求体输出,就会把参考图内容完整打印。
- 处理:dry-run 输出摘要,只保留 `imageReferenceCount`、尺寸、模型和 prompt,不输出完整 base64。live 请求仍按实际需要传 `image` 数组。
- 验证:执行 `node scripts/generate-edutainment-tv-map-concepts.mjs --dry-run`,输出应只显示 `imageReferenceCount: 1`,不出现完整 base64。
- 关联:`scripts/generate-edutainment-tv-map-concepts.mjs``docs/design/【前端体验】寓教于乐电视端乐园地图入口概念图-2026-05-18.md`
## 忘记密码后仍提示手机号或密码错误先查认证快照同步
- 现象:用户通过“忘记密码”重设密码后,接口返回成功或页面进入登录态,但再次使用新密码登录仍提示“手机号或密码错误”;重启后还可能出现 `Bearer JWT 版本已失效`,日志里的 token version 与本地快照不一致。
@@ -273,8 +281,8 @@
## 寓教于乐作品和宝贝识物模板同时消失先查入口种子
- 现象:发现页“寓教于乐”分类下已发布的宝贝识物作品突然消失,同时创作界面模板选项中也看不到或无法正常展示 `宝贝识物`
- 原因:创作入口配置事实源已迁到 SpacetimeDB `creation_entry_type_config`;前端用 `baby-object-match` 入口可见性同时控制创作模板展示和发现页宝贝识物公开作品合入。若默认种子或后台配置缺少 `baby-object-match` 行,两条链路会一起被判定为不可见。
- 处理:确认 `server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs` 默认种子包含 `id=baby-object-match``title=宝贝识物``visible=true``open=true``sort_order=90`api-server 测试降级配置也要同步包含该类型。入口图片路径需指向真实存在资源,避免卡片图片 404。
- 原因:创作入口配置事实源已迁到 SpacetimeDB `creation_entry_type_config`;前端用 `baby-object-match` 入口可见性同时控制创作模板展示和发现页宝贝识物公开作品合入。若默认种子或后台配置缺少 `baby-object-match` 行,或旧迁移把该行纠偏成 `open=false` / `敬请期待`两条链路会一起被判定为不可见或不可创建
- 处理:确认 `server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs` 默认种子包含 `id=baby-object-match``title=宝贝识物``visible=true``open=true``sort_order=90`api-server 测试降级配置也要同步包含该类型。若已有本地库保留旧默认锁定记录,迁移只应把精确匹配历史默认 `敬请期待` 的宝贝识物行纠回 `可创建`,不要覆盖后台手动配置。入口图片路径需指向真实存在资源,避免卡片图片 404。
- 验证:运行 `cargo test -p module-runtime default_creation_entry_types_include_baby_object_match --manifest-path server-rs/Cargo.toml``cargo test -p api-server test_creation_entry_config_response_keeps_baby_object_match_visible --manifest-path server-rs/Cargo.toml``cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml``npm run test -- src/components/platform-entry/platformEntryCreationTypes.test.ts`
- 关联:`server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs``server-rs/crates/api-server/src/creation_entry_config.rs``docs/technical/NEW_WORK_ENTRY_CONFIG_2026-05-01.md`
@@ -422,6 +430,14 @@
- 验证:`spacetime list --server http://127.0.0.1:3101` 能看到目标库;重新发布不再使用无权限 identity。
- 关联:`scripts/dev.mjs``docs/technical/SPACETIMEDB_START_SH_PUBLISH_403_IDENTITY_FIX_2026-04-26.md`
## `dev:spacetime` 启动后 3101 又断开先查 publish 是否被 spacetime.json 干扰
- 现象:浏览器报 `Failed to initiate WebSocket connection`,目标为 `ws://127.0.0.1:3101/v1/database/<db>/subscribe`,端口检查发现 `3101` 没有长期监听;手动运行 `npm run dev:spacetime` 可看到 standalone 短暂启动后退出,发布阶段报 `No database target matches '<db>'`
- 原因:SpacetimeDB CLI 会读取仓库根目录 `spacetime.json`。如果本地发布命令没有显式 `--no-config`,CLI 可能按配置文件里的 target 解析数据库,覆盖脚本已传入的 `.env.local` 数据库名和 `--server`,导致 publish 失败;`dev.mjs` 捕获错误后会清理刚启动的 standalone,于是浏览器看到 3101 被拒绝连接。
- 处理:`scripts/dev.mjs` 的本地 publish 固定追加 `--no-config`,只使用脚本解析出的数据库名、module path 和实际 SpacetimeDB server。排查时前台运行 `npm run dev:spacetime -- --no-interactive`,若看到该错误,先确认脚本是否仍带 `--no-config`,再查 `.env.local` / `spacetime.local.json` 的数据库名。
- 验证:`npm run test -- scripts/dev.test.ts` 覆盖 publish 参数包含 `--no-config``npm run dev:spacetime -- --no-interactive``http://127.0.0.1:3101/v1/ping` 应保持 200。
- 关联:`scripts/dev.mjs``scripts/dev.test.ts``docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
## `npm run dev` 本地 SpacetimeDB 401 / 403 可重置默认 local 身份
- 现象:`npm run dev` 启动本地开发栈时,SpacetimeDB 在登录、发布或预检查阶段返回 `401` / `403`,清理后仍像在使用旧 token 或旧本地库。
@@ -1071,3 +1087,11 @@
- 处理:打开草稿时把持久化 `generationStatus=generating` 等同于生成中 notice,恢复对应玩法生成进度页;恢复计时使用作品摘要 `updatedAt` 推导 `startedAtMs`
- 验证:`npm test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "persisted generating"`
- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## 创作作品架消失先查入口配置 procedure 与本地库权限
- 现象:寓教于乐或创作中心下草稿 / 已发布作品突然整块消失,`GET /api/creation-entry/config` 返回 `502`details 中为 `No such procedure`
- 原因:本地 `.env.local``spacetime.local.json` 指向的 SpacetimeDB 库没有发布当前 `spacetime-module`,或当前 CLI 身份无权发布该库;例如旧 `xushi-p4wfr` 库缺 `get_creation_entry_config` 时,前端拿不到入口配置就不会渲染作品架。
- 处理:优先切换到拥有目标库权限的 SpacetimeDB 身份后重新运行 `npm run dev` 完成发布;若只是本地验证,可用 gitignored 的 `spacetime.local.json` 指向可发布的本地库。debug 构建的 `api-server` 对入口配置缺 procedure 会使用后端默认入口配置兜底,避免作品架因本地库漂移整块空白。
- 验证:`curl.exe -i http://127.0.0.1:8082/api/creation-entry/config` 返回 `200` 且包含 `baby-object-match`;前端草稿页作品架重新渲染。
- 关联:`server-rs/crates/api-server/src/state.rs``server-rs/crates/api-server/src/creation_entry_config.rs``docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
@@ -0,0 +1,36 @@
# 寓教于乐电视端乐园地图入口概念图
更新时间:`2026-05-18`
## 背景
寓教于乐板块需要一个面向电视端 / 横屏大屏的图形化入口,整体感觉接近主题乐园地图,但必须保留 Genarrative 现有的明亮卡通绘本插画风,不借用任何真实品牌乐园或版权角色。
## 目标
- 远看像一个完整乐园地图,近看能分辨每个玩法入口。
- 入口区域清晰分区,后续可以叠加焦点框、按钮和中文标题。
- 中心和下方保留足够留白,适合遥控器焦点、儿童角色或主推荐位。
- 风格保持和寓教于乐现有草地舞台资源一致。
## 本次概念方向
1. 环形乐园岛:中央草地广场 + 外圈入口环路。
2. 展开绘本地图:横向展开的大绘本页,左右页自然衔接。
3. 云朵空中岛:多个浮岛通过彩虹桥和云朵步道连接。
4. 草地舞台地图:更接近实际运行态的横屏草地入口首屏。
## 推荐方向
优先推荐 `草地舞台地图` 作为后续落地主方向,因为它与现有寓教于乐草地舞台最接近,且中央下方留白最适合后续叠加交互焦点。
## 生图脚本
- 生成脚本:`scripts/generate-edutainment-tv-map-concepts.mjs`
- 默认尺寸:`2048x1152`
- 风格参考:`public/child-motion-demo/picture-book-grass-stage.png`
- 输出目录:`output/imagegen/edutainment-tv-map-entry-concepts-20260518/`
## 说明
本次结果是设计概念稿,不直接进入 `public/` 正式资源目录。后续若要继续细化,可在同一脚本里增加新的横屏变体,并保持“不写文字、不露品牌 IP、绘本插画风”这三条底线。
@@ -49,6 +49,8 @@
热身结束后展示“开始游戏”按钮,用户点击后进入宝贝识物首关本地 Demo。该入口只用于热身关后的本地体验验证;正式平台体验仍必须通过“宝贝识物”创作模板发布后,在寓教于乐板块进入。
发现页的寓教于乐频道同时提供独立热身关入口,用户可直接进入 `/child-motion-demo`
### 3.3 固定流程顺序
热身关必须按照以下顺序执行:
@@ -45,6 +45,10 @@ npm run dev:api-server
后端日志默认写入 `logs/api-server/`。后端 API smoke 使用 `npm run dev:api-server` 并检查 `/healthz`;不要使用旧 `api-server:maincloud` 或任何 `GENARRATIVE_SPACETIME_MAINCLOUD_*` 口径。
如果本地 `GET /api/creation-entry/config` 返回 `No such procedure`,通常是 `.env.local` 指向的 SpacetimeDB 库还没有发布当前 `spacetime-module`,或当前 CLI 身份无权发布该库。debug 构建的 `api-server` 会临时使用后端默认入口配置兜底,避免创作作品架整块消失;正式修复仍应切换到拥有目标库权限的 SpacetimeDB 身份后重新运行 `npm run dev` 完成发布,或用 gitignored 的 `spacetime.local.json` 指向可发布的本地库。
本地 `npm run dev:spacetime` 发布模块时必须显式忽略仓库根目录的 `spacetime.json`,由脚本固定追加 `--no-config` 并使用命令参数里传入的数据库名和 `--server http://127.0.0.1:3101`。否则 CLI 可能把发布目标改写到配置文件里的其他数据库,导致 `dev:spacetime` 启动后又因发布失败自动退出,浏览器随后会在 `ws://127.0.0.1:3101/v1/database/.../subscribe` 看到连接拒绝。
查看本地 Rust / SpacetimeDB 日志:
```bash
@@ -189,9 +189,9 @@
当前包含:
- `baby-object-match`:宝贝识物当前入口状态为 `visible=true``open=false`,创作 Tab 展示为“敬请期待”,进入创作与发布链路;历史资产接口为 `/api/creation/edutainment/baby-object-match/assets`,后续重新开放时继续复用该资产链路
- `baby-object-match`:宝贝识物当前入口状态为 `visible=true``open=true`,创作 Tab 展示为“可创建”,进入宝贝识物创作、生成、结果页、试玩和发布链路;资产生成接口为 `/api/creation/edutainment/baby-object-match/assets`。入口关闭只允许通过 SpacetimeDB / 后台入口配置显式调整,默认种子和 debug 兜底都必须保持可创建
- `baby-love-drawing`:宝贝爱画本地 demo,魔法生成接口为 `/api/creation/edutainment/baby-love-drawing/magic`
- `child-motion-demo`:儿童动作识别热身关。真实动作数据来自 mocap WebSocket,不要把浏览器摄像头视频流当作主动作数据源。
- `child-motion-demo`:儿童动作识别热身关。真实动作数据来自 mocap WebSocket,不要把浏览器摄像头视频流当作主动作数据源;发现页的寓教于乐频道同时提供独立热身关入口,点击后进入 `/child-motion-demo`
## 创意互动 Agent
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<rect width="1024" height="1024" rx="160" fill="#101010"/>
<rect x="270" y="214" width="484" height="606" rx="228" fill="#fff5df"/>
<circle cx="650" cy="330" r="112" fill="#d77750"/>
<circle cx="676" cy="354" r="86" fill="#fff5df"/>
<circle cx="430" cy="470" r="34" fill="#121212"/>
<circle cx="590" cy="470" r="34" fill="#121212"/>
<path d="M512 552L540 590L578 618L540 646L512 684L484 646L446 618L484 590Z" fill="#ffd25d"/>
<rect x="360" y="742" width="304" height="48" rx="24" fill="#d77750"/>
</svg>

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<rect width="1024" height="1024" rx="160" fill="#101418"/>
<path d="M236 504C236 294 382 176 512 176C676 176 788 322 788 504C788 702 648 832 512 832C372 832 236 702 236 504Z" fill="#fff5df"/>
<circle cx="618" cy="326" r="72" fill="#101418"/>
<circle cx="618" cy="326" r="34" fill="#ffd25d"/>
<circle cx="438" cy="488" r="30" fill="#101418"/>
<rect x="506" y="548" width="126" height="46" rx="23" fill="#d77750"/>
<path d="M512 624L536 658L570 682L536 706L512 740L488 706L454 682L488 658Z" fill="#ffd25d"/>
</svg>

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<rect width="1024" height="1024" rx="160" fill="#17110e"/>
<rect x="254" y="254" width="516" height="516" rx="190" fill="#d77750"/>
<rect x="330" y="320" width="364" height="386" rx="140" fill="#fff5df"/>
<circle cx="440" cy="478" r="30" fill="#17110e"/>
<circle cx="584" cy="478" r="30" fill="#17110e"/>
<rect x="458" y="594" width="108" height="44" rx="22" fill="#d77750"/>
<circle cx="512" cy="254" r="54" fill="#ffd25d"/>
<circle cx="512" cy="254" r="24" fill="#17110e"/>
</svg>

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<rect width="1024" height="1024" rx="160" fill="#101418"/>
<path d="M628 217L759 272L814 402L759 724L628 779H396L265 724L210 402L265 272L396 217Z" fill="#fff5df"/>
<circle cx="512" cy="398" r="126" fill="#101418"/>
<circle cx="512" cy="398" r="62" fill="#ffd25d"/>
<circle cx="390" cy="570" r="30" fill="#101418"/>
<circle cx="634" cy="570" r="30" fill="#101418"/>
<rect x="380" y="704" width="264" height="54" rx="27" fill="#31c7a9"/>
<rect x="318" y="268" width="142" height="58" rx="29" fill="#ff6a5f"/>
</svg>

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<rect width="1024" height="1024" rx="160" fill="#111111"/>
<rect x="340" y="188" width="344" height="648" rx="172" fill="#fff5df"/>
<circle cx="512" cy="336" r="112" fill="#d77750"/>
<rect x="390" y="442" width="244" height="280" rx="122" fill="#111111"/>
<circle cx="512" cy="582" r="58" fill="#ffd25d"/>
<circle cx="432" cy="332" r="24" fill="#121212"/>
<circle cx="592" cy="332" r="24" fill="#121212"/>
<rect x="404" y="782" width="216" height="60" rx="30" fill="#fff5df"/>
</svg>

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<rect width="1024" height="1024" rx="160" fill="#101010"/>
<rect x="286" y="300" width="452" height="456" rx="150" fill="#fff5df"/>
<circle cx="286" cy="530" r="88" fill="#101010"/>
<circle cx="738" cy="530" r="88" fill="#101010"/>
<circle cx="512" cy="300" r="76" fill="#ffd25d"/>
<circle cx="430" cy="474" r="24" fill="#101010"/>
<circle cx="594" cy="474" r="24" fill="#101010"/>
<path d="M512 536L540 576L580 604L540 632L512 672L484 632L444 604L484 576Z" fill="#101010"/>
<path d="M512 570L526 590L546 604L526 618L512 638L498 618L478 604L498 590Z" fill="#ffd25d"/>
<rect x="360" y="744" width="304" height="54" rx="27" fill="#d77750"/>
</svg>

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 KiB

Some files were not shown because too many files have changed in this diff Show More