From 763f4b5b4cd6ecdba25a22ed3cbc02a4dfafd977 Mon Sep 17 00:00:00 2001 From: Linghong Date: Tue, 25 Aug 2026 10:03:24 +0000 Subject: [PATCH] =?UTF-8?q?=E7=AD=96=E5=88=92=E5=88=97=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E7=9A=84=E5=91=BD=E4=B8=AD=E5=88=A4=E6=8D=AE=E8=A1=A5=E4=B8=8A?= =?UTF-8?q?=E7=AA=84=E6=9D=A1=EF=BC=8Chydrate=20=E8=BF=98=E6=B2=A1?= =?UTF-8?q?=E5=9B=9E=E6=9D=A5=E6=97=B6=E6=BE=84=E6=B8=85=E5=8D=A1=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E8=A2=AB=E8=A3=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一笔只用 `:has(.plan-gdd-surface)` 认策划链路,漏了一格:`PlanGddSurface` 在 `planGddState === null` 时整个返回 null,而 `planGddState` 初值就是 null,要靠 `hydrate_game_creator_plan_gdd_state` 这一次独立 IPC 才填上。首帧竞态、以及 hydrate 持续失败(非写锁争用的错误会把 state 留在 null)这两种情形下,策划 run 照跑、 `PlanningLaneRuntimeStrip` 照渲染澄清卡,但容器上的 `:has()` 不命中,#193 那套 `display: block` + 列表 `height: 100%` + `overflow: hidden` 原样回来,卡片又被静默裁到 可视区外。 判据改成 `.plan-gdd-surface` 与 `.planning-lane-runtime-strip` 的并集。两个类都只属于 策划链路,做游戏与做素材仍走 master 的 block 列。窄条在场本身就等价于「有东西要用户 动手」,并进来之后「卡片存在」和「列布局生效」是同一件事,不再依赖另一条异步链路的 时序。 守门加在已有的策划样式测试里(同一处已经钉着 #193 误删的几条规则):断言这条 flex 列 规则的 `:has()` 必须包含窄条类。已验证非空转——把并集改回单一类,这条断言变红。 Co-Authored-By: Claude Opus 5 --- apps/ai-game-creator-shell/src/styles.css | 40 ++++++++++++++----- .../tests/appSurface/plan-gdd.suite.ts | 8 ++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 046a93732..ba86fce33 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -7517,11 +7517,21 @@ iframe.preview-frame { 所以列表上那个 `padding-bottom: 196px`(本意是给「浮起来的输入框」留位)从来没有对应 的定位规则,那个模型压根没成立过。 - `:has(.plan-gdd-surface)` 只在策划面在场时命中,做游戏与做素材保持 master 的 block - 列原样不动。用 flex 列而不是 grid:子元素个数随待确认命令的有无浮动,固定行模板会 - 错位;而且下面那条 `.plan-gdd-surface { flex: 0 0 auto }` 本来就是照 flex 写的,容器 - 变成 flex 之后它才真正生效——在此之前它一直是死声明。 */ -.game-workbench-chat .project-supervisor-conversation:has(.plan-gdd-surface) { + 命中判据是两个类的并集,两个都只属于策划链路,做游戏与做素材保持 master 的 block + 列原样不动。为什么不能只认策划面:`PlanGddSurface` 在 `planGddState === null` 时整个 + 返回 null,而 `planGddState` 初值就是 null、要靠一次独立的 IPC 才填上。首帧竞态、以及 + hydrate 持续失败时,策划 run 照跑、澄清卡照渲染,只认策划面就会漏掉这一格,裁剪原样 + 复发。窄条自己在场就等价于「有东西要用户动手」,把它并进来之后,「卡片存在」和「列 + 布局生效」才是同一件事。 + + 用 flex 列而不是 grid:子元素个数随待确认命令的有无浮动,固定行模板会错位;而且下面 + 那条 `.plan-gdd-surface { flex: 0 0 auto }` 本来就是照 flex 写的,容器变成 flex 之后它 + 才真正生效——在此之前它一直是死声明。 */ +.game-workbench-chat + .project-supervisor-conversation:has( + .plan-gdd-surface, + .planning-lane-runtime-strip + ) { display: flex; flex-direction: column; gap: 10px; @@ -7535,7 +7545,10 @@ iframe.preview-frame { 输入框定高),由列表吸收剩余空间。`height: 100%` 必须撤掉,否则它照旧独吞整列; `padding-bottom` 也一并收回,输入框已经回到文档流里,不再需要给它留空。 */ .game-workbench-chat - .project-supervisor-conversation:has(.plan-gdd-surface) + .project-supervisor-conversation:has( + .plan-gdd-surface, + .planning-lane-runtime-strip + ) .project-supervisor-message-list { flex: 1 1 auto; height: auto; @@ -7547,13 +7560,22 @@ iframe.preview-frame { /* 这三个成员不参与压缩:消息列表变长时不能把它们挤没——它们正是用户唯一需要动手的 地方(澄清卡、输入框)。 */ .game-workbench-chat - .project-supervisor-conversation:has(.plan-gdd-surface) + .project-supervisor-conversation:has( + .plan-gdd-surface, + .planning-lane-runtime-strip + ) .planning-lane-runtime-strip, .game-workbench-chat - .project-supervisor-conversation:has(.plan-gdd-surface) + .project-supervisor-conversation:has( + .plan-gdd-surface, + .planning-lane-runtime-strip + ) .project-supervisor-composer, .game-workbench-chat - .project-supervisor-conversation:has(.plan-gdd-surface) + .project-supervisor-conversation:has( + .plan-gdd-surface, + .planning-lane-runtime-strip + ) .project-supervisor-workspace-status { flex: 0 0 auto; } diff --git a/apps/ai-game-creator-shell/tests/appSurface/plan-gdd.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/plan-gdd.suite.ts index d6ed7813b..fd20d253e 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/plan-gdd.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/plan-gdd.suite.ts @@ -578,5 +578,13 @@ export function registerPlanGddApprovalTests() { expect(styles).toMatch( /\.game-workbench-layout\.is-conversation-only \.game-workbench-stage\s*\{[^}]*display:\s*none/s, ); + // 工作台里的会话列是 `overflow: hidden` 的定高列。策划链路比另外两条多出窄条 + // (澄清卡 / 失败恢复),只有把这一列排成 flex 列、窄条不参与压缩,它才落在可视 + // 区里;否则组件照常渲染却被静默裁到线外,屏幕上什么都没有。命中判据必须包含窄条 + // 自身——只认 `.plan-gdd-surface` 时,`planGddState` 还没 hydrate 出来的那一格里 + // 澄清卡照样被裁。 + expect(styles).toMatch( + /\.game-workbench-chat\s+\.project-supervisor-conversation:has\([^)]*\.planning-lane-runtime-strip[^)]*\)\s*\{[^}]*display:\s*flex/s, + ); }); }