From 1d3c062cf9d9c875e769377f1d8b989f905045fd Mon Sep 17 00:00:00 2001 From: kdletters Date: Tue, 25 Aug 2026 12:09:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D188=E5=92=8C191=E5=90=88?= =?UTF-8?q?=E5=85=A5=E5=90=8E=E7=9A=84=E5=B7=A5=E4=BD=9C=E5=8F=B0=E5=9B=9E?= =?UTF-8?q?=E5=BD=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复右侧 Codex 对话定位和移动端布局 恢复资源页播放入口并保持运行页导航语义 修正资源红点与发送按钮的可访问名称测试 更新项目开发工作台 PRD 约束 --- .../ProjectSupervisorView.tsx | 9 +- apps/ai-game-creator-shell/src/styles.css | 108 +++++++++++++++--- .../src/view/project-development/index.tsx | 42 +++---- .../tests/appSurface/home.suite.ts | 4 +- .../appSurface/project-development.suite.ts | 36 ++++-- ...AI游戏创作】项目开发工作台PRD-2026-07-20.md | 2 +- 6 files changed, 143 insertions(+), 58 deletions(-) diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index d7f3dbd3b..3dde6bd9e 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -99,9 +99,14 @@ export function ProjectSupervisorView({ onPlanGddDecision, ...runtimePanelProps }: ProjectSupervisorViewProps) { + const submitLabel = needsUserInput + ? '等待回答' + : runtimePanelProps.controlBusy + ? '思考中' + : '发送'; return (
@@ -249,6 +254,8 @@ export function ProjectSupervisorView({ />
- {mode === 'run' ? ( - - ) : null} + {mode === 'resources' && !assetCanvasRoute && !resourceEditorRoute && diff --git a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts index 2cf84effa..a7ffae767 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts @@ -170,7 +170,7 @@ export function registerClientHomeTests() { ).not.toBeNull(); fireEvent.click( within(screen.getByLabelText('资源栏目大纲')).getByRole('button', { - name: '项目版本', + name: /^项目版本/, }), ); expect(screen.getByRole('button', { name: /版本 1/ })).not.toBeNull(); @@ -345,7 +345,7 @@ export function registerClientHomeTests() { ).not.toBeNull(); fireEvent.click( within(screen.getByLabelText('资源栏目大纲')).getByRole('button', { - name: '项目版本', + name: /^项目版本/, }), ); expect(screen.getByRole('button', { name: /版本 1/ })).not.toBeNull(); diff --git a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts index 5eb599eec..16e0479cb 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts @@ -1323,7 +1323,7 @@ export function registerProjectWorkbenchFoundationTests() { }), ); }); - fireEvent.click(within(outline).getByRole('button', { name: /项目版本/ })); + fireEvent.click(within(outline).getByRole('button', { name: /^项目版本/ })); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 220)); }); @@ -1379,7 +1379,7 @@ export function registerProjectWorkbenchFoundationTests() { screen.getByRole('region', { name: '音乐音效资源画布' }), ).not.toBeNull(); - fireEvent.click(within(outline).getByRole('button', { name: /项目版本/ })); + fireEvent.click(within(outline).getByRole('button', { name: /^项目版本/ })); expect( screen.getByRole('region', { name: '项目版本资源画布' }), ).not.toBeNull(); @@ -4330,7 +4330,13 @@ export function registerProjectWorkbenchFoundationTests() { /\.launcher-main:has\(\.game-project-workbench\)\s*\{[^}]*padding-bottom:\s*0/s, ); expect(styles).toMatch( - /\.game-workbench-toolbar\s*\{[^}]*padding:\s*8px 12px[^}]*background:\s*transparent/s, + /\.game-workbench-toolbar\s*\{[^}]*position:\s*relative[^}]*padding:\s*8px 12px[^}]*background:\s*transparent/s, + ); + expect(styles).toMatch( + /\.game-workbench-view-actions \.game-workbench-play-button\s*\{[^}]*position:\s*absolute[^}]*left:\s*50%[^}]*transform:\s*translateX\(-50%\)/s, + ); + expect(styles).toMatch( + /@media \(max-width: 1000px\)[\s\S]*?\.game-workbench-view-actions \.game-workbench-play-button\s*\{[^}]*position:\s*static[^}]*transform:\s*none/s, ); expect(styles).toMatch( /@media \(min-width: 761px\)[\s\S]*?\.game-project-workbench\s*\{[^}]*grid-template-rows:\s*minmax\(0, 1fr\) auto[^}]*height:\s*100dvh/, @@ -4345,10 +4351,13 @@ export function registerProjectWorkbenchFoundationTests() { /\.project-supervisor-conversation\s*\{[^}]*grid-template-rows/s, ); expect(styles).toMatch( - /\.game-workbench-chat \.project-supervisor-conversation\s*\{[^}]*height:\s*100%[^}]*min-height:\s*0[^}]*overflow:\s*hidden/s, + /\.game-workbench-chat \.project-supervisor-conversation\s*\{[^}]*position:\s*relative[^}]*height:\s*100%[^}]*min-height:\s*0[^}]*overflow:\s*hidden/s, ); expect(styles).toMatch( - /\.game-workbench-chat \.project-supervisor-message-list\s*\{[^}]*flex:\s*1 1 auto[^}]*min-height:\s*96px[^}]*overflow-y:\s*auto/s, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-message-list\s*\{[^}]*position:\s*absolute[^}]*bottom:\s*104px[^}]*left:\s*0/s, + ); + expect(styles).toMatch( + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-message-list\s*\{[^}]*flex:\s*1 1 auto[^}]*min-height:\s*96px[^}]*overflow-y:\s*auto/s, ); // 审批卡与运行状态各自带上限并内部滚动,不能靠挤别人来容纳自己的内容。锁的是 // 「有 clamp 上限 + 自己滚」这个不变量,不锁具体数值——三个断点是随排版调整的 @@ -4383,14 +4392,14 @@ export function registerProjectWorkbenchFoundationTests() { /\.game-workbench-chat \.project-runtime-summary\s*\{[^}]*position:\s*sticky[^}]*top:\s*-10px/s, ); expect(styles).toMatch( - /\.game-workbench-chat \.project-supervisor-composer\s*\{[^}]*grid-template-columns:\s*minmax\(0, 1fr\) 82px[^}]*z-index:\s*2[^}]*padding-top:\s*8px[^}]*background:\s*transparent/s, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-composer\s*\{[^}]*grid-template-columns:\s*minmax\(0, 1fr\) 82px[^}]*z-index:\s*2[^}]*padding-top:\s*8px[^}]*background:\s*transparent/s, ); const composerRule = styles.match( - /\.game-workbench-chat \.project-supervisor-composer\s*\{([^}]*)\}/s, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-composer\s*\{([^}]*)\}/s, ); expect(composerRule?.[1]).not.toContain('border-top:'); expect(styles).toMatch( - /\.game-workbench-chat \.project-supervisor-composer button\s*\{[^}]*min-height:\s*72px[^}]*white-space:\s*nowrap/s, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-composer\s+button\s*\{[^}]*min-height:\s*72px[^}]*white-space:\s*nowrap/s, ); expect(styles).toMatch( /\.game-run-surface\s*\{[^}]*grid-template-rows:\s*minmax\(300px, 1fr\) auto[^}]*grid-row:\s*2 \/ -1[^}]*height:\s*100%/s, @@ -4420,6 +4429,9 @@ export function registerProjectWorkbenchFoundationTests() { expect(styles).toMatch( /@media \(max-width: 760px\)[\s\S]*?\.game-workbench-layout\s*\{[^}]*height:\s*auto/, ); + expect(styles).toMatch( + /@media \(max-width: 760px\)[\s\S]*?\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-message-list\s*\{[^}]*position:\s*static[^}]*height:\s*52vh[^}]*flex:\s*1 1 auto/s, + ); expect(styles).toMatch( /\.game-resource-canvas-content\s*\{[^}]*width:\s*100%[^}]*min-width:\s*max\(100%, 620px\)/s, ); @@ -4505,19 +4517,19 @@ export function registerProjectWorkbenchFoundationTests() { ); const messageListRule = styles.match( - /\.game-workbench-chat \.project-supervisor-message-list\s*\{([^}]*)\}/s, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-message-list\s*\{([^}]*)\}/s, )?.[1] ?? ''; const messageRule = styles.match( - /\.game-workbench-chat \.project-supervisor-message-list \.message\s*\{([^}]*)\}/s, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-message-list\s+\.message\s*\{([^}]*)\}/s, )?.[1] ?? ''; const userMessageRule = styles.match( - /\.game-workbench-chat \.project-supervisor-message-list \.message--user\s*\{([^}]*)\}/s, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-message-list\s+\.message--user\s*\{([^}]*)\}/s, )?.[1] ?? ''; const processCardRules = Array.from( styles.matchAll( - /\.game-workbench-chat\s+\.project-supervisor-message-list\s*>\s*\.project-supervisor-process-card\s*\{([^}]*)\}/gs, + /\.game-workbench-chat\s+\.project-supervisor-surface\.is-direct-codex\s+\.project-supervisor-message-list\s*>\s*\.project-supervisor-process-card\s*\{([^}]*)\}/gs, ), (match) => match[1], ); diff --git a/docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md b/docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md index 788cf40be..984bc55e6 100644 --- a/docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md +++ b/docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md @@ -114,7 +114,7 @@ - 工作台四区作为同一应用工作面直接衔接,外层不使用卡片圆角或留白间距。中央主视窗与右侧 Supervisor 使用各自的平台区域底色,并以稳定的竖向分隔线明确边界;不通过外层圆角容器、阴影或区块间缝重新包装四区。 - 右侧正式钱包入口属于客户端宿主 chrome,在普通项目工作台与 UI Editor 子路由中都必须持续可见、键盘可达且可操作,不得因进入素材编辑壳或隐藏全局 chrome 而丢失余额、充值和使用详情入口。 - 资源总览的“资源依赖 / 资源类型”视图切换使用连通的分段按钮组,相邻选项共享边界并保持唯一选中语义。每个分段都必须有清晰的键盘焦点指示,焦点环不得被分段容器的圆角或 `overflow` 裁切。 -- 右侧 Supervisor 对话中,用户消息使用右对齐、最大宽度受限的主题暖色气泡,assistant 消息保持左对齐;消息换行不得产生水平溢出,执行过程卡继续占满消息区可用宽度。气泡正文在 light / dark 平台主题下均须满足 WCAG AA 普通文本 `4.5:1` 对比度。 +- 右侧 Supervisor 对话中,用户消息使用右对齐、最大宽度受限的主题暖色气泡,assistant 消息保持左对齐;消息换行不得产生水平溢出,执行过程卡继续占满消息区可用宽度。消息列表必须约束在右侧对话列内并独立滚动,不得覆盖中央资源或运行视图;提交按钮必须保留随状态变化的可访问名称。气泡正文在 light / dark 平台主题下均须满足 WCAG AA 普通文本 `4.5:1` 对比度。 - 客户端正式产品仍只按最小 `1280×720` 横屏合同交付,并保留 `1280×800` 默认窗口与既有基线验收;更窄浏览器样式只负责不崩溃和开发兼容,不改成移动端创作工作台。 - 工作台顶部播放按钮在桌面视窗中水平居中;资源管理视窗触发播放时直接切换到运行视窗并启动本地预览,不再弹出 `game.run_local` 二次确认。 - 普通用户界面不默认展示内部错误码或本机绝对路径,也不展示 External Editor Base URL 或 Developer API Key;官方服务地址由客户端构建固定,远端编辑自动使用当前陶泥儿登录态。仅独立 standalone game-chat release 或显式高级自定义模式在隔离配置页维护 External v1 URL/Key,确需诊断的信息进入受控详情或开发模式。