收紧 AGC 资源 kind 类型边界并统一族判据
- 将 manifest 与本地导入回执的资源 kind 改为 ts-rs 生成绑定 - 集中音频与视觉资源族判据并复用 Rust 枚举方法 - 放宽 Unknown 未解析语义并清理上传类型墓碑注释 - 标记共享生成绑定并同步项目决策与实施计划
This commit is contained in:
@@ -468,16 +468,8 @@ pub(crate) fn external_editor_api_credentials_override_is_active() -> bool {
|
||||
}
|
||||
|
||||
/// 上传素材落盘的 manifest `kind`:只由**内容证据**推导(`mediaType` 优先,扩展名兜底)。
|
||||
///
|
||||
/// 不用 `uploaded` 这类**来源词**当类型:它不在 canonical 目录里,会经别名表落到
|
||||
/// `image → unclassified`,把本该归「音频」「文档」的上传素材一起说成图片。
|
||||
///
|
||||
/// 更不能把 `ui` 当"图片的默认类型":`ui → ui-design → ui-interaction` 会把任意上传图片
|
||||
/// 钉死在「UI 交互」栏,而"是不是 UI 素材"跟"扩展名是不是 .png"毫无关系;这类错值还不可恢复
|
||||
/// ——读时自愈只在落盘 `category` 是 `unclassified` 且该 `kind` 能派生出非 `unclassified`
|
||||
/// 分类时才生效,`kind` 本身错时自愈只会把错值放大。
|
||||
///
|
||||
/// 判不出内容类型时返回中性的 `asset`(派生 `unclassified` → 「待归类」),**不猜具体类型**。
|
||||
/// 判不出内容类型时返回 `Unknown`;调用者负责决定是否拒绝写入或保留待后续归类,
|
||||
/// 这里不猜具体类型。
|
||||
fn uploaded_asset_kind(file_name: &str, media_type: &str) -> GameCreationAppAssetKind {
|
||||
let media_type = media_type.trim().to_ascii_lowercase();
|
||||
let extension = Path::new(file_name)
|
||||
@@ -538,8 +530,9 @@ fn uploaded_asset_kind(file_name: &str, media_type: &str) -> GameCreationAppAsse
|
||||
}
|
||||
}
|
||||
|
||||
/// 登记边界的 kind 解析:空白入参按「没有信息」处理,落中性 `image`(→「待归类」);
|
||||
/// 非空但认不出的原值严格收口成 `Unknown`(同样「待归类」)并把原始串交给 reporter 留痕。
|
||||
/// 登记边界的 kind 解析:空白入参按「没有信息」处理,沿用登记入口的 `Image` 默认值;
|
||||
/// 非空但认不出的原值严格收口成 `Unknown` 并把原始串交给 reporter 留痕,
|
||||
/// 由调用者决定是否以错误结束或继续后续归类。
|
||||
///
|
||||
/// 三条外部登记边界必须同口径:平台导入(`commands::imported_platform_asset_kind`)、
|
||||
/// 画板导入(`sync_canvas_project_assets_at`)、外部 `register_local_asset`。
|
||||
|
||||
@@ -3999,17 +3999,7 @@ pub(crate) fn normalize_local_project_raster_resource_at(
|
||||
"sourceSubtype 必须是图片族 kind,无法识别:{value}"
|
||||
));
|
||||
}
|
||||
if !matches!(
|
||||
kind,
|
||||
GameCreationAppAssetKind::Image
|
||||
| GameCreationAppAssetKind::Scene
|
||||
| GameCreationAppAssetKind::Character
|
||||
| GameCreationAppAssetKind::CharacterAnimation
|
||||
| GameCreationAppAssetKind::Icon
|
||||
| GameCreationAppAssetKind::IconSpritesheet
|
||||
| GameCreationAppAssetKind::IconSpec
|
||||
| GameCreationAppAssetKind::UiDesign
|
||||
) {
|
||||
if !kind.is_visual() {
|
||||
return Err(format!(
|
||||
"sourceSubtype 必须是图片族 kind,不能使用:{}",
|
||||
kind.as_str()
|
||||
|
||||
@@ -6,6 +6,7 @@ import { open as openNativeFileDialog } from '@tauri-apps/plugin-dialog';
|
||||
import { X } from 'lucide-react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { GameCreationAppAssetKind } from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import {
|
||||
loadEditorAssetLibrary,
|
||||
resolveClientAssetReadUrl,
|
||||
@@ -58,7 +59,7 @@ type LocalProjectFilesResponse = { files?: LocalProjectFile[] };
|
||||
type ImportAssetResponse = {
|
||||
id: string;
|
||||
localPath: string;
|
||||
assetKind?: string | null;
|
||||
assetKind?: GameCreationAppAssetKind | null;
|
||||
};
|
||||
|
||||
type GenericImportResponse = { assets: ImportAssetResponse[] };
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { FileManagerFile } from '@cubone/react-file-manager';
|
||||
import type { ComponentType, ReactNode } from 'react';
|
||||
|
||||
import type { GameCreationAppAssetKind } from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
|
||||
export type ImportedAsset = {
|
||||
id: string;
|
||||
localPath: string;
|
||||
assetKind: string | null;
|
||||
assetKind: GameCreationAppAssetKind | null;
|
||||
};
|
||||
|
||||
export type AssetSource = 'local' | 'remote';
|
||||
@@ -104,7 +106,7 @@ export type ManifestAsset = {
|
||||
id: string;
|
||||
localPath: string;
|
||||
mediaType: string;
|
||||
kind?: string;
|
||||
kind?: GameCreationAppAssetKind;
|
||||
source?: { kind?: string };
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
type GameCreationAppAssetSourceKind,
|
||||
type GameCreationAppManifest,
|
||||
isGameCreationAppAssetAudioKind,
|
||||
isGameCreationAppAssetVisualKind,
|
||||
selectGameCreationAppReadyTasks,
|
||||
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import { taskRowsFromManifest } from '../agent-runtime';
|
||||
@@ -102,12 +104,9 @@ export function isProjectAudioAsset(
|
||||
asset: GameCreationAppManifest['assets'][number],
|
||||
) {
|
||||
const mediaType = asset.mediaType.toLowerCase();
|
||||
const kind = asset.kind.toLowerCase();
|
||||
return (
|
||||
mediaType.startsWith('audio/') ||
|
||||
kind === 'audio' ||
|
||||
kind === 'sound-effect' ||
|
||||
kind === 'background-music'
|
||||
isGameCreationAppAssetAudioKind(asset.kind)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,14 +114,11 @@ export function isProjectVisualAsset(
|
||||
asset: GameCreationAppManifest['assets'][number],
|
||||
) {
|
||||
const mediaType = asset.mediaType.toLowerCase();
|
||||
const kind = asset.kind.toLowerCase();
|
||||
return (
|
||||
mediaType.startsWith('image/') ||
|
||||
mediaType.startsWith('video/') ||
|
||||
mediaType === 'application/vnd.genarrative.image-sequence' ||
|
||||
kind === 'image' ||
|
||||
kind === 'icon' ||
|
||||
kind === 'character-animation'
|
||||
isGameCreationAppAssetVisualKind(asset.kind)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import type { GameCreationAppAssetKind } from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import type { ImportedAsset } from '../../components/AssetImporter';
|
||||
import {
|
||||
prepareDesignImageBatch,
|
||||
@@ -78,7 +79,11 @@ import { useUiEditorNodeFocus } from './useUiEditorNodeFocus';
|
||||
const SEPARATION_IMPORT_BATCH_SIZE = 100;
|
||||
|
||||
type LocalImageImportResponse = {
|
||||
assets: Array<{ id: string; localPath: string; assetKind?: string | null }>;
|
||||
assets: Array<{
|
||||
id: string;
|
||||
localPath: string;
|
||||
assetKind?: GameCreationAppAssetKind | null;
|
||||
}>;
|
||||
};
|
||||
|
||||
function normalizeProjectRelativePath(path: string): string {
|
||||
@@ -1074,7 +1079,11 @@ export function useUiEditorSession(
|
||||
];
|
||||
const importedByPath = new Map<
|
||||
string,
|
||||
{ id: string; localPath: string; assetKind: string | null }
|
||||
{
|
||||
id: string;
|
||||
localPath: string;
|
||||
assetKind: GameCreationAppAssetKind | null;
|
||||
}
|
||||
>();
|
||||
for (
|
||||
let index = 0;
|
||||
|
||||
Reference in New Issue
Block a user