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 f5b099e27..e1e5f215d 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 @@ -500,6 +500,25 @@ export function ResourceCanvasAssetGenerationPanelView({ {resolveEditorImageSizeLabel({ aspectRatio, imageSize })} )} + {referenceEnabled ? null : ( + /* + 不给 `@` 的入口要**当场**说清为什么,否则用户只会读成「这里漏了一个按钮」 + (验收现场那条「生成图标素材没法 @」就是这么来的)。 + + 原因在通道而不在 UI:图集走平台 `icon-spritesheets` 通道,提交体只有一张 + `referenceId`(独立权威规范图)+ `iconDescriptions` + 切片参数,客户端这一侧对 + 「图集带用户参考」是**显式拒绝**(`normalize_platform_art_reference_asset_ids`: + 「透明美术图集只接受规范图引用,不接受用户参考素材」),所以放开 UI 也提交不出去。 + 判据与文案都由同一处给出,别的 reference-free 入口将来接入时不必各写一份。 + */ +

+ 该入口走平台图集通道:参考只有一张权威规范图(图标规范), + 不支持另挑参考图——把需要哪些图标写进提示词。 +

+ )} {referenceProblemNotice ? (

{ screen.queryByRole('button', { name: '插入素材引用' }) !== null, `${candidate.id}(${candidate.label})`, ).toBe(resourceCanvasAssetGenerationAcceptsReferences(candidate)); + // 说明行与选择器互补:有 @ 就不该出现「为什么不给 @」,反之必须在场。 + expect( + screen.queryByText(/不支持另挑参考图/u) !== null, + `${candidate.id}(${candidate.label})的参考口径说明`, + ).toBe(!resourceCanvasAssetGenerationAcceptsReferences(candidate)); view.unmount(); } }); @@ -405,10 +410,28 @@ describe('生成面板的参考接线', () => { ); const panel = screen.getByRole('dialog', { name: '生成图标素材' }); - expect(panel.textContent).not.toContain('参考图'); + /* + * 「不呈现参考选择」的判据落在**计数与选择器**上,不落在「正文里出现过『参考图』三个字」上: + * 这一档现在会有一段说明行解释为什么不给 `@`(「…不支持另挑参考图…」),按词匹配会把 + * 说明行也一起判失败。 + */ + expect( + panel.querySelector('[data-resource-canvas-generation-reference-count]'), + ).toBeNull(); + expect(panel.textContent).not.toMatch(/参考图\s*\d+\s*\/\s*\d+/u); const prompt = screen.getByLabelText('生成提示词'); expect((prompt as HTMLTextAreaElement).tagName).toBe('TEXTAREA'); + /* + * 不给 `@` 的入口必须当场说清为什么:验收现场那条「生成图标素材没法 @」读起来像漏了一个 + * 按钮,真实原因却在那条平台通道上(只有一张权威规范图 `referenceId` + `iconDescriptions`, + * 客户端对图集的用户参考是显式拒绝)。说明行带 `data-*` 标记,便于逐条入口对照判据。 + */ + expect( + panel.querySelector('[data-resource-canvas-generation-reference-free]') + ?.textContent, + ).toContain('权威规范图'); + await user.click( within(panel).getByRole('button', { name: '生成图标素材' }), ); diff --git a/docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md b/docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md index 930e0f16d..1de78d7c0 100644 --- a/docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md +++ b/docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md @@ -60,6 +60,7 @@ - **复用**:比例 / 尺寸选项与默认值来自网页端美术画布的纯模型 `src/components/image-editor/ImageCanvasGenerationModel.ts`(`EDITOR_IMAGE_DIMENSION_OPTIONS` / `IMAGE_MODEL_NANOBANANA2`),尺寸标签复用 `resolveEditorImageSizeLabel`;提示词上限复用 AGC 自己的 `resourceEditPromptMaxLength`(图片类默认 32000,与 Rust `LOCAL_PROJECT_ASSET_MAX_PROMPT_CHARS` 同口径);「AI 润色」复用 `ResourcePromptPolishSlot`(`polish_local_project_prompt`);面板外壳复用 AGC 的 `ThemedModal`(与「生成素材」面板同一套宿主 chrome)。 - **参考图(`@` 引用)**:提示词输入区就是聊天 / 快速编辑同一个共享组件 `ResourceReferenceInput`(`@` 按钮的文案是「插入素材引用」,候选集只放当前项目已登记的位图,SVG 与未落盘条目排除)。是否挂它只由**一条**判据决定:`resourceCanvasAssetGenerationAcceptsReferences`(前端)与 `platform_art_asset_kind_accepts_user_reference_assets`(Rust)——**唯一例外是图集 `icon-spritesheet`**(合同只接受单张权威规范引用,原生对多余参考是显式拒绝)。图标规范 `icon-spec` 虽然产出权威规范图,但生成时同样可以带参考,上限与普通生成一致(5 张;带规范前置的入口是「权威规范图 1 张 + 用户参考 4 张」)。面板里那句「图标规范不给选择器」是 kind 词汇收敛前的残留注释,已按现行判据改写,并补了「逐条入口的 `@` 与判据一致」的用例防止再漂。 +- **图集(生成图标素材)为什么没有 `@`**:图集走的是**另一条平台通道** `POST /api/external/v1/editor/icon-spritesheets/generations`,客户端提交体是 `referenceId`(唯一一张独立权威规范图)+ `iconDescriptions` + 切片参数(`sliceMode` / `sliceCount` / `gridX` / `gridY` / `screenColor`),**没有** `referenceImageSrcs`;结果绑定阶段还硬校验「参考集合恰好一张、且不等于产物自身」(`strict spritesheet 图集必须绑定唯一且独立的 art-spec resourceId`)。也就是说图集的「参考」在客户端合同里被定义为那一张权威规范图,图标内容靠 `iconDescriptions`(由提示词派生)描述。因此前端不给选择器、原生对用户参考是**显式拒绝**(错误原文:`透明美术图集只接受规范图引用,不接受用户参考素材`),而不是静默丢弃;面板里那句「该入口走平台图集通道……」就是这条口径的当面说明。**可行但未做**:平台 OpenAPI 的 `EditorIconSpritesheetGenerationRequest` 里**有** `referenceImageSrcs` 字段(客户端目前不发),所以「放开图集的用户参考」是产品 + 平台口径变更,需要同时改 Rust(判据 / 请求体 / 结果绑定校验与上限)与前端,不在本轮范围内。 - **收窄**:本地 IPC 的比例白名单是 `1:1 / 2:3 / 3:2 / 9:16 / 16:9`,网页端还有 `4:3`。前端做的是「主站纯模型 ∩ 本地白名单」,不是另抄一份选项表(测试钉住两者包含关系与 `4:3` 缺席)。 - **没有复用到的**:网页端的生成 composer 子视图(`ImageCanvasBasicGenerationComposerView` / `ImageCanvasCharacterGenerationComposerView` / `ImageCanvasIconSpritesheetComposerView` / `ImageCanvasSpecGenerationPanelView` / `ImageCanvasGenerationImageOptionsView`)。原因有二:① 它们的比例选项含本地通道拒绝的 `4:3`;② 它们自带模型选择器与 BFF 直连(`ImageCanvasSpecGenerationPanelView` 直连 `/api/editor/llm/icon-specs/*`,AGC 无该通道),本地 IPC 没有 `model` 入参,照搬会渲染一批改不了请求的假控件。参考图入参**不属于**这类缺口:AGC 侧由 `referenceAssetIds` 交原生按当前账号重新绑定(见上一条),面板照常呈现 `@`。因此面板是 AGC 自己的薄壳,只把**纯模型**接进来。 - **不做的**:不改 Rust、不改 external v1 / OpenAPI、不改 `packages/`(共享包只被消费)、不复制主站 `src/components/image-editor/` 目录。