UI 编辑器文档资产的 kind + mediaType 判据收敛到共享契约唯一实现
- packages/shared/src/contracts/gameCreationApp.ts:新增 `isGameCreationAppUiDesignDocAsset(asset)`,`kind` 严格解析后再比文档 kind、`mediaType` 比 `application/json`,这是该判据的唯一定义。 - packages/shared/src/contracts/gameCreationApp.test.ts:补用例钉住判据——只满足一半(kind 对 / mediaType 不符、mediaType 对 / kind 不是文档)与 legacy kind(`UI`、`ui`、带空格、大写)一律返回 false。 - apps/ai-game-creator-shell/src/view/project-development/index.tsx:桥接结果校验与「自动打开 ui-workflow 完成资源」两处改用共享判据,删掉本地 kind/mediaType 双条件。 - apps/ai-game-creator-shell/src/features/ui-editor/uiDesignResourceBridge.ts:`findLinkedUiDesignResource` 的过滤条件改用共享判据。 - apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx:缩略图文档分支改用共享判据(原来只看 mediaType,kind 不同也能命中)。
This commit is contained in:
+2
-2
@@ -53,10 +53,10 @@ import { createPortal, flushSync } from 'react-dom';
|
||||
import { PlatformResourceFilterBar } from '../../../../../packages/shared/src/components/PlatformResourceFilterBar';
|
||||
import { PlatformSegmentedTabs } from '../../../../../packages/shared/src/components/PlatformSegmentedTabs';
|
||||
import {
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
type GameCreationAppAssetManifestEntry,
|
||||
gameCreationAppAssetTags,
|
||||
type GameIterationVersion,
|
||||
isGameCreationAppUiDesignDocAsset,
|
||||
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import { resolveTauriInvoke } from '../../app/tauri';
|
||||
import {
|
||||
@@ -1311,7 +1311,7 @@ function ResourcePickerThumbnail({
|
||||
if (mediaType.startsWith('image/')) {
|
||||
return <Loader2 size={18} className="animate-spin" aria-hidden="true" />;
|
||||
}
|
||||
if (mediaType === GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE) {
|
||||
if (asset && isGameCreationAppUiDesignDocAsset(asset)) {
|
||||
return <FileText size={18} aria-hidden="true" />;
|
||||
}
|
||||
return <ImageIcon size={18} aria-hidden="true" />;
|
||||
|
||||
@@ -4,8 +4,7 @@ import type {
|
||||
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import {
|
||||
GAME_CREATION_APP_UI_DESIGN_ASSET_KIND,
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
isGameCreationAppUiDesignDocAsset,
|
||||
parseGameCreationAppAssetKind,
|
||||
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
|
||||
@@ -54,11 +53,7 @@ export function findLinkedUiDesignResource(
|
||||
manifest.assets
|
||||
.filter(
|
||||
(asset) =>
|
||||
parseGameCreationAppAssetKind(
|
||||
asset.kind,
|
||||
'ui-design-resource-bridge.linked-document',
|
||||
) === GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND &&
|
||||
asset.mediaType === GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE &&
|
||||
isGameCreationAppUiDesignDocAsset(asset) &&
|
||||
asset.source.referenceResourceIds?.some((reference) =>
|
||||
referenceIds.has(reference),
|
||||
),
|
||||
|
||||
@@ -83,10 +83,10 @@ import type {
|
||||
import {
|
||||
GAME_CREATION_APP_UI_DESIGN_ASSET_KIND,
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import {
|
||||
isGameCreationAppAssetKind,
|
||||
isGameCreationAppUiDesignDocAsset,
|
||||
parseGameCreationAppAssetKind,
|
||||
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import { ImageCanvasCharacterAnimationPanelView } from '../../../../../src/components/image-editor/ImageCanvasCharacterAnimationPanelView';
|
||||
@@ -5262,8 +5262,7 @@ export default function ProjectDevelopmentView({
|
||||
if (canvasOpenEpochRef.current !== openEpoch) return;
|
||||
if (
|
||||
result.manifest.projectId !== manifest.projectId ||
|
||||
result.asset.kind !== GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND ||
|
||||
result.asset.mediaType !== GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE
|
||||
!isGameCreationAppUiDesignDocAsset(result.asset)
|
||||
) {
|
||||
throw new Error('UI 编辑资源结果与当前项目不一致');
|
||||
}
|
||||
@@ -5400,8 +5399,7 @@ export default function ProjectDevelopmentView({
|
||||
if (uiEditorRoute) return;
|
||||
const completed = manifest.assets.find(
|
||||
(asset) =>
|
||||
asset.kind === GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND &&
|
||||
asset.mediaType === GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE &&
|
||||
isGameCreationAppUiDesignDocAsset(asset) &&
|
||||
asset.source.generationKind === 'ui-workflow.completed',
|
||||
);
|
||||
if (
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
GAME_CREATION_APP_COMMANDS,
|
||||
GAME_CREATION_APP_LIMITED_RUN_COMMANDS,
|
||||
GAME_CREATION_APP_MANIFEST_SCHEMA_VERSION,
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
type GameCreationAgentRunTrace,
|
||||
gameCreationAppAssetCategory,
|
||||
gameCreationAppAssetCategoryForKind,
|
||||
@@ -23,6 +25,7 @@ import {
|
||||
gameCreationAppAssetPersistedCategory,
|
||||
gameCreationAppAssetTags,
|
||||
type GameCreationAppManifest,
|
||||
isGameCreationAppUiDesignDocAsset,
|
||||
normalizeGameCreationAppAssetCategory,
|
||||
normalizeGameCreationAppAssetTags,
|
||||
parseGameCreationAppAssetKind,
|
||||
@@ -795,6 +798,39 @@ describe('AI 游戏创作 App 共享契约', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('识别 UI 编辑器文档资产只认 kind + mediaType 的唯一定义', () => {
|
||||
expect(
|
||||
isGameCreationAppUiDesignDocAsset({
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
// 只满足一半不算文档:kind 对但 mediaType 不符、mediaType 对但 kind 不是文档。
|
||||
expect(
|
||||
isGameCreationAppUiDesignDocAsset({
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: 'text/plain',
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isGameCreationAppUiDesignDocAsset({
|
||||
kind: 'ui-design',
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
// legacy / 非 canonical kind 一律严格收口,不被当作文档(判据只做等值匹配)。
|
||||
for (const raw of ['UI', 'ui', 'ui-design-doc ', 'UI-DESIGN-DOC']) {
|
||||
expect(
|
||||
isGameCreationAppUiDesignDocAsset({
|
||||
kind: raw,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
}),
|
||||
).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('maps every asset kind to a functional category', () => {
|
||||
expect(GAME_CREATION_APP_ASSET_CATEGORIES).toHaveLength(6);
|
||||
expect(Object.keys(GAME_CREATION_APP_ASSET_CATEGORY_BY_KIND)).toHaveLength(
|
||||
|
||||
@@ -499,6 +499,23 @@ export const GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND: GameCreationAppAssetKin
|
||||
export const GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE =
|
||||
'application/json' as const;
|
||||
|
||||
/**
|
||||
* 该 manifest 资产是否是 UI 编辑器文档:`kind` + `mediaType` 双条件是唯一定义。
|
||||
*
|
||||
* 判据原本散在三处(资源画布入口、UI 编辑器桥接、资源引用入口),任一处改了 kind /
|
||||
* mediaType 口径就会出现「能引用但打不开」这类分叉,所以只保留这一份实现。
|
||||
* `kind` 走严格解析:认不出的值收口成 `unknown`,不等于文档 kind。
|
||||
*/
|
||||
export function isGameCreationAppUiDesignDocAsset(
|
||||
asset: Pick<GameCreationAppAssetManifestEntry, 'kind' | 'mediaType'>,
|
||||
): boolean {
|
||||
return (
|
||||
parseGameCreationAppAssetKind(asset.kind, 'ui-design-doc.asset') ===
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND &&
|
||||
asset.mediaType === GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行期 kind 列表:穷举 Record 保证它不可能与生成 union 分叉(漏一个/多一个都编译报错)。
|
||||
* 键顺序即生成声明顺序。kind 字符串表**只有这一份运行期副本**,别名表已删除。
|
||||
|
||||
Reference in New Issue
Block a user