From 9e4b2b1dc11d71505d3804421271d7a9d5dc884d Mon Sep 17 00:00:00 2001 From: Linghong Date: Mon, 24 Aug 2026 13:28:05 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BA=A4=E4=BB=98=E8=A1=8C=E8=A2=AB=E8=A3=81?= =?UTF-8?q?=E6=8E=89=EF=BC=9A=E7=AD=96=E5=88=92=E9=9D=A2=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=8F=82=E4=B8=8E=20flex=20=E5=8E=8B=E7=BC=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 批准后 GUI 里看不到路径和「查看 GDD 正文」「打开文件」两个按钮。不是没渲染,是被 切了:`.plan-gdd-surface` 为了画圆角带 `overflow: hidden`,而工作台那条又给它 `flex: 0 1 auto` + `min-height: 0`。聊天列纵向吃紧时 flex 把这个面压到内容高度以 下,裁掉的正是最底下那行。标题和轮次在上面,所以还看得见——症状就是「那一栏太小」。 批准前 strip 只有两行、从不需要压缩,所以直到加了第三行才暴露。 它也不需要可压缩:标题栏是固定几行,审批卡自己有 max-height 和内滚,高度天然有界。 改成 `flex: 0 0 auto`。另外「标题栏定高 + 正文占余下」的两行轨道只在审批卡在场时 成立,批准后面里只剩标题栏,把它挪到 `--with-card` 修饰类下。 补一条 A/B 过的断言(改回可压缩即红):这个面必须 `flex: 0 0 auto` 且不带 grid-template-rows,两行轨道只出现在 `--with-card` 上。 Co-Authored-By: Claude Opus 5 --- apps/ai-game-creator-shell/src/styles.css | 10 ++++- .../appSurface/project-development.suite.ts | 39 ++++++++++++------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 991d3314e..031b3fe47 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -7088,8 +7088,16 @@ iframe.preview-frame { /* 审批卡是这一列里唯一没有天花板的成员:Fast GDD 的决定项越多它越高,能把消息列表和运行 时面板一起挤出可视区。和 `.agent-runtime-status` 一样给它自己的上限加内部滚动,而不是 让它去挤别人。 */ +/* 这个面不参与 flex 压缩。它自己不会长高:标题栏是固定几行,审批卡下面有自己的 + max-height 和内滚,所以高度天然有界。让它可压缩的话,`overflow: hidden`(画圆角 + 要的)会把底部切掉——批准后交付行正好在那儿,表现是路径和两个按钮凭空消失。 */ .game-workbench-chat .plan-gdd-surface { - flex: 0 1 auto; + flex: 0 0 auto; +} + +/* 只有审批卡在场时才需要「标题栏定高 + 正文占余下」的两行轨道。批准后卡片收掉, + 面里只剩标题栏,这条声明会给它套上一个不存在的第二行。 */ +.game-workbench-chat .plan-gdd-surface--with-card { min-height: 0; grid-template-rows: auto minmax(0, 1fr); } 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 7c65cfb49..75b65c8cc 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 @@ -816,7 +816,9 @@ export function registerProjectWorkbenchFoundationTests() { 'workbench-art-viewport-memory', '美术资源视口记忆', ); - const codeTask = manifest.tasks.find((task) => task.id === 'code-prototype'); + const codeTask = manifest.tasks.find( + (task) => task.id === 'code-prototype', + ); expect(codeTask).toBeDefined(); codeTask!.status = 'completed'; manifest.assets = [ @@ -1114,7 +1116,9 @@ export function registerProjectWorkbenchFoundationTests() { ).not.toBeNull(); fireEvent.click(within(outline).getByRole('button', { name: /设计文档/ })); - dispatchPageWheel(screen.getByRole('button', { name: /下一页\s*美术资源/ })); + dispatchPageWheel( + screen.getByRole('button', { name: /下一页\s*美术资源/ }), + ); expect( screen.getByRole('region', { name: '美术资源资源画布' }), ).not.toBeNull(); @@ -1177,9 +1181,7 @@ export function registerProjectWorkbenchFoundationTests() { name: /打开资源详情:设计文档 section\.md/, }), ); - expect( - screen.getByRole('dialog', { name: 'section.md' }), - ).not.toBeNull(); + expect(screen.getByRole('dialog', { name: 'section.md' })).not.toBeNull(); dispatchPageWheel(dependencyCanvas); expect( screen.getByRole('region', { name: '美术资源资源画布' }), @@ -1321,9 +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)); }); @@ -1331,9 +1331,7 @@ export function registerProjectWorkbenchFoundationTests() { screen.getByRole('region', { name: '项目版本资源画布' }), ).not.toBeNull(); - fireEvent.click( - within(outline).getByRole('button', { name: /设计文档/ }), - ); + fireEvent.click(within(outline).getByRole('button', { name: /设计文档/ })); act(() => { dependencyCanvas.dispatchEvent( new WheelEvent('wheel', { @@ -3747,10 +3745,7 @@ export function registerProjectWorkbenchFoundationTests() { '[data-resource-viewport]', ); const viewportScale = Number( - viewportElement - ?.getAttribute('data-resource-viewport') - ?.split(',') - .at(2), + viewportElement?.getAttribute('data-resource-viewport')?.split(',').at(2), ); expect(viewportScale).toBeGreaterThan(0); const expectedX = Math.round(12 + (-100 - 20) / viewportScale); @@ -4191,6 +4186,20 @@ export function registerProjectWorkbenchFoundationTests() { expect(styles).toMatch( /\.game-workbench-chat \.planning-lane-runtime-strip\s*\{[^}]*max-height:\s*clamp\([^)]*\)/s, ); + // 策划面画圆角要 `overflow: hidden`,所以它绝不能参与 flex 压缩:一旦可压缩,被压到 + // 内容高度以下时裁掉的正是最底下那行——批准后的交付出口(路径 + 两个按钮)就这么 + // 凭空消失过一次。它也不需要可压缩,审批卡自己有上限和内滚,高度天然有界。 + const planGddSurfaceRule = styles.match( + /\.game-workbench-chat \.plan-gdd-surface\s*\{[^}]*\}/s, + ); + expect(planGddSurfaceRule).not.toBeNull(); + expect(planGddSurfaceRule?.[0]).toMatch(/flex:\s*0 0 auto/); + // 「标题栏定高 + 正文占余下」的两行轨道只在审批卡在场时成立;批准后面里只剩标题栏, + // 套上不存在的第二行只会让它错位。 + expect(planGddSurfaceRule?.[0]).not.toMatch(/grid-template-rows/); + expect(styles).toMatch( + /\.game-workbench-chat \.plan-gdd-surface--with-card\s*\{[^}]*grid-template-rows:\s*auto minmax\(0, 1fr\)/s, + ); expect(styles).toMatch( /\.game-workbench-chat \.project-runtime-summary\s*\{[^}]*position:\s*sticky[^}]*top:\s*-10px/s, );