diff --git a/.hermes/shared-memory/decision-log.md b/.hermes/shared-memory/decision-log.md index 0ca4927b..fcc727ec 100644 --- a/.hermes/shared-memory/decision-log.md +++ b/.hermes/shared-memory/decision-log.md @@ -1305,3 +1305,11 @@ - 影响范围:拼消消工作台 payload、`shared-contracts` / `packages/shared` 契约、api-server 生成编排、SpacetimeDB session/work snapshot、文档与生成进度展示。 - 验证方式:`npm run spacetime:generate`、`npm run check:encoding`、`npm run check:server-rs-ddd`、`cargo test -p module-puzzle-clear`、`cargo test -p spacetime-client puzzle_clear -- --nocapture`、`npm run test -- src/components/puzzle-clear-creation/PuzzleClearWorkspace.test.tsx src/services/miniGameDraftGenerationProgress.test.ts src/routing/appPageRoutes.test.ts src/services/publicWorkCode.test.ts`。 - 关联文档:`docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md`、`docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。 + +## 2026-06-06 统一创作页表头跟随后台玩法入口配置 + +- 背景:统一创作页长期使用固定表头 `想做个什么玩法?`,导致跳一跳等玩法希望按自身语义展示标题时只能改前端或默认契约。 +- 决策:`creationTypes[].unifiedCreationSpec.title` 继续作为统一创作页表头传输字段,但默认值改为玩法入口中文名称;读取和保存时如果发现旧公共表头或代码默认中文名,则归一为当前入口 `title`,后台手动配置成其它标题时保留自定义值。 +- 影响范围:`shared-contracts` 默认 spec、`module-runtime` 入口配置响应、`spacetime-module` 后台保存归一化、后台入口开关页摘要和前端 fallback spec。 +- 验证方式:`GET /api/creation-entry/config` 中各玩法 `unifiedCreationSpec.title` 默认等于入口中文名;后台修改入口名称后,未自定义表头的统一创作页跟随变化。 +- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。 diff --git a/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.test.tsx b/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.test.tsx index da9362d7..adad5145 100644 --- a/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.test.tsx +++ b/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.test.tsx @@ -27,7 +27,7 @@ vi.mock('../api/adminApiClient', () => ({ const puzzleSpec: UnifiedCreationSpecPayload = { playId: 'puzzle', - title: '想做个什么玩法?', + title: '拼图', workspaceStage: 'puzzle-agent-workspace', generationStage: 'puzzle-generating', resultStage: 'puzzle-result', @@ -88,6 +88,9 @@ test('创作入口后台展示并保存统一创作契约', async () => { await screen.findByText('pictureDescription'); expect(container.querySelector('.admin-subsection .admin-info-list')).not.toBeNull(); + expect( + container.querySelector('.admin-subsection .admin-info-list')?.textContent, + ).toContain('拼图'); expect(container.querySelector('.admin-panel .admin-panel')).toBeNull(); expect(container.querySelector('.admin-muted')).toBeNull(); diff --git a/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.tsx b/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.tsx index 9de00709..9390f0f1 100644 --- a/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.tsx +++ b/apps/admin-web/src/pages/AdminCreationEntrySwitchPage.tsx @@ -707,6 +707,10 @@ function UnifiedCreationSpecSummary({specJson}: {specJson: string}) {
玩法
{parsed.spec.playId}
+
+
表头
+
{parsed.spec.title}
+
阶段
diff --git a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md index 9f950b43..4ed742ed 100644 --- a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md +++ b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md @@ -14,7 +14,7 @@ 创作恢复参数只保留 `sessionId`、`profileId`、`draftId`、`workId` 这四个私有 query。它们只允许在同一条创作链路的结果页、生成页、工作台之间保留;切到首页、公开作品详情、runtime 或另一条玩法链路时必须清掉。生成页等待时间统一以生成状态里的 `startedAtMs` 为准;创建该状态时优先使用后端 session 下发的时间戳,作品摘要里的 `updatedAt` 仍只用于排序与摘要展示,不作为前端自行推导业务状态的真相。 -统一创作入口覆盖当前可进入创作链路的已有模板:`rpg`、`big-fish`、`puzzle`、`match3d`、`jump-hop`、`wooden-fish`、`square-hole`、`bark-battle`、`visual-novel`、`baby-object-match` 和 `creative-agent`;`airp` 仍是未开放占位,不作为当前统一创作链路目标。拼图、抓大鹅、跳一跳和敲木鱼在前端继续经过 `UnifiedCreationWorkspace` 和 `UnifiedGenerationPage`:`UnifiedCreationWorkspace` 作为平台壳依赖的统一创作编排层,再内部调用 `src/components/unified-creation/workspaces/` 下的 `PuzzleCreationWorkspace`、`Match3DCreationWorkspace`、`JumpHopCreationWorkspace` 和 `WoodenFishCreationWorkspace`。其它已有模板由平台壳用 `UnifiedCreationPage` 包住既有工作台,复用统一标题栏、返回入口、页面级纵向滚动和隐藏字段契约,同时保留各玩法自己的表单、草稿恢复和后续编排。创作页字段清单由后端在 `GET /api/creation-entry/config` 的 `creationTypes[].unifiedCreationSpec` 下发,前端仅在该扩展位缺失时回退到本地默认 spec;字段类型只保留 `text`、`select`、`image`、`audio`。`UnifiedCreationPage` 不在 UI 中额外展示字段说明 chip,也不在右上角显示内部 `playId`、模板 ID 或工作台阶段名;竖屏移动端必须能从标题、表单一路滑到提交按钮。各玩法工作台负责渲染真实输入控件、上传、历史素材、校验和提交,但返回按钮只保留在统一页头,工作台内部不再重复渲染。暗色创作进度卡片位于 `platform-remap-surface` 内时,必须用组件专属 class 覆盖浅色主题 remap,确保白字、浅色边框和进度条底色不会被全局规则改成深色;不要只依赖通用 `text-white*` 类。敲木鱼的音效和功德词条面板不得放进独立内部滚动容器,移动端应跟随页面自然滚动展开。生成页统一展示阶段、当前步骤、总进度、错误和重试动作。 +统一创作入口覆盖当前可进入创作链路的已有模板:`rpg`、`big-fish`、`puzzle`、`match3d`、`jump-hop`、`wooden-fish`、`square-hole`、`bark-battle`、`visual-novel`、`baby-object-match` 和 `creative-agent`;`airp` 仍是未开放占位,不作为当前统一创作链路目标。拼图、抓大鹅、跳一跳和敲木鱼在前端继续经过 `UnifiedCreationWorkspace` 和 `UnifiedGenerationPage`:`UnifiedCreationWorkspace` 作为平台壳依赖的统一创作编排层,再内部调用 `src/components/unified-creation/workspaces/` 下的 `PuzzleCreationWorkspace`、`Match3DCreationWorkspace`、`JumpHopCreationWorkspace` 和 `WoodenFishCreationWorkspace`。其它已有模板由平台壳用 `UnifiedCreationPage` 包住既有工作台,复用统一标题栏、返回入口、页面级纵向滚动和隐藏字段契约,同时保留各玩法自己的表单、草稿恢复和后续编排。创作页字段清单和表头由后端在 `GET /api/creation-entry/config` 的 `creationTypes[].unifiedCreationSpec` 下发,前端仅在该扩展位缺失时回退到本地默认 spec;字段类型只保留 `text`、`select`、`image`、`audio`。统一创作页表头属于后台玩法入口配置,默认使用同一入口的中文名称;旧库里仍持久化为 `想做个什么玩法?` 或代码默认中文名的 spec title,会在读取和保存时归一到当前入口名称,后台单独写成其它标题时保留该自定义值。`UnifiedCreationPage` 不在 UI 中额外展示字段说明 chip,也不在右上角显示内部 `playId`、模板 ID 或工作台阶段名;竖屏移动端必须能从标题、表单一路滑到提交按钮。各玩法工作台负责渲染真实输入控件、上传、历史素材、校验和提交,但返回按钮只保留在统一页头,工作台内部不再重复渲染。暗色创作进度卡片位于 `platform-remap-surface` 内时,必须用组件专属 class 覆盖浅色主题 remap,确保白字、浅色边框和进度条底色不会被全局规则改成深色;不要只依赖通用 `text-white*` 类。敲木鱼的音效和功德词条面板不得放进独立内部滚动容器,移动端应跟随页面自然滚动展开。生成页统一展示阶段、当前步骤、总进度、错误和重试动作。 创作表单提交前的泥点余额前置校验只允许用独立弹窗提示失败原因,不得把用户退回创作入口或玩法模板列表,也不得清空当前表单状态。当前适用拼图、抓大鹅和汪汪声浪等会在前端提交前校验泥点的生成入口;余额不足、余额读取失败都应停留在当前工作台,由用户关闭提示后继续编辑或自行补足泥点。 diff --git a/server-rs/crates/module-runtime/src/application.rs b/server-rs/crates/module-runtime/src/application.rs index 2c578dd9..cae5c119 100644 --- a/server-rs/crates/module-runtime/src/application.rs +++ b/server-rs/crates/module-runtime/src/application.rs @@ -12,7 +12,7 @@ use crate::format_utc_micros; use shared_contracts::creation_entry_config::{ CreationEntryConfigResponse, CreationEntryEventBannerResponse, CreationEntryStartCardResponse, CreationEntryTypeModalResponse, CreationEntryTypeResponse, - encode_unified_creation_spec_response, resolve_unified_creation_spec_response, + encode_unified_creation_spec_response, resolve_unified_creation_spec_response_with_entry_title, }; /// 将创作入口领域快照转换为前后台共享的 HTTP 契约响应。 @@ -45,8 +45,9 @@ pub fn build_creation_entry_config_response( .creation_types .into_iter() .map(|item| { - let unified_creation_spec = resolve_unified_creation_spec_response( + let unified_creation_spec = resolve_unified_creation_spec_response_with_entry_title( item.id.as_str(), + item.title.as_str(), item.unified_creation_spec_json.as_deref(), ); CreationEntryTypeResponse { @@ -161,10 +162,9 @@ fn normalize_creation_entry_announcement_banner_value( ); } - let banner = serde_json::from_value::(Value::Object( - object.clone(), - )) - .map_err(|error| format!("第 {} 条公告对象非法:{error}", index + 1))?; + let banner = + serde_json::from_value::(Value::Object(object.clone())) + .map_err(|error| format!("第 {} 条公告对象非法:{error}", index + 1))?; normalize_creation_entry_event_banner_response(index, banner) } @@ -243,8 +243,8 @@ pub fn resolve_creation_entry_event_banner_responses( banners } .into_iter() - .map(build_creation_entry_event_banner_response) - .collect() + .map(build_creation_entry_event_banner_response) + .collect() } /// 把领域公告快照转换为 HTTP 响应字段。 @@ -332,10 +332,7 @@ fn normalize_banner_html_code( } let lower_html_code = html_code.to_ascii_lowercase(); if lower_html_code.contains(" Option { let (workspace_stage, generation_stage, result_stage, fields) = match play_id { @@ -172,18 +173,8 @@ pub fn build_phase1_unified_creation_spec(play_id: &str) -> Option Option Option Option<&'static str> { + match play_id { + "rpg" => Some("文字冒险"), + "big-fish" => Some("摸鱼"), + "puzzle" => Some("拼图"), + "match3d" => Some("抓大鹅"), + "jump-hop" => Some("跳一跳"), + "wooden-fish" => Some("敲木鱼"), + "square-hole" => Some("方洞"), + "bark-battle" => Some("汪汪声浪"), + "visual-novel" => Some("视觉小说"), + "baby-object-match" => Some("宝贝识物"), + "creative-agent" => Some("智能体创作"), + _ => None, + } +} + +pub fn normalize_unified_creation_spec_title_for_entry( + play_id: &str, + entry_title: &str, + mut spec: UnifiedCreationSpecResponse, +) -> UnifiedCreationSpecResponse { + let entry_title = entry_title.trim(); + if entry_title.is_empty() { + return spec; + } + + let spec_title = spec.title.trim(); + let default_title = default_unified_creation_title(play_id).unwrap_or_default(); + if spec_title == LEGACY_UNIFIED_CREATION_TITLE + || (!default_title.is_empty() && spec_title == default_title) + { + spec.title = entry_title.to_string(); + } + spec +} + pub fn validate_unified_creation_spec_response( spec: &UnifiedCreationSpecResponse, ) -> Result<(), String> { @@ -311,10 +339,27 @@ pub fn resolve_unified_creation_spec_response( play_id: &str, value: Option<&str>, ) -> Option { - match value { + resolve_unified_creation_spec_response_with_entry_title( + play_id, + default_unified_creation_title(play_id).unwrap_or_default(), + value, + ) +} + +pub fn resolve_unified_creation_spec_response_with_entry_title( + play_id: &str, + entry_title: &str, + value: Option<&str>, +) -> Option { + let spec = match value { Some(raw) => decode_unified_creation_spec_response(raw).ok(), None => build_phase1_unified_creation_spec(play_id), - } + }?; + Some(normalize_unified_creation_spec_title_for_entry( + play_id, + entry_title, + spec, + )) } fn unified_creation_field( @@ -338,10 +383,12 @@ mod tests { #[test] fn phase1_unified_creation_specs_cover_existing_templates() { let puzzle = build_phase1_unified_creation_spec("puzzle").expect("puzzle spec"); + assert_eq!(puzzle.title, "拼图"); assert_eq!(puzzle.fields[0].id, "pictureDescription"); assert_eq!(puzzle.fields[1].kind, "image"); let match3d = build_phase1_unified_creation_spec("match3d").expect("match3d spec"); + assert_eq!(match3d.title, "抓大鹅"); assert_eq!( match3d .fields @@ -352,6 +399,7 @@ mod tests { ); let jump_hop = build_phase1_unified_creation_spec("jump-hop").expect("jump-hop spec"); + assert_eq!(jump_hop.title, "跳一跳"); assert!( jump_hop .fields @@ -389,6 +437,45 @@ mod tests { ); } + #[test] + fn unified_creation_spec_title_can_fallback_to_entry_title() { + let raw = r#"{ + "playId": "puzzle", + "title": "想做个什么玩法?", + "workspaceStage": "puzzle-agent-workspace", + "generationStage": "puzzle-generating", + "resultStage": "puzzle-result", + "fields": [ + { + "id": "pictureDescription", + "kind": "text", + "label": "画面描述", + "required": true + } + ] + }"#; + + let spec = resolve_unified_creation_spec_response_with_entry_title( + "puzzle", + "定制拼图", + Some(raw), + ) + .expect("puzzle spec"); + + assert_eq!(spec.title, "定制拼图"); + } + + #[test] + fn unified_creation_spec_title_keeps_admin_custom_copy() { + let mut spec = build_phase1_unified_creation_spec("jump-hop").expect("jump-hop spec"); + spec.title = "你的跳一跳是...".to_string(); + + let normalized = + normalize_unified_creation_spec_title_for_entry("jump-hop", "跳一跳", spec); + + assert_eq!(normalized.title, "你的跳一跳是..."); + } + #[test] fn creation_entry_event_banner_defaults_to_structured_render_mode() { let banner = serde_json::from_str::( diff --git a/server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs b/server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs index 232fc1e3..0dbf9b72 100644 --- a/server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs +++ b/server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs @@ -122,7 +122,8 @@ fn upsert_creation_entry_type_config_in_tx( if input.title.trim().is_empty() { return Err("入口标题不能为空".to_string()); } - let unified_creation_spec_json = normalize_unified_creation_spec_json(&id, &input)?; + let unified_creation_spec_json = + normalize_unified_creation_spec_json(&id, input.title.trim(), &input)?; let row = CreationEntryTypeConfig { id: id.clone(), title: input.title.trim().to_string(), @@ -297,6 +298,7 @@ fn seed_creation_entry_config_if_missing(ctx: &ReducerContext) { migrate_baby_object_match_entry_from_old_coming_soon_default(ctx, now); migrate_wooden_fish_entry_from_old_puzzle_image_default(ctx, now); migrate_jump_hop_entry_from_old_puzzle_default(ctx, now); + migrate_unified_creation_titles_to_entry_defaults(ctx, now); } fn migrate_rpg_entry_from_old_hidden_default(ctx: &ReducerContext, now: Timestamp) { @@ -477,6 +479,51 @@ fn migrate_jump_hop_entry_from_old_puzzle_default(ctx: &ReducerContext, now: Tim }); } +fn migrate_unified_creation_titles_to_entry_defaults(ctx: &ReducerContext, now: Timestamp) { + let rows = ctx + .db + .creation_entry_type_config() + .iter() + .collect::>(); + for row in rows { + let Some(raw_spec_json) = row.unified_creation_spec_json.as_deref() else { + continue; + }; + let Ok(spec) = + shared_contracts::creation_entry_config::decode_unified_creation_spec_response( + raw_spec_json, + ) + else { + continue; + }; + let normalized = + shared_contracts::creation_entry_config::normalize_unified_creation_spec_title_for_entry( + &row.id, + &row.title, + spec.clone(), + ); + if normalized.title == spec.title { + continue; + } + let Ok(unified_creation_spec_json) = + shared_contracts::creation_entry_config::encode_unified_creation_spec_response( + &normalized, + ) + else { + continue; + }; + + ctx.db + .creation_entry_type_config() + .id() + .update(CreationEntryTypeConfig { + unified_creation_spec_json: Some(unified_creation_spec_json), + updated_at: now, + ..row + }); + } +} + fn default_creation_entry_type_configs(now: Timestamp) -> Vec { module_runtime::default_creation_entry_type_snapshots(now.to_micros_since_unix_epoch()) .into_iter() @@ -500,6 +547,7 @@ fn default_creation_entry_type_configs(now: Timestamp) -> Vec Result, String> { let Some(spec_json) = input.unified_creation_spec_json.as_deref() else { @@ -512,6 +560,12 @@ fn normalize_unified_creation_spec_json( let spec = shared_contracts::creation_entry_config::decode_unified_creation_spec_response(normalized)?; + let spec = + shared_contracts::creation_entry_config::normalize_unified_creation_spec_title_for_entry( + id, + entry_title, + spec, + ); shared_contracts::creation_entry_config::validate_unified_creation_spec_for_play(id, &spec)?; shared_contracts::creation_entry_config::encode_unified_creation_spec_response(&spec).map(Some) } diff --git a/src/components/unified-creation/unifiedCreationSpecs.test.ts b/src/components/unified-creation/unifiedCreationSpecs.test.ts index 8e6daa98..611e294d 100644 --- a/src/components/unified-creation/unifiedCreationSpecs.test.ts +++ b/src/components/unified-creation/unifiedCreationSpecs.test.ts @@ -36,41 +36,49 @@ describe('unified creation specs', () => { test('主要链路都映射到统一创作、生成、结果阶段', () => { expect(getUnifiedCreationSpec('rpg')).toMatchObject({ + title: '文字冒险', workspaceStage: 'agent-workspace', generationStage: 'custom-world-generating', resultStage: 'custom-world-result', }); expect(getUnifiedCreationSpec('puzzle')).toMatchObject({ + title: '拼图', workspaceStage: 'puzzle-agent-workspace', generationStage: 'puzzle-generating', resultStage: 'puzzle-result', }); expect(getUnifiedCreationSpec('match3d')).toMatchObject({ + title: '抓大鹅', workspaceStage: 'match3d-agent-workspace', generationStage: 'match3d-generating', resultStage: 'match3d-result', }); expect(getUnifiedCreationSpec('jump-hop')).toMatchObject({ + title: '跳一跳', workspaceStage: 'jump-hop-workspace', generationStage: 'jump-hop-generating', resultStage: 'jump-hop-result', }); expect(getUnifiedCreationSpec('wooden-fish')).toMatchObject({ + title: '敲木鱼', workspaceStage: 'wooden-fish-workspace', generationStage: 'wooden-fish-generating', resultStage: 'wooden-fish-result', }); expect(getUnifiedCreationSpec('bark-battle')).toMatchObject({ + title: '汪汪声浪', workspaceStage: 'bark-battle-workspace', generationStage: 'bark-battle-generating', resultStage: 'bark-battle-result', }); expect(getUnifiedCreationSpec('visual-novel')).toMatchObject({ + title: '视觉小说', workspaceStage: 'visual-novel-agent-workspace', generationStage: 'visual-novel-generating', resultStage: 'visual-novel-result', }); expect(getUnifiedCreationSpec('baby-object-match')).toMatchObject({ + title: '宝贝识物', workspaceStage: 'baby-object-match-workspace', generationStage: 'baby-object-match-generating', resultStage: 'baby-object-match-result', diff --git a/src/components/unified-creation/unifiedCreationSpecs.ts b/src/components/unified-creation/unifiedCreationSpecs.ts index 3fb7c204..3b750212 100644 --- a/src/components/unified-creation/unifiedCreationSpecs.ts +++ b/src/components/unified-creation/unifiedCreationSpecs.ts @@ -27,7 +27,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< > = { rpg: { playId: 'rpg', - title: '想做个什么玩法?', + title: '文字冒险', workspaceStage: 'agent-workspace', generationStage: 'custom-world-generating', resultStage: 'custom-world-result', @@ -42,7 +42,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'big-fish': { playId: 'big-fish', - title: '想做个什么玩法?', + title: '摸鱼', workspaceStage: 'big-fish-agent-workspace', generationStage: 'big-fish-generating', resultStage: 'big-fish-result', @@ -57,7 +57,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, puzzle: { playId: 'puzzle', - title: '想做个什么玩法?', + title: '拼图', workspaceStage: 'puzzle-agent-workspace', generationStage: 'puzzle-generating', resultStage: 'puzzle-result', @@ -84,7 +84,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, match3d: { playId: 'match3d', - title: '想做个什么玩法?', + title: '抓大鹅', workspaceStage: 'match3d-agent-workspace', generationStage: 'match3d-generating', resultStage: 'match3d-result', @@ -105,7 +105,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'jump-hop': { playId: 'jump-hop', - title: '想做个什么玩法?', + title: '跳一跳', workspaceStage: 'jump-hop-workspace', generationStage: 'jump-hop-generating', resultStage: 'jump-hop-result', @@ -120,7 +120,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'wooden-fish': { playId: 'wooden-fish', - title: '想做个什么玩法?', + title: '敲木鱼', workspaceStage: 'wooden-fish-workspace', generationStage: 'wooden-fish-generating', resultStage: 'wooden-fish-result', @@ -153,7 +153,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'square-hole': { playId: 'square-hole', - title: '想做个什么玩法?', + title: '方洞', workspaceStage: 'square-hole-agent-workspace', generationStage: 'square-hole-generating', resultStage: 'square-hole-result', @@ -168,7 +168,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'bark-battle': { playId: 'bark-battle', - title: '想做个什么玩法?', + title: '汪汪声浪', workspaceStage: 'bark-battle-workspace', generationStage: 'bark-battle-generating', resultStage: 'bark-battle-result', @@ -213,7 +213,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'visual-novel': { playId: 'visual-novel', - title: '想做个什么玩法?', + title: '视觉小说', workspaceStage: 'visual-novel-agent-workspace', generationStage: 'visual-novel-generating', resultStage: 'visual-novel-result', @@ -234,7 +234,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'baby-object-match': { playId: 'baby-object-match', - title: '想做个什么玩法?', + title: '宝贝识物', workspaceStage: 'baby-object-match-workspace', generationStage: 'baby-object-match-generating', resultStage: 'baby-object-match-result', @@ -255,7 +255,7 @@ const FALLBACK_UNIFIED_CREATION_SPECS: Record< }, 'creative-agent': { playId: 'creative-agent', - title: '想做个什么玩法?', + title: '智能体创作', workspaceStage: 'creative-agent-workspace', generationStage: 'puzzle-generating', resultStage: 'puzzle-result', diff --git a/src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx b/src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx index a36992ad..856dbc5b 100644 --- a/src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx +++ b/src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx @@ -78,7 +78,7 @@ test('match3d workspace submits derived entry form payload instead of agent chat />, ); - expect(screen.getByText('想做个什么玩法?')).toBeTruthy(); + expect(screen.getByText('抓大鹅')).toBeTruthy(); expect(screen.getByLabelText('想做一个什么题材的抓大鹅?')).toBeTruthy(); expect(screen.queryByText('2D素材风格')).toBeNull(); expect(screen.queryByRole('button', { name: '扁平图标' })).toBeNull(); @@ -130,7 +130,7 @@ test('match3d workspace can defer visible chrome to the unified creation page', expect(workspace?.className).not.toContain('h-full'); expect(workspace?.className).not.toContain('overflow-hidden'); expect(workspace?.className).not.toContain('platform-remap-surface'); - expect(screen.queryByRole('heading', { name: '想做个什么玩法?' })).toBeNull(); + expect(screen.queryByRole('heading', { name: '抓大鹅' })).toBeNull(); const themeInput = screen.getByLabelText('想做一个什么题材的抓大鹅?'); expect(themeInput).toBeTruthy(); expect(themeInput.className).not.toContain('h-full'); diff --git a/src/components/unified-creation/workspaces/Match3DCreationWorkspace.tsx b/src/components/unified-creation/workspaces/Match3DCreationWorkspace.tsx index 916dd9eb..d9fb2864 100644 --- a/src/components/unified-creation/workspaces/Match3DCreationWorkspace.tsx +++ b/src/components/unified-creation/workspaces/Match3DCreationWorkspace.tsx @@ -115,7 +115,7 @@ export function Match3DCreationWorkspace({ onCreateFromForm, initialFormPayload = null, showBackButton = true, - title = '想做个什么玩法?', + title = '抓大鹅', unifiedChrome = false, }: Match3DCreationWorkspaceProps) { const [formState, setFormState] = useState(() => diff --git a/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx b/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx index 0ed7ebc0..350e7258 100644 --- a/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx +++ b/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx @@ -188,7 +188,7 @@ test('puzzle workspace submits the work form instead of agent chat', () => { expect(screen.queryByLabelText('作品名称')).toBeNull(); expect(screen.queryByLabelText('作品描述')).toBeNull(); - expect(screen.getByText('想做个什么玩法?')).toBeTruthy(); + expect(screen.getByText('拼图')).toBeTruthy(); expect(screen.queryByText('try')).toBeNull(); expect(screen.queryByText('Template')).toBeNull(); @@ -238,7 +238,7 @@ test('puzzle workspace can defer visible chrome to the unified creation page', ( expect(workspace?.className).not.toContain('platform-remap-surface'); expect(imagePanel?.className).toContain('flex-none'); expect(imagePanel?.className).not.toContain('flex-1'); - expect(screen.queryByRole('heading', { name: '想做个什么玩法?' })).toBeNull(); + expect(screen.queryByRole('heading', { name: '拼图' })).toBeNull(); expect(screen.getByLabelText('画面描述')).toBeTruthy(); }); diff --git a/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.tsx b/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.tsx index 74c184a6..024ad826 100644 --- a/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.tsx +++ b/src/components/unified-creation/workspaces/PuzzleCreationWorkspace.tsx @@ -246,7 +246,7 @@ export function PuzzleCreationWorkspace({ onAutoSaveForm, initialFormPayload = null, showBackButton = true, - title = '想做个什么玩法?', + title = '拼图', unifiedChrome = false, }: PuzzleCreationWorkspaceProps) { const [formState, setFormState] = useState(() =>