From eda34187fc1bb36665c9c8261aca3b31a761d426 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Thu, 10 Sep 2026 20:01:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20AGC=20=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E6=A0=8F=E7=9B=AE=E5=A4=A7=E7=BA=B2=E5=B8=B8=E6=80=81=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E8=AF=BB=EF=BC=9A=E6=81=A2=E5=A4=8D=20Dock=20?= =?UTF-8?q?=E5=BA=95=E6=9D=BF=E3=80=81=E6=A0=8F=E7=9B=AE=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E4=B8=8E=E5=AE=8C=E6=95=B4=E6=96=87=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - styles.css:`.game-resource-outline` 常态从 `opacity: 0.68` + `scale(0.86)` + 透明底 + 无阴影,改回完整尺寸(`opacity: 1`、`transform: translateY(-50%)`)并给现有视觉语言的 Dock 底板(`--platform-subpanel-border` 边框 + `--platform-nav-fill` 背景 + 与右下角缩放 Dock 同款阴影 + `blur(6px)`)。 - styles.css:`.game-resource-outline button` 常态列宽由 `0 minmax(0,1fr)` 改为 `18px minmax(0,1fr)`、`gap: 6px`,栏目图标常态 width 14px / opacity 1,不再只在悬停时才出现。 - styles.css:栏目文字 11px → 12px;当前栏目(`.is-active`)常态就带 `--platform-nav-active-*` 的边框、底色和阴影,用户一眼能认出当前分区。 - styles.css:悬停/键盘聚焦保留为同一套 token 的加强(更深的边框、`--platform-nav-active-shadow`、`blur(10px)`),不再靠"常态缩到只剩文字"表达层级。 - 不改交互语义:大纲仍然是 `openResourceBookChild` 的入口,按钮 key、aria-label、aria-current 与未读小圆点全部保持原样。 - 测试:新增 CSS 声明级断言,钉住常态不降透明度、不 scale、有底板,图标常态 14px 且文字 12px(jsdom 不加载 styles.css,可见性只能钉声明)。 - 测试:新增 DOM 用例,断言每条栏目入口同时具备图标与栏目文字,且切换栏目后当前项带 `aria-current="page"`。 - 文档:同步《GameAgent资源自由画板与快速编辑》的「操作边界」与「验收」两条,把"常态缩小、只露栏目文字"改成"常态即可读可点",并同步 pitfalls 里的对应口径。 --- apps/ai-game-creator-shell/src/styles.css | 56 ++++------- .../appSurface/project-development.suite.ts | 96 +++++++++++++++++++ docs/project-memory/shared-memory/pitfalls.md | 2 +- ...】GameAgent资源自由画板与快速编辑-2026-08-20.md | 4 +- 4 files changed, 119 insertions(+), 39 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 78d961934..5a56894ea 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -6393,19 +6393,20 @@ iframe.preview-frame { max-height: calc(100% - 24px); padding: 6px; overflow: auto; - border: 1px solid transparent; + /* 常态就给完整尺寸和 Dock 底板:只留 11px 文字、去掉图标和底色时,用户认不出 + 这是栏目入口。悬停/键盘聚焦仍在同一套 token 上加强(见下方 :hover 规则)。 */ + border: 1px solid var(--platform-subpanel-border); border-radius: 14px; - background: transparent; - box-shadow: none; - opacity: 0.68; - transform: translateY(-50%) scale(0.86); + background: var(--platform-nav-fill); + box-shadow: 0 6px 18px rgb(112 70 52 / 12%); + opacity: 1; + transform: translateY(-50%); transform-origin: left center; transition: border-color 160ms ease, opacity 160ms ease, - transform 160ms ease, box-shadow 160ms ease; - backdrop-filter: none; + backdrop-filter: blur(6px); } .game-resource-outline:hover, @@ -6414,15 +6415,15 @@ iframe.preview-frame { background: var(--platform-nav-fill); box-shadow: var(--platform-nav-active-shadow); opacity: 1; - transform: translateY(-50%) scale(1); + transform: translateY(-50%); backdrop-filter: blur(10px); } .game-resource-outline button { display: grid; - grid-template-columns: 0 minmax(0, 1fr); + grid-template-columns: 18px minmax(0, 1fr); align-items: center; - gap: 0; + gap: 6px; min-height: 34px; padding: 0 9px; border: 1px solid transparent; @@ -6432,32 +6433,19 @@ iframe.preview-frame { cursor: pointer; text-align: left; transition: - grid-template-columns 160ms ease, - gap 160ms ease; -} - -.game-resource-outline:hover button, -.game-resource-outline:has(> button:focus-visible) button { - grid-template-columns: 18px minmax(0, 1fr); - gap: 6px; + background-color 160ms ease, + border-color 160ms ease; } +/* 图标与栏目文字常态就在位(上面两条规则),悬停/键盘聚焦只加强 Dock 底板与当前项高亮。 */ .game-resource-outline button svg { - width: 0; - min-width: 0; - opacity: 0; - transform: scale(0.65); - transition: - width 160ms ease, - opacity 120ms ease, - transform 160ms ease; -} - -.game-resource-outline:hover button svg, -.game-resource-outline:has(> button:focus-visible) button svg { width: 14px; + min-width: 0; opacity: 1; transform: scale(1); + transition: + opacity 120ms ease, + transform 160ms ease; } .game-resource-outline button:hover, @@ -6467,19 +6455,15 @@ iframe.preview-frame { } .game-resource-outline button.is-active { - color: var(--platform-nav-item-text-active); -} - -.game-resource-outline:hover button.is-active, -.game-resource-outline:has(> button:focus-visible) button.is-active { border-color: var(--platform-nav-active-border); background: var(--platform-nav-active-fill); + color: var(--platform-nav-item-text-active); box-shadow: var(--platform-nav-active-shadow); } .game-resource-outline button strong { overflow: hidden; - font-size: 11px; + font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } 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 8f9f9cdf8..04a1bcfea 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 @@ -1162,6 +1162,102 @@ export function registerProjectWorkbenchFoundationTests() { expect(styleNumber(notice, 'z-index')).toBeGreaterThan(sceneZIndex); }); + it('keeps the outline dock readable without hover', () => { + const styles = readFileSync( + resolve(process.cwd(), 'apps/ai-game-creator-shell/src/styles.css'), + 'utf8', + ); + const outline = styleRuleBody(styles, '\\.game-resource-outline'); + // 常态必须有 Dock 底板且不再整体降透明度/缩小,否则用户认不出这是栏目入口。 + expect(styleNumber(outline, 'opacity')).toBe(1); + expect(outline).not.toMatch(/scale\(/u); + expect(outline).toMatch(/background:\s*var\(--platform-nav-fill\)/u); + expect(outline).toMatch( + /border:\s*1px solid var\(--platform-subpanel-border\)/u, + ); + + // 栏目图标常态就展开:常态把 svg 收成 width: 0、只剩 11px 文字是"认不出"的根因。 + expect(styleRuleBody(styles, '\\.game-resource-outline button')).toMatch( + /grid-template-columns:\s*18px minmax\(0, 1fr\)/u, + ); + const icon = styleRuleBody(styles, '\\.game-resource-outline button svg'); + expect(styleNumber(icon, 'width')).toBe(14); + expect(styleNumber(icon, 'opacity')).toBe(1); + expect( + styleNumber( + styleRuleBody(styles, '\\.game-resource-outline button strong'), + 'font-size', + ), + ).toBe(12); + }); + + it('renders every outline entry with its icon and section label', async () => { + const manifest = createGameCreationAppManifest( + 'workbench-outline-dock', + '栏目大纲可读性测试', + ); + manifest.assets = [ + { + id: 'outline-art', + kind: 'character', + mediaType: 'image/png', + localPath: 'assets/outline-art.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-outline-dock', + manifest, + attachments: [], + recentRunStatus: null, + recentRunStopReason: null, + supervisor: React.createElement('div', null, '项目总控'), + onHomeOpen: vi.fn(), + onProjectsOpen: vi.fn(), + }), + ); + + const outline = await screen.findByLabelText('资源栏目大纲'); + const entries = Array.from(outline.querySelectorAll('button')); + expect(entries.length).toBeGreaterThan(0); + // 每条栏目都是"图标 + 栏目文字"的完整入口,不是只有一行小字。 + expect( + entries.every( + (button) => + button.querySelector('svg') !== null && + (button.querySelector('strong')?.textContent ?? '').trim().length > 0, + ), + ).toBe(true); + await openResourceBookCategory('美术资源'); + expect( + within(outline) + .getByRole('button', { name: '美术资源' }) + .getAttribute('aria-current'), + ).toBe('page'); + }); + it('keeps the shared selection overlay colours resolvable from the scene root', () => { const sharedStyles = readFileSync( resolve(process.cwd(), 'packages/image-canvas-react/src/styles.css'), diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 997060dc7..3c409c169 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -5115,7 +5115,7 @@ - 原因二(何时被关掉):`display: none` 是 PR #193 重排工作台时改的(原始提交 `3a8dff140` 里是 `display: flex`),不是设计选择。 - 门禁为什么抓不到:既有用例用 `screen.getByLabelText('搜索项目资源')` + `fireEvent.change`;`getByLabelText` 默认**不校验可见性**,`display: none` 的元素照样命中并成功改值。AGC 的 vitest 也没有开 `css: true`、`styles.css` 不会被加载,所以 `toBeVisible()` 在 AGC 里是**恒真的假守卫**——可见性只能读 CSS 源文件做声明级断言(现有口径见 `apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts` 的 `styleRuleBody` 断言),交互链路另用真实 DOM 用例覆盖。 - 处理:搜索框恢复 `display: flex`,常态 `align-self: center` 居中(总览态标题在左上、分页态栏目标题栏左右两端都有内容,只有中段是空的),与缩放 Dock 同级 `z-index: 40`;提示条同样抬到场景之上。 -- 同批修复(大纲):栏目大纲常态是 `opacity: 0.68` + `scale(0.86)` + 全透明底 + `svg { width: 0 }`,常态只剩 11px 文字,用户认不出那是分区入口;现在常态就给 Dock 底板、14px 栏目图标和 12px 栏目文字并高亮当前栏目,悬停/键盘聚焦只做同一套 token 的加强。**注意**:`docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md` 的「操作边界」与「验收」两条仍写着"常态缩小、只露栏目文字",该文件当时有其它未提交改动,本批没有一并改,需要后续同步。 +- 同批修复(大纲):栏目大纲常态是 `opacity: 0.68` + `scale(0.86)` + 全透明底 + `svg { width: 0 }`,常态只剩 11px 文字,用户认不出那是分区入口;现在常态就给 Dock 底板、14px 栏目图标和 12px 栏目文字并高亮当前栏目,悬停/键盘聚焦只做同一套 token 的加强。同批把 `docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md` 的「操作边界」与「验收」两条一起改成"常态即可读可点",不留描述旧收起态的文档。 - 关联:`apps/ai-game-creator-shell/src/styles.css`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx`。 ## AGC 复用共享画布组件时要自己渲染 `.genarrative-image-canvas` 根类(2026-09-10) diff --git a/docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md b/docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md index b9c49dcee..21dc52d44 100644 --- a/docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md +++ b/docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md @@ -50,7 +50,7 @@ ## 操作边界 -- 栏目顺序固定为 `UI 交互 -> 角色与对象 -> 场景与环境 -> 音频 -> 文档 -> 待归类 -> 项目版本`。完全空项目显示全部栏目的分区展览;任一栏目出现资源后,分页大纲以左侧垂直居中的悬浮 Dock 展示全部栏目。常态缩小、降低不透明度并移除容器与选中项背景,只露出栏目文字;悬停或键盘聚焦时平滑恢复完整尺寸,显示栏目图标、Dock 背景和选中态视觉强调。默认停留在该顺序中的第一个非空栏目,空栏目仍可打开空画布。游戏代码资产按其 manifest `category` 落入 `待归类` 并正常显示卡片。 +- 栏目顺序固定为 `UI 交互 -> 角色与对象 -> 场景与环境 -> 音频 -> 文档 -> 待归类 -> 项目版本`。完全空项目显示全部栏目的分区展览;任一栏目出现资源后,分页大纲以左侧垂直居中的悬浮 Dock 展示全部栏目。Dock 常态即可读可点:Dock 底板、与栏目标题栏同款的栏目图标和 12px 栏目文字常驻,当前栏目常态带选中态强调;悬停或键盘聚焦只做同一套 token 的加强(更深的边框、阴影与背景模糊),不再用"常态缩小、降低不透明度、只露栏目文字"表达层级。默认停留在该顺序中的第一个非空栏目,空栏目仍可打开空画布。游戏代码资产按其 manifest `category` 落入 `待归类` 并正常显示卡片。 - 滚轮不切换栏目。点击大纲、底部“下一页”标题、总览入口或自动定位资源属于显式切页;切页前必须终止旧栏目的画布拖动和 pointer capture,避免旧 viewport 写入新栏目。 - 每种排序模式下的每个栏目画布都保留独立 viewport;首次进入该“排序模式 + 栏目”组合时按当前内容适配视口,离开后再返回则恢复该组合上次的平移和缩放。空白处拖拽平移画布,资源卡拖拽移动卡片并更新依赖线,Ctrl/Meta 缩放只作用于当前组合,不能牵动其它排序模式或栏目。 - 依赖画布复用 `@genarrative/image-canvas-core` 的 viewport 计算,并复用现有资源卡片、布局和依赖连线模型。 @@ -99,7 +99,7 @@ ## 验收 -1. 完全空项目在按依赖与按类型下都显示相同的分区展览;任一栏目出现资源后,两种模式都切换为栏目分页画布,大纲以左侧垂直居中的悬浮 Dock 覆盖在全宽画布上,包含全部栏目的图标和文字、不显示数量,空栏目仍可打开空画布。 +1. 完全空项目在按依赖与按类型下都显示相同的分区展览;任一栏目出现资源后,两种模式都切换为栏目分页画布,大纲以左侧垂直居中的悬浮 Dock 覆盖在全宽画布上,常态就带 Dock 底板、全部栏目的图标和文字(不显示数量),悬停/键盘聚焦只做加强,空栏目仍可打开空画布。 2. 栏目顺序为 `UI 交互 -> 角色与对象 -> 场景与环境 -> 音频 -> 文档 -> 待归类 -> 项目版本`;点击大纲、总览入口和底部下一页标题切页,滚轮不切换栏目。只登记游戏代码的项目在「待归类」栏目正常显示代码卡片。 3. 在当前栏目内拖拽空白可以无限平移画布,拖拽资源卡可以移动卡片并同步依赖线,Ctrl/Meta + 滚轮以指针位置为锚点缩放,复位按钮可以适配当前栏目内容;普通平移不会因资源 extent、图片测量或窗口 resize 被拉回,连续缩放仍停在共享画布的最小/最大比例范围内。 4. 按依赖模式显示当前栏目内同类型两端资源的合法精确引用,并具有与当前可见连线一致的无障碍关系说明;按类型模式不显示引导线;搜索隐藏任一端点后连线和说明同时消失。