修复 AGC 前端资源 kind 判据与用例的 canonical 残留
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m1s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m6s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m14s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m52s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m31s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m50s
Project CI / Repository checks (pull_request) Successful in 4m44s
Project CI / Frontend tests (pull_request) Successful in 6m20s
Project CI / Backend tests (pull_request) Successful in 8m27s
Project CI / Native shell tests (pull_request) Successful in 8m15s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m10s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m1s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m6s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m14s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m52s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m31s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m50s
Project CI / Repository checks (pull_request) Successful in 4m44s
Project CI / Frontend tests (pull_request) Successful in 6m20s
Project CI / Backend tests (pull_request) Successful in 8m27s
Project CI / Native shell tests (pull_request) Successful in 8m15s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m10s
- resource-canvas/resourceCanvasAssetGenerationReferenceModel.ts:「图集不接受用户参考」判据改用 canonical icon-spritesheet,删除残留平台词的字符串清单。 - view/project-development/index.tsx:美术摘要的图片产出判据显式排除 ui-design,候选 UI 原型图不再顶掉「仅完成计划」。 - tests/appSurface/project-development.suite.ts:generateCall 支持按素材名定位,规范 / 图集 / UI 设计图入口断言改用 canonical kind。 - tests/appSurface/project-assets.suite.ts:/asset-register 与开发面板用例改用 canonical kind,面板默认值按 unknown 断言。 - tests/resourceCanvasAssetGenerationReferences.test.tsx:夹具与提交断言改用 icon-spritesheet / ui-design。 - docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md:载荷表与规范通道说明改用 canonical kind,并写明平台请求词汇只留在 source.generationKind。 - docs/project-memory/shared-memory/pitfalls.md:记录前端 kind 判据与 fixture 必须一起按 canonical 成员重写。
This commit is contained in:
+4
-8
@@ -23,21 +23,17 @@ export const RESOURCE_CANVAS_ASSET_GENERATION_MAX_USER_REFERENCES_WITH_SPEC = 4;
|
||||
/**
|
||||
* 不接受用户参考的生成类型。
|
||||
*
|
||||
* 目前只有 `art-spritesheet`(图集)按合同只接受单张规范引用:这类入口不呈现用户参考选择器,
|
||||
* 原生提交也会显式拒绝额外参考(不是静默丢弃)。
|
||||
* 目前只有 `icon-spritesheet`(图集)按合同只接受单张规范引用:这类入口不呈现用户参考选择器,
|
||||
* 原生提交也会显式拒绝额外参考(不是静默丢弃)。判据直接用 manifest 的 canonical kind,
|
||||
* 与 Rust 侧 `platform_art_asset_kind_accepts_user_reference_assets` 同一个词。
|
||||
*
|
||||
* 其它入口一律支持用户参考,包括 `icon-spec`(图标规范):它虽然产出权威规范图,但生成时同样
|
||||
* 可以带参考图,上限与普通生成一致。
|
||||
*/
|
||||
const RESOURCE_CANVAS_ASSET_GENERATION_REFERENCE_FREE_KINDS: readonly string[] =
|
||||
['art-spritesheet'];
|
||||
|
||||
export function resourceCanvasAssetGenerationAcceptsReferences(
|
||||
action: ResourceCanvasAssetToolAction,
|
||||
): boolean {
|
||||
return !RESOURCE_CANVAS_ASSET_GENERATION_REFERENCE_FREE_KINDS.includes(
|
||||
action.assetKind,
|
||||
);
|
||||
return action.assetKind !== 'icon-spritesheet';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3938,10 +3938,17 @@ export default function ProjectDevelopmentView({
|
||||
resourceRenameAsset,
|
||||
],
|
||||
);
|
||||
/**
|
||||
* 「美术 Agent 是否已产出图片」= 图片类资产里排除待视觉验收的 UI 原型图。
|
||||
*
|
||||
* `ui-design` 是候选界面图(验收通过才成为正式界面图),不是美术图片产出;把它算进来会
|
||||
* 让「美术资源计划已完成,尚未生成或登记图片」被一张原型图顶掉。
|
||||
*/
|
||||
const hasRegisteredArtImageAssets = manifest.assets.some(
|
||||
(asset) =>
|
||||
asset.mediaType.startsWith('image/') &&
|
||||
isGameCreationAppAssetVisualKind(asset.kind),
|
||||
isGameCreationAppAssetVisualKind(asset.kind) &&
|
||||
asset.kind !== GAME_CREATION_APP_UI_DESIGN_ASSET_KIND,
|
||||
);
|
||||
const allAgentSummaries = [
|
||||
summarizeAgent(manifest, 'design', '设计实现 Agent'),
|
||||
|
||||
@@ -396,14 +396,14 @@ export function registerProjectAssetTests() {
|
||||
).not.toBeNull();
|
||||
invoke.mockClear();
|
||||
|
||||
await submitChat('/asset-register assets/hero.png sprite image/png');
|
||||
await submitChat('/asset-register assets/hero.png image image/png');
|
||||
|
||||
expect(
|
||||
await screen.findByText('asset.register · assets/hero.png'),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'登记 /tmp/authorized-game/assets/hero.png · sprite · image/png',
|
||||
'登记 /tmp/authorized-game/assets/hero.png · image · image/png',
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(invoke).not.toHaveBeenCalledWith(
|
||||
@@ -419,7 +419,7 @@ export function registerProjectAssetTests() {
|
||||
expect(invoke).toHaveBeenCalledWith('register_local_asset', {
|
||||
projectPath: '/tmp/authorized-game',
|
||||
localPath: 'assets/hero.png',
|
||||
kind: 'sprite',
|
||||
kind: 'image',
|
||||
mediaType: 'image/png',
|
||||
sourceKind: 'generated',
|
||||
canvasProjectId: '',
|
||||
@@ -547,7 +547,7 @@ export function registerProjectAssetTests() {
|
||||
).not.toBeNull();
|
||||
invoke.mockClear();
|
||||
|
||||
await submitChat('/asset-register assets/hero.png sprite image/png');
|
||||
await submitChat('/asset-register assets/hero.png image image/png');
|
||||
|
||||
expect(
|
||||
await screen.findByText('准备登记项目资产:assets/hero.png'),
|
||||
@@ -571,7 +571,7 @@ export function registerProjectAssetTests() {
|
||||
expect(invoke).toHaveBeenCalledWith('register_local_asset', {
|
||||
projectPath: '/tmp/authorized-game',
|
||||
localPath: 'assets/hero.png',
|
||||
kind: 'sprite',
|
||||
kind: 'image',
|
||||
mediaType: 'image/png',
|
||||
sourceKind: 'generated',
|
||||
canvasProjectId: '',
|
||||
@@ -675,7 +675,9 @@ export function registerProjectAssetTests() {
|
||||
expect(invoke).toHaveBeenCalledWith('register_local_asset', {
|
||||
projectPath: '/tmp/genarrative-ai-game-draft',
|
||||
localPath: 'assets/hero.png',
|
||||
kind: 'asset',
|
||||
// 面板默认「资产类型」是 `unknown`(待归类):写入口径允许先落 Unknown,
|
||||
// 由后续归类决定怎么处理,不在这里编一个非 canonical 的别名。
|
||||
kind: 'unknown',
|
||||
mediaType: 'application/octet-stream',
|
||||
sourceKind: 'generated',
|
||||
canvasProjectId: '',
|
||||
|
||||
@@ -2,6 +2,7 @@ import userEvent from '@testing-library/user-event';
|
||||
import { createRef } from 'react';
|
||||
|
||||
import type {
|
||||
GameCreationAppAssetKind,
|
||||
ProjectResourceCanvasLayout,
|
||||
ProjectResourceCanvasPosition,
|
||||
} from '../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
@@ -370,16 +371,27 @@ async function submitBottomToolbarPanel(
|
||||
*
|
||||
* 后台化之后「提交」这一步是 `start_local_project_asset_generation`(入参校验 + 落项目内账本),
|
||||
* 所以载荷断言看的是它;生成本身由 Rust 后台任务跑。
|
||||
*
|
||||
* 三条规范类入口共用同一个 canonical kind(本地 IPC 只有一条规范通道),kind 不足以定位,
|
||||
* 所以再按 `assetName` 挑一次,避免取到同 kind 的另一条调用。
|
||||
*/
|
||||
function generateCall(calls: readonly BottomToolbarInvokeCall[], kind: string) {
|
||||
function generateCall(
|
||||
calls: readonly BottomToolbarInvokeCall[],
|
||||
kind: GameCreationAppAssetKind,
|
||||
assetName?: string,
|
||||
) {
|
||||
const call = calls.find(
|
||||
(entry) =>
|
||||
entry.command === 'start_local_project_asset_generation' &&
|
||||
invokeStringField(entry.args, 'kind') === kind,
|
||||
invokeStringField(entry.args, 'kind') === kind &&
|
||||
(assetName === undefined ||
|
||||
invokeStringField(entry.args, 'assetName') === assetName),
|
||||
);
|
||||
if (!call?.args) {
|
||||
throw new Error(
|
||||
`missing start_local_project_asset_generation call for ${kind}`,
|
||||
`missing start_local_project_asset_generation call for ${
|
||||
assetName ?? kind
|
||||
}`,
|
||||
);
|
||||
}
|
||||
return call.args;
|
||||
@@ -12101,7 +12113,7 @@ export function registerProjectAgentStatusTests() {
|
||||
menu: '生成规范',
|
||||
prompt: '像素月光厨房统一视觉规范',
|
||||
});
|
||||
expect(generateCall(calls, 'icon-spec')).toMatchObject({
|
||||
expect(generateCall(calls, 'icon-spec', '图标规范')).toMatchObject({
|
||||
kind: 'icon-spec',
|
||||
prompt: '像素月光厨房统一视觉规范',
|
||||
aspectRatio: '1:1',
|
||||
@@ -12110,13 +12122,14 @@ export function registerProjectAgentStatusTests() {
|
||||
outputPath: 'assets/art-spec.png',
|
||||
});
|
||||
|
||||
// 3) 生成规范 → 自定义规范:规格档固定展示 1:1·1K,不带比例选择器。
|
||||
// 3) 生成规范 → 自定义规范:规格档固定展示 1:1·1K,不带比例选择器;与上面那条
|
||||
// 共用 `icon-spec` 通道,只有素材名能区分。
|
||||
await submitBottomToolbarPanel('自定义规范', {
|
||||
menu: '生成规范',
|
||||
prompt: '场景与道具的统一色彩规范',
|
||||
});
|
||||
expect(generateCall(calls, 'spec')).toMatchObject({
|
||||
kind: 'spec',
|
||||
expect(generateCall(calls, 'icon-spec', '自定义规范')).toMatchObject({
|
||||
kind: 'icon-spec',
|
||||
prompt: '场景与道具的统一色彩规范',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '1K',
|
||||
@@ -12191,7 +12204,7 @@ export function registerProjectAgentStatusTests() {
|
||||
await submitBottomToolbarPanel('生成图标素材', {
|
||||
prompt: '各种敌人头像:骷髅 哥布林 强盗',
|
||||
});
|
||||
expect(generateCall(calls, 'art-spritesheet')).toMatchObject({
|
||||
expect(generateCall(calls, 'icon-spritesheet')).toMatchObject({
|
||||
kind: 'icon-spritesheet',
|
||||
prompt: '各种敌人头像:骷髅 哥布林 强盗',
|
||||
aspectRatio: '1:1',
|
||||
@@ -12204,7 +12217,7 @@ export function registerProjectAgentStatusTests() {
|
||||
await submitBottomToolbarPanel('生成 UI 设计图', {
|
||||
prompt: '横屏单屏界面,含 HUD 与操作控件',
|
||||
});
|
||||
expect(generateCall(calls, 'ui-prototype')).toMatchObject({
|
||||
expect(generateCall(calls, 'ui-design')).toMatchObject({
|
||||
kind: 'ui-design',
|
||||
prompt: '横屏单屏界面,含 HUD 与操作控件',
|
||||
aspectRatio: '16:9',
|
||||
@@ -12219,7 +12232,7 @@ export function registerProjectAgentStatusTests() {
|
||||
menu: '生成规范',
|
||||
prompt: '第二版图标规范',
|
||||
});
|
||||
expect(generateCall(calls, 'icon-spec')).toMatchObject({
|
||||
expect(generateCall(calls, 'icon-spec', '图标规范')).toMatchObject({
|
||||
kind: 'icon-spec',
|
||||
assetName: '图标规范',
|
||||
outputPath: null,
|
||||
@@ -12241,10 +12254,10 @@ export function registerProjectAgentStatusTests() {
|
||||
menu: '生成规范',
|
||||
prompt: '三角色头身比与服装规范',
|
||||
});
|
||||
// 本地 IPC 只有一条规范通道(spec),没有 specType 入参:角色规范与自定义规范共用它,
|
||||
// 靠素材名与提示词区分。
|
||||
expect(generateCall(calls, 'spec')).toMatchObject({
|
||||
kind: 'spec',
|
||||
// 本地 IPC 只有一条规范通道(`icon-spec`),没有 specType 入参:角色规范与自定义规范
|
||||
// 共用它,靠素材名与提示词区分。
|
||||
expect(generateCall(calls, 'icon-spec', '角色规范')).toMatchObject({
|
||||
kind: 'icon-spec',
|
||||
prompt: '三角色头身比与服装规范',
|
||||
assetName: '角色规范',
|
||||
outputPath: null,
|
||||
|
||||
@@ -62,13 +62,13 @@ const iconSpecAction = action({
|
||||
const uiPrototypeAction = action({
|
||||
id: 'generate-ui-prototype',
|
||||
label: '生成 UI 设计图',
|
||||
assetKind: 'ui-prototype',
|
||||
assetKind: 'ui-design',
|
||||
requiresIconSpecReference: true,
|
||||
});
|
||||
const spritesheetAction = action({
|
||||
id: 'generate-icon-spritesheet',
|
||||
label: '生成图标素材',
|
||||
assetKind: 'art-spritesheet',
|
||||
assetKind: 'icon-spritesheet',
|
||||
requiresIconSpecReference: true,
|
||||
});
|
||||
|
||||
@@ -295,7 +295,7 @@ describe('生成面板的参考接线', () => {
|
||||
);
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
expect(onSubmit.mock.calls[0]?.[0]).toMatchObject({
|
||||
kind: 'art-spritesheet',
|
||||
kind: 'icon-spritesheet',
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user