From d80011656becddb6a8cda0f2959242c8a49dcfd4 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Fri, 11 Sep 2026 16:28:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=8A=E8=B5=84=E6=BA=90=E5=8D=A1=E7=9A=84?= =?UTF-8?q?=20@=20=E5=BC=95=E7=94=A8=E5=85=A5=E5=8F=A3=E6=8C=AA=E8=BF=9B?= =?UTF-8?q?=E9=80=89=E4=B8=AD=E8=B5=84=E6=BA=90=E5=B7=A5=E5=85=B7=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除资源卡右上角的 @ 引用圆钮,连同它的 44×44 热区伪元素与全部样式一起退役 - 选中资源的画布工具条新增「@引用」动作,排在「UI 编辑器」之后,出站负载与来源逐字未变(source: 'resource-card') - 测试:原「卡片圆钮视觉尺寸」用例改写为「入口只留在工具条里」的源级 + 样式级断言,并补反向断言卡片组件内不再出现插入调用 - 测试:新增键盘通路用例,聚焦后回车必须真正派发 agc-resource-reference-insert,且两条通路负载逐字相同、每次激活只派发一次 - harness 注释同步:资源卡本体只剩「选中资源」与媒体播放钮 --- apps/ai-game-creator-shell/src/styles.css | 53 ----- .../src/view/project-development/index.tsx | 52 ++-- .../tests/appSurface/harness.ts | 4 +- .../appSurface/project-development.suite.ts | 223 ++++++++++++++---- 4 files changed, 204 insertions(+), 128 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index eff4c5325..7db5bf8ae 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -7025,59 +7025,6 @@ iframe.preview-frame { outline-offset: 1px; } -.game-resource-card-reference { - position: absolute; - right: 49px; - /* 与右侧 34px 播放钮圆心对齐:9 + 34 / 2 = 14 + 24 / 2。 */ - bottom: 14px; - z-index: 2; - display: grid; - /* 视觉按 0.8× 缩小:30 → 24,内部图标 16 → 13 同步缩。 */ - width: 24px; - height: 24px; - padding: 0; - border: 1px solid rgb(255 255 255 / 70%); - border-radius: 50%; - background: rgb(75 48 38 / 82%); - color: #fff; - box-shadow: 0 5px 14px rgb(52 30 22 / 24%); - cursor: pointer; - place-items: center; -} - -/* - * 视觉缩小但热区不缩:伪元素撑出 44×44(本仓 window chrome / 素材选择列表同口径的最小 - * 可点尺寸),只扩命中范围,不参与布局也不改变绘制;整体向左让 4px,让热区右边缘正好与 - * 右侧 34px 播放钮的盒子相邻而不覆盖它。 - */ -.game-resource-card-reference::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 44px; - height: 44px; - border-radius: 50%; - transform: translate(calc(-50% - 4px), -50%); -} - -.game-resource-card-reference svg { - width: 13px; - height: 13px; -} - -.game-resource-card:not(:has(.game-resource-card-media-control)) - .game-resource-card-reference { - right: 9px; -} - -.game-resource-card-reference:hover, -.game-resource-card-reference:focus-visible { - background: #c96f44; - outline: 2px solid rgb(255 255 255 / 92%); - outline-offset: 1px; -} - .game-resource-card-placeholder, .game-resource-card-version-visual, .game-resource-card-audio-visual { diff --git a/apps/ai-game-creator-shell/src/view/project-development/index.tsx b/apps/ai-game-creator-shell/src/view/project-development/index.tsx index 9644a0850..9ab2bbdd4 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/index.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/index.tsx @@ -894,30 +894,6 @@ const ResourceCard = memo(function ResourceCard({ )} ) : null} - {resource.manifestAssetId ? ( - - ) : null} ); }); @@ -5742,6 +5718,34 @@ export default function ProjectDevelopmentView({ UI 编辑器 ) : null} + {selectedResource?.manifestAssetId ? ( + } + onClick={() => + dispatchResourceReferenceInsert({ + type: 'resource', + resourceId: + selectedResource.manifestAssetId!, + kind: + selectedResource.subtype || + selectedResource.category, + mediaType: selectedResource.mediaType, + label: selectedResource.label, + category: + projectResourceAssetCategory( + selectedResource, + ), + tags: selectedResource.assetTags ?? [], + source: 'resource-card', + }) + } + > + @引用 + + ) : null} {selectedResource?.manifestAssetId ? ( <资源文件名>`;分类标签可能自带空格(例如 `UI 交互`), // 这里按模板做精确匹配。 function escapeRegExpLiteral(value: string) { 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 c2875b225..093e299e0 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 @@ -1,7 +1,10 @@ +import userEvent from '@testing-library/user-event'; + import type { ProjectResourceCanvasLayout, ProjectResourceCanvasPosition, } from '../../../../packages/shared/src/contracts/gameCreationApp'; +import { RESOURCE_REFERENCE_INSERT_EVENT } from '../../src/features/project-workspace/resourceReferences'; import { RESOURCE_BOOK_OVERVIEW_STACK_LIMIT } from '../../src/view/project-development/resourceBookLayout'; import { RESOURCE_CANVAS_CARD_WIDTH, @@ -2899,53 +2902,15 @@ export function registerProjectWorkbenchFoundationTests() { expect(styles).not.toMatch(/\.game-resource-card-open\s*\{/); }); - it('keeps the resource card @ reference button visually small without shrinking its hit area', () => { + it('资源卡的 @ 引用入口只留在选中工具条里', () => { const styles = readFileSync( resolve(process.cwd(), 'apps/ai-game-creator-shell/src/styles.css'), 'utf8', ); - const reference = styleRuleBody(styles, '\\.game-resource-card-reference'); - const referenceSize = styleNumber(reference, 'width'); - // 视觉按 0.8× 缩:原 30×30 圆钮 → 24×24,内部图标 16 → 13。 - expect(referenceSize).toBe(24); - expect(styleNumber(reference, 'height')).toBe(24); - expect(referenceSize).toBeLessThan(30); - const icon = styleRuleBody(styles, '\\.game-resource-card-reference svg'); - expect(styleNumber(icon, 'width')).toBe(13); - expect(styleNumber(icon, 'height')).toBe(13); - expect(styleNumber(icon, 'width')).toBeLessThan(16); + // 卡片右上角那个圆钮连同它撑出来的 44×44 热区一起退役:卡片本体只剩 + // 「选中资源」与媒体播放钮,卡片 chrome 用例(上一条)继续钉这条口径。 + expect(styles).not.toContain('.game-resource-card-reference'); - // 热区不跟着缩:伪元素撑到 44×44(本仓 window chrome / 素材选择列表同口径的最小可点尺寸)。 - const hitArea = styleRuleBody( - styles, - '\\.game-resource-card-reference::before', - ); - const hitSize = styleNumber(hitArea, 'width'); - expect(hitSize).toBeGreaterThanOrEqual(44); - expect(styleNumber(hitArea, 'height')).toBeGreaterThanOrEqual(44); - expect(hitSize).toBeGreaterThan(referenceSize); - expect(hitArea).toMatch(/position:\s*absolute/u); - - // 热区向左让开后与右侧 34px 播放钮的盒子相邻而不覆盖:不许为了撑热区去挤压播放钮。 - const shiftLeft = Number( - /translate\(calc\(-50% - (\d+)px\), -50%\)/u.exec(hitArea)?.[1], - ); - expect(shiftLeft).toBeGreaterThan(0); - const playButton = - /\.game-resource-card-media-control\s*\{[^}]*right:\s*(\d+)px[^}]*width:\s*(\d+)px[^}]*\}/su.exec( - styles, - ); - expect(playButton).not.toBeNull(); - const playBoxLeftFromRight = - Number(playButton![1]) + Number(playButton![2]); - const hitAreaLeftFromRight = - styleNumber(reference, 'right') + - referenceSize / 2 - - hitSize / 2 + - shiftLeft; - expect(hitAreaLeftFromRight).toBeGreaterThanOrEqual(playBoxLeftFromRight); - - // 语义未动:入口仍在资源卡上,仍是「引用资源