04e5512777
- 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 不同也能命中)。
122 lines
3.2 KiB
TypeScript
122 lines
3.2 KiB
TypeScript
import type {
|
|
GameCreationAppAssetManifestEntry,
|
|
GameCreationAppManifest,
|
|
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
|
import {
|
|
GAME_CREATION_APP_UI_DESIGN_ASSET_KIND,
|
|
isGameCreationAppUiDesignDocAsset,
|
|
parseGameCreationAppAssetKind,
|
|
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
|
|
|
export type UiDesignResourceBridgeResult = {
|
|
asset: GameCreationAppAssetManifestEntry;
|
|
manifest: GameCreationAppManifest;
|
|
committedProjectRevision: number;
|
|
created: boolean;
|
|
};
|
|
|
|
export type UiDesignResourceBridgeInvoke = <T>(
|
|
command: string,
|
|
args?: Record<string, unknown>,
|
|
) => Promise<T>;
|
|
|
|
function uiWorkflowStagePriority(
|
|
generationKind: string | null | undefined,
|
|
): number {
|
|
switch (generationKind) {
|
|
case 'ui-workflow.completed':
|
|
return 3;
|
|
case 'ui-workflow.binding-ready':
|
|
return 2;
|
|
case 'ui-workflow.reference-ready':
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
export function findLinkedUiDesignResource(
|
|
manifest: GameCreationAppManifest,
|
|
prototypeAssetId: string,
|
|
) {
|
|
const normalizedId = prototypeAssetId.trim();
|
|
if (!normalizedId) return null;
|
|
const prototype = manifest.assets.find((asset) => asset.id === normalizedId);
|
|
const referenceIds = new Set(
|
|
[
|
|
normalizedId,
|
|
prototype?.source.resourceId,
|
|
prototype?.source.assetObjectId,
|
|
].filter((value): value is string => Boolean(value?.trim())),
|
|
);
|
|
return (
|
|
manifest.assets
|
|
.filter(
|
|
(asset) =>
|
|
isGameCreationAppUiDesignDocAsset(asset) &&
|
|
asset.source.referenceResourceIds?.some((reference) =>
|
|
referenceIds.has(reference),
|
|
),
|
|
)
|
|
.sort(
|
|
(left, right) =>
|
|
uiWorkflowStagePriority(right.source.generationKind) -
|
|
uiWorkflowStagePriority(left.source.generationKind),
|
|
)[0] ?? null
|
|
);
|
|
}
|
|
|
|
export async function ensureUiDesignResourceForPrototype({
|
|
projectPath,
|
|
manifest,
|
|
prototypeAssetId,
|
|
invoke,
|
|
}: {
|
|
projectPath: string;
|
|
manifest: GameCreationAppManifest;
|
|
prototypeAssetId: string;
|
|
invoke: UiDesignResourceBridgeInvoke;
|
|
}): Promise<UiDesignResourceBridgeResult> {
|
|
const normalizedPrototypeAssetId = prototypeAssetId.trim();
|
|
if (!normalizedPrototypeAssetId) {
|
|
throw new Error('UI 原型资产身份不能为空');
|
|
}
|
|
const prototype = manifest.assets.find(
|
|
(asset) => asset.id === normalizedPrototypeAssetId,
|
|
);
|
|
if (
|
|
!prototype ||
|
|
parseGameCreationAppAssetKind(
|
|
prototype.kind,
|
|
'ui-design-resource-bridge.prototype',
|
|
) !== GAME_CREATION_APP_UI_DESIGN_ASSET_KIND
|
|
) {
|
|
throw new Error('目标资源不是 UI 原型图片');
|
|
}
|
|
if (!prototype.mediaType.toLowerCase().startsWith('image/')) {
|
|
throw new Error('UI 原型资源必须是图片');
|
|
}
|
|
const linked = findLinkedUiDesignResource(
|
|
manifest,
|
|
normalizedPrototypeAssetId,
|
|
);
|
|
if (linked) {
|
|
return {
|
|
asset: linked,
|
|
manifest,
|
|
committedProjectRevision: 0,
|
|
created: false,
|
|
};
|
|
}
|
|
return invoke<UiDesignResourceBridgeResult>(
|
|
'ensure_ui_design_resource_for_prototype',
|
|
{
|
|
input: {
|
|
projectPath,
|
|
expectedProjectId: manifest.projectId,
|
|
prototypeAssetId: normalizedPrototypeAssetId,
|
|
},
|
|
},
|
|
);
|
|
}
|