From 3bd4daf5d5237423cd4abdfe82da80f370459d73 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Fri, 11 Sep 2026 00:57:52 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E3=80=8C=E5=9B=9E=E5=88=B0=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E6=80=BB=E8=A7=88=E3=80=8D=E5=91=BD=E4=B8=AD=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F=E7=9A=84=E5=AE=88=E5=8D=AB=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 成因回填:分页态标题栏右端的「资源总览」(回到资源总览)按钮点不动,是常驻工具条带 `.game-resource-book-tools` 造成的——它是横跨整行、`z-index: 40` 的绝对定位层,正好盖在栏目标题栏那条带(`z-index: 30`)上,把按钮的命中区域整段吃掉。上一提交撤掉这条带后问题已消失,本次只补守卫,不改实现。 - project-development.suite.ts:新增声明级守卫,钉住浮层提示层(`.game-resource-book-notices`)与返回按钮所在标题栏的层级关系——提示层 `z-index` 高于标题栏(提示要看得见),因此容器必须 `pointer-events: none`、只有提示条自己 `pointer-events: auto`;提示条顶边 ≥ 标题栏 `min-height`,连视觉重叠都不发生;并反向断言常驻工具条带在 styles.css 与 index.tsx 里都已不存在(变异实测:把容器改回 `pointer-events: auto` 该用例立即红灯)。 - project-development.suite.ts:新增渲染级守卫——附件导入失败条在场(提示层挂载)时进入分页态,断言返回按钮不在提示层容器内、不是 disabled,点击后 `data-resource-book-view` 回到 `main` 且「资源总览」region 在场。 - 说明:jsdom 不做布局命中判定,像素级"是否被盖住"只能由声明级的层级/命中声明 + 真机复核确认;渲染级用例钉的是"提示层在场时返回入口仍然渲染、可点、并真的接在返回总览上"。 --- .../appSurface/project-development.suite.ts | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) 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 04da6b4d8..b5040611a 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 @@ -1605,6 +1605,135 @@ export function registerProjectWorkbenchFoundationTests() { expect(searchTrigger().className).not.toContain('is-active'); }); + it('keeps the overview-return button out of the floating notice layer hit region', () => { + const styles = readFileSync( + resolve(process.cwd(), 'apps/ai-game-creator-shell/src/styles.css'), + 'utf8', + ); + const tsxSource = readFileSync( + resolve( + process.cwd(), + 'apps/ai-game-creator-shell/src/view/project-development/index.tsx', + ), + 'utf8', + ); + const sceneZIndex = styleNumber( + styleRuleBody(styles, '\\.game-resource-book-scene'), + 'z-index', + ); + const titlebar = styleRuleBody( + styles, + '\\.game-resource-book-scene-titlebar', + ); + const titlebarZIndex = styleNumber(titlebar, 'z-index'); + const titlebarHeight = styleNumber(titlebar, 'min-height'); + expect(titlebarZIndex).toBeGreaterThan(sceneZIndex); + expect(titlebarHeight).toBeGreaterThan(0); + + // 提示层为了看得见必须浮在栏目标题栏之上——正因为如此,它必须把命中区域限死在提示条 + // 自己的盒子里:容器 `pointer-events: none` + 提示条 `auto`。分页态标题栏右端的 + // 「资源总览」(回到资源总览)就在这条带里,容器一旦吃点击,那颗按钮就点不动。 + const notices = styleRuleBody(styles, '\\.game-resource-book-notices'); + expect(styleNumber(notices, 'z-index')).toBeGreaterThan(titlebarZIndex); + expect(notices).toMatch(/pointer-events:\s*none/u); + expect( + styleRuleBody(styles, '\\.game-resource-book-notices > \\*'), + ).toMatch(/pointer-events:\s*auto/u); + // 提示条顶边在标题栏那条带以下:连视觉重叠都不会发生,不只是命中被放行。 + expect(styleNumber(notices, 'top')).toBeGreaterThanOrEqual(titlebarHeight); + + // 成因本身也钉住:横跨整行、z-index 40 的常驻工具条带(当初为常驻搜索框引入)已经不在了, + // 它盖在标题栏那条带上时,正好吃掉了返回按钮的命中区域。 + expect(styles).not.toContain('game-resource-book-tools'); + expect(tsxSource).not.toContain('game-resource-book-tools'); + }); + + it('returns to the overview from the paged titlebar while the notice layer is on screen', async () => { + const manifest = createGameCreationAppManifest( + 'workbench-return-overview-hit', + '回到总览命中区测试', + ); + manifest.assets = [ + { + id: 'return-overview-art', + kind: 'character', + mediaType: 'image/png', + localPath: 'assets/return-hero.png', + source: { kind: 'generated', taskId: 'art-asset-plan' }, + }, + ]; + const invoke = vi.fn( + async (command: string, args?: Record) => { + if (command === 'read_local_project_resource_graph') { + return resourceGraphForInputs(args); + } + if (command === 'read_local_project_resource_canvas_layout') { + return { + schemaVersion: 'game-creator-resource-layout.v1', + projectId: args?.expectedProjectId, + mode: args?.mode, + revision: 0, + positions: [], + updatedAt: 0, + }; + } + throw new Error(`unexpected invoke ${command}`); + }, + ); + window.__TAURI__ = { core: { invoke } }; + + render( + React.createElement(ProjectDevelopmentView, { + projectName: manifest.name, + projectPath: '/tmp/workbench-return-overview-hit', + manifest, + attachments: [ + { + fileName: 'broken.txt', + mediaType: 'text/plain', + status: 'failed' as const, + error: '附件导入失败', + }, + ], + recentRunStatus: null, + recentRunStopReason: null, + supervisor: React.createElement('div', null, '项目总控'), + onHomeOpen: vi.fn(), + onProjectsOpen: vi.fn(), + }), + ); + + // 提示层在场:附件导入失败条渲染在这层浮层里,而这层浮层不属于画本场景。 + const notices = document.querySelector('.game-resource-book-notices'); + expect(notices).not.toBeNull(); + expect(notices?.querySelector('.game-attachment-errors')).not.toBeNull(); + expect(notices?.closest('.game-resource-book-scene')).toBeNull(); + expect(notices?.querySelector('.game-resource-book-scene')).toBeNull(); + + await openResourceBookCategory('角色与对象'); + const activeTitlebar = document.querySelector( + '.game-resource-book-scene-titlebar.is-active', + ); + expect(activeTitlebar).not.toBeNull(); + const returnButton = within(activeTitlebar as HTMLElement).getByRole( + 'button', + { name: '收起资源' }, + ) as HTMLButtonElement; + // 按钮可点:不是 disabled,祖先链上也没有提示层那类跨整行的浮层容器。 + expect(returnButton.disabled).toBe(false); + expect(returnButton.closest('.game-resource-book-notices')).toBeNull(); + + // jsdom 不做布局命中的判定,能钉住的是"层级关系 + 按钮真的接在返回总览上": + // 默认运行时不带 aria-disabled、点击真的切回总览态。 + fireEvent.click(returnButton); + expect( + document + .querySelector('[data-resource-book-view]') + ?.getAttribute('data-resource-book-view'), + ).toBe('main'); + expect(screen.getByRole('region', { name: '资源总览' })).not.toBeNull(); + }); + it('paints the marquee selection box with the scene token root and non-empty geometry', async () => { const manifest = createGameCreationAppManifest( 'workbench-marquee-colour',