diff --git a/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationPanelView.tsx b/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationPanelView.tsx
index 68886de45..f5b099e27 100644
--- a/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationPanelView.tsx
+++ b/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationPanelView.tsx
@@ -235,8 +235,14 @@ export function ResourceCanvasAssetGenerationPanelView({
/**
* 参考选择只对有真实参考能力的入口呈现。
*
- * 图集只接受单张规范引用、图标规范本身就是权威规范图产出方:这两类入口不给选择器,
- * 原生侧同样拒绝额外参考(不是静默丢弃)。
+ * `@` 引用输入区是**共享组件**(`ResourceReferenceInput`,聊天 / 快速编辑 / 这里同一份),
+ * 这一处只是「要不要挂它」的唯一分支:判据在 `resourceCanvasAssetGenerationAcceptsReferences`,
+ * 不在这里另写一套 kind 清单。
+ *
+ * 拒绝用户参考的只有**图集**(`icon-spritesheet`):按合同它只接受单张权威规范引用,
+ * 原生侧对多余参考是**显式拒绝**(不是静默丢弃)。图标规范(`icon-spec`)虽然产出权威规范图,
+ * 但生成时同样可以带参考图,上限与普通生成一致——它**有** `@` 选择器。
+ * 两端必须同一个词:Rust 侧是 `platform_art_asset_kind_accepts_user_reference_assets`。
*/
const referenceEnabled =
resourceCanvasAssetGenerationAcceptsReferences(action);
diff --git a/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationTasksPanelView.tsx b/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationTasksPanelView.tsx
index 539131016..216f8800d 100644
--- a/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationTasksPanelView.tsx
+++ b/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasAssetGenerationTasksPanelView.tsx
@@ -44,12 +44,18 @@ export type ResourceCanvasAssetGenerationTasksPanelViewProps = {
/** 定位到该任务产出的素材卡(宿主复用既有 `pendingResourceFocusRef` 聚焦链)。 */
onFocusTask: (task: ResourceCanvasAssetGenerationTask) => void;
/**
- * 锚点落在哪个工作面:资源栏目画布 / 运行表现层 / UI 编辑器。
+ * 锚点落在哪个工作面:资源总览页 / 资源栏目画布 / 运行表现层 / UI 编辑器。
*
- * 三处右上角的既有 chrome 不同(运行表现层右上角是版本入口、UI 编辑器是整页编辑器壳),
- * 所以坐标由这一档在样式里分档给出;缺省按资源栏目画布算。
+ * 四处的既有 chrome 不同,所以坐标由这一档在样式里分档给出;缺省按资源栏目画布算:
+ * - `canvas-overview`(资源总览 / 画本):顶部没有钉死的栏目标题栏,锚点贴画布顶边内缩;
+ * - `canvas`(资源栏目画布):顶部钉着**整宽**的栏目标题栏(含「返回资源总览」入口),
+ * 锚点必须让开它,否则会压在那条标题栏上(验收现场那条「生成任务和标题栏重叠」);
+ * - `run`:右上角被版本入口占着;
+ * - `editor`:UI 编辑器是整页编辑器壳。
+ *
+ * 两页之间切换时锚点高度走样式里的 transition,不瞬移。
*/
- placement?: 'canvas' | 'run' | 'editor';
+ placement?: 'canvas-overview' | 'canvas' | 'run' | 'editor';
};
/**
diff --git a/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasGenerationPlaceholderCardView.tsx b/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasGenerationPlaceholderCardView.tsx
index 2c0305d3b..503a6489a 100644
--- a/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasGenerationPlaceholderCardView.tsx
+++ b/apps/ai-game-creator-shell/src/features/resource-canvas/ResourceCanvasGenerationPlaceholderCardView.tsx
@@ -2,6 +2,7 @@ import './resourceCanvasGenerationPanel.css';
import { Sparkles, X } from 'lucide-react';
import type { PointerEvent as ReactPointerEvent } from 'react';
+import { useRef } from 'react';
import {
RESOURCE_CANVAS_GENERATION_PLACEHOLDER_STATUS_LABELS,
@@ -44,6 +45,14 @@ export function ResourceCanvasGenerationPlaceholderCardView({
onTogglePanel,
onRemove,
}: ResourceCanvasGenerationPlaceholderCardViewProps) {
+ /**
+ * 这一次点击手势是从哪儿起手的。
+ *
+ * 删除按钮自己会 `stopPropagation`,所以「按在删除按钮上、松手落回卡片」这种手势里,
+ * 浏览器把 `click` 派给两者的共同祖先(也就是这张卡)——删除按钮的删除没发生,卡片的
+ * 「开合生成浮层」却执行了。判据不看移动距离:**从删除按钮起手的手势永远不是「点卡片」**。
+ */
+ const gestureOriginRef = useRef<'card' | 'remove'>('card');
return (
{
+ gestureOriginRef.current = 'card';
+ onPointerDown(event);
+ }}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
onPointerCancel={onPointerCancel}
onLostPointerCapture={onPointerCancel}
onClick={(event) => {
event.stopPropagation();
+ const origin = gestureOriginRef.current;
+ gestureOriginRef.current = 'card';
+ if (origin === 'remove') {
+ return;
+ }
onTogglePanel();
}}
onKeyDown={(event) => {
@@ -87,7 +104,12 @@ export function ResourceCanvasGenerationPlaceholderCardView({