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);
-
- // 语义未动:入口仍在资源卡上,仍是「引用资源