收敛 AGC 资源 kind 写入与读取到正式枚举成员
- assets.rs:`register_local_asset_at` / `register_local_asset_entry` 改为接收 `GameCreationAppAssetKind`,画板导入与画板同步在边界解析 kind - assets.rs:`infer_canvas_export_asset_kind` 直接返回枚举成员,去掉仅供兜底的 canonical 归一 - commands.rs:资源登记按 `asset.register` 上下文解析 kind,字体导入写 `Font`,本地文件类型表与平台导入归一改用枚举 - direct_runtime.rs:直连游戏工程产物统一写 `Code`,图集切片读取按 `Icon` 叠加切片路径判定,陶泥儿美术身份改收枚举 - canvas_generation.rs:新增 `platform_art_asset_manifest_kind` 收口平台美术请求词汇,切片登记 category 改用 `Icon.as_str()` - manifest.rs:必填视觉资产校验改用 `IconSpec` / `UiDesign` / `IconSpritesheet`,修正此前与写入侧对不上的比对 - project_gates.rs、autonomous_policy.rs、recovery_scan.rs、direct_tool_bridge.rs、ui_editor workflow/persistence/resource_bridge:读取侧与 fixture 改用正式枚举成员 - tests:资源相关 fixture 与断言改用正式枚举成员
This commit is contained in:
@@ -63,32 +63,31 @@ const DIRECT_CODEX_ART_ASSET_PATHS: [&str; 3] = [
|
||||
const DIRECT_CODEX_ART_AGENT_ID: &str = "direct-codex-art";
|
||||
const DIRECT_CODEX_HOME_CREATE_PROJECT_MARKER: &str = "[[AGC_CREATE_PROJECT]]";
|
||||
|
||||
fn direct_codex_game_outputs(root: &Path) -> Vec<(String, &'static str, &'static str)> {
|
||||
/// 直连 Codex 生成的游戏工程文件 → manifest 登记项。
|
||||
///
|
||||
/// 这些不是用户素材,而是游戏工程自身的源码/配置(`index.html`、`style.css`、`game.js`、
|
||||
/// `package.json`、`package-lock.json`、`vite.config.js`)。它们的 manifest kind 统一是正式成员
|
||||
/// `code`:`code` 与旧的 `game-*` / `image` 兜底一样派生 `unclassified`(「待归类」),
|
||||
/// 不会把工程文件塞进内容栏目,同时写入侧不再产出非 canonical 的 kind。
|
||||
fn direct_codex_game_outputs(root: &Path) -> Vec<(String, GameCreationAppAssetKind, &'static str)> {
|
||||
let entry = agent_runtime_game_entry_relative_path(root);
|
||||
let prefix = if entry == AGENT_RUNTIME_GAME_ENTRY_ROOT_PATH {
|
||||
""
|
||||
} else {
|
||||
"game/"
|
||||
};
|
||||
let code = GameCreationAppAssetKind::Code;
|
||||
vec![
|
||||
(entry.to_string(), "game-entry", "text/html"),
|
||||
(format!("{prefix}style.css"), "game-style", "text/css"),
|
||||
(format!("{prefix}game.js"), "game-script", "text/javascript"),
|
||||
(
|
||||
format!("{prefix}package.json"),
|
||||
"game-package",
|
||||
"application/json",
|
||||
),
|
||||
(entry.to_string(), code, "text/html"),
|
||||
(format!("{prefix}style.css"), code, "text/css"),
|
||||
(format!("{prefix}game.js"), code, "text/javascript"),
|
||||
(format!("{prefix}package.json"), code, "application/json"),
|
||||
(
|
||||
format!("{prefix}package-lock.json"),
|
||||
"game-lockfile",
|
||||
code,
|
||||
"application/json",
|
||||
),
|
||||
(
|
||||
format!("{prefix}vite.config.js"),
|
||||
"game-build-config",
|
||||
"text/javascript",
|
||||
),
|
||||
(format!("{prefix}vite.config.js"), code, "text/javascript"),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -540,7 +539,7 @@ fn direct_taonier_spritesheet_matches_retained_stage_result_at(
|
||||
let Some(art_spec) = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -550,7 +549,7 @@ fn direct_taonier_spritesheet_matches_retained_stage_result_at(
|
||||
let Some(spritesheet) = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_SPRITESHEET_ASSET_PATH,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"/api/external/v1/editor/icon-spritesheets/generations",
|
||||
"icon-spritesheet",
|
||||
Some(&art_spec),
|
||||
@@ -1986,7 +1985,7 @@ fn direct_taonier_art_generation_runtime_context(
|
||||
fn direct_taonier_art_asset_identity(
|
||||
root: &Path,
|
||||
expected_path: &str,
|
||||
expected_kind: &str,
|
||||
expected_kind: GameCreationAppAssetKind,
|
||||
expected_generation_route: &str,
|
||||
expected_generation_kind: &str,
|
||||
expected_reference_source: Option<&DirectTaonierArtAssetIdentity>,
|
||||
@@ -2094,7 +2093,7 @@ fn direct_taonier_art_base_is_valid(root: &Path) -> bool {
|
||||
let Some(art_spec) = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -2104,7 +2103,7 @@ fn direct_taonier_art_base_is_valid(root: &Path) -> bool {
|
||||
let Some(background) = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
Some(&art_spec),
|
||||
@@ -2119,7 +2118,7 @@ fn direct_taonier_art_package_is_valid(root: &Path) -> bool {
|
||||
let Some(art_spec) = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -2132,7 +2131,7 @@ fn direct_taonier_art_package_is_valid(root: &Path) -> bool {
|
||||
let Some(spritesheet) = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_SPRITESHEET_ASSET_PATH,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"/api/external/v1/editor/icon-spritesheets/generations",
|
||||
"icon-spritesheet",
|
||||
Some(&art_spec),
|
||||
@@ -2183,7 +2182,7 @@ fn direct_registered_taonier_slice_paths(root: &Path) -> Vec<String> {
|
||||
let Some(art_spec) = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -2225,7 +2224,10 @@ fn direct_registered_taonier_slice_paths(root: &Path) -> Vec<String> {
|
||||
return Vec::new();
|
||||
};
|
||||
if assets.next().is_some()
|
||||
|| asset.kind != "art-spritesheet-slice"
|
||||
|| asset.kind != GameCreationAppAssetKind::Icon
|
||||
|| !asset
|
||||
.local_path
|
||||
.starts_with("assets/art-spritesheet-slices/")
|
||||
|| asset.media_type != "image/png"
|
||||
|| asset.source.kind != GameCreationAppAssetSourceKind::Canvas
|
||||
|| asset.source.generation_route.as_deref()
|
||||
@@ -2443,7 +2445,7 @@ fn snapshot_direct_spritesheet_recovery_manifest_for_install(
|
||||
let current_art_spec = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -2462,7 +2464,7 @@ fn register_direct_recovered_spritesheet_at(
|
||||
register_local_asset_at(
|
||||
root,
|
||||
DIRECT_CODEX_SPRITESHEET_ASSET_PATH,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"image/png",
|
||||
"platform-art-recovery",
|
||||
source,
|
||||
@@ -2499,7 +2501,7 @@ fn install_direct_recovered_spritesheet_binding_at(
|
||||
®istered_asset.id,
|
||||
content_sha256,
|
||||
"image/png",
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet.as_str(),
|
||||
)?;
|
||||
let binding = new_external_editor_resource_binding(
|
||||
project_id,
|
||||
@@ -2816,7 +2818,7 @@ async fn generate_direct_taonier_art_asset_at(
|
||||
let art_spec = direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -2976,7 +2978,7 @@ pub(crate) async fn ensure_direct_taonier_art_package_at(
|
||||
direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -2987,7 +2989,7 @@ pub(crate) async fn ensure_direct_taonier_art_package_at(
|
||||
direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
Some(art_spec),
|
||||
@@ -3053,7 +3055,7 @@ pub(crate) async fn ensure_direct_taonier_art_package_at(
|
||||
direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -3071,7 +3073,7 @@ pub(crate) async fn ensure_direct_taonier_art_package_at(
|
||||
|| direct_taonier_art_asset_identity(
|
||||
root,
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
Some(&art_spec),
|
||||
@@ -3802,7 +3804,7 @@ fn sync_direct_codex_project_file_projection_at(
|
||||
register_local_asset_at(
|
||||
root,
|
||||
&local_path,
|
||||
"game-source",
|
||||
GameCreationAppAssetKind::Code,
|
||||
media_type,
|
||||
"direct-codex",
|
||||
direct_codex_generated_source(),
|
||||
@@ -5878,7 +5880,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"replacement-before-crash",
|
||||
Vec::new(),
|
||||
&replacement_spec,
|
||||
@@ -5976,7 +5978,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"strict-crash-spec",
|
||||
Vec::new(),
|
||||
&new_spec,
|
||||
@@ -5990,7 +5992,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"strict-crash-background",
|
||||
vec!["strict-crash-spec".to_string()],
|
||||
&new_background,
|
||||
@@ -6206,7 +6208,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"replacement-before-crash",
|
||||
Vec::new(),
|
||||
&tiny_replacement_png(64, 96, 224),
|
||||
@@ -6289,7 +6291,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"unknown-stage-replacement",
|
||||
Vec::new(),
|
||||
&tiny_replacement_png(224, 64, 32),
|
||||
@@ -6885,7 +6887,7 @@ mod tests {
|
||||
let art_spec = direct_taonier_art_asset_identity(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
None,
|
||||
@@ -6900,7 +6902,7 @@ mod tests {
|
||||
let concurrent = register_local_asset_at(
|
||||
root.path(),
|
||||
concurrent_path,
|
||||
"illustration",
|
||||
GameCreationAppAssetKind::Image,
|
||||
"image/png",
|
||||
"uploaded",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -6933,7 +6935,7 @@ mod tests {
|
||||
let recovery = register_local_asset_at(
|
||||
root.path(),
|
||||
recovery_path,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"image/png",
|
||||
"platform-art-recovery",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -6981,7 +6983,7 @@ mod tests {
|
||||
let registered = register_local_asset_at(
|
||||
root.path(),
|
||||
local_path,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"image/png",
|
||||
"platform-art-recovery",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -7098,14 +7100,18 @@ mod tests {
|
||||
}));
|
||||
}
|
||||
|
||||
fn register_direct_taonier_art_asset_fixture(root: &Path, local_path: &str, kind: &str) {
|
||||
fn register_direct_taonier_art_asset_fixture(
|
||||
root: &Path,
|
||||
local_path: &str,
|
||||
kind: GameCreationAppAssetKind,
|
||||
) {
|
||||
let (generation_route, generation_kind, reference_resource_ids) = match kind {
|
||||
"art-spritesheet" => (
|
||||
GameCreationAppAssetKind::IconSpritesheet => (
|
||||
"/api/external/v1/editor/icon-spritesheets/generations",
|
||||
"icon-spritesheet",
|
||||
vec!["taonier-resource-icon-spec".to_string()],
|
||||
),
|
||||
"game-background" => (
|
||||
GameCreationAppAssetKind::Scene => (
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
vec!["taonier-resource-icon-spec".to_string()],
|
||||
@@ -7116,6 +7122,14 @@ mod tests {
|
||||
Vec::new(),
|
||||
),
|
||||
};
|
||||
// 平台侧资源/对象/任务 id 按生成阶段命名(art-spec / game-background /
|
||||
// art-spritesheet),与 manifest kind 是两套词汇;kind 收紧成枚举后不能让这些
|
||||
// fixture 标识跟着改名。
|
||||
let identity_slug = match kind {
|
||||
GameCreationAppAssetKind::IconSpritesheet => "art-spritesheet",
|
||||
GameCreationAppAssetKind::Scene => "game-background",
|
||||
_ => kind.as_str(),
|
||||
};
|
||||
let mut png_bytes = Vec::new();
|
||||
let image = image::DynamicImage::ImageRgba8(image::RgbaImage::from_pixel(
|
||||
1,
|
||||
@@ -7138,9 +7152,9 @@ mod tests {
|
||||
GameCreationAppAssetSource {
|
||||
kind: GameCreationAppAssetSourceKind::Canvas,
|
||||
canvas_project_id: Some("taonier-project".to_string()),
|
||||
resource_id: Some(format!("taonier-resource-{kind}")),
|
||||
asset_object_id: Some(format!("taonier-object-{kind}")),
|
||||
task_id: Some(format!("taonier-task-{kind}")),
|
||||
resource_id: Some(format!("taonier-resource-{identity_slug}")),
|
||||
asset_object_id: Some(format!("taonier-object-{identity_slug}")),
|
||||
task_id: Some(format!("taonier-task-{identity_slug}")),
|
||||
prompt: None,
|
||||
model: Some("gpt-image-2".to_string()),
|
||||
generation_route: Some(generation_route.to_string()),
|
||||
@@ -7156,7 +7170,7 @@ mod tests {
|
||||
register_direct_taonier_art_asset_fixture(
|
||||
root,
|
||||
DIRECT_CODEX_SPRITESHEET_ASSET_PATH,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
);
|
||||
let main_bytes = std::fs::read(root.join(DIRECT_CODEX_SPRITESHEET_ASSET_PATH))
|
||||
.expect("spritesheet bytes");
|
||||
@@ -7233,7 +7247,7 @@ mod tests {
|
||||
register_local_asset_at(
|
||||
root,
|
||||
path,
|
||||
"art-spritesheet-slice",
|
||||
GameCreationAppAssetKind::Icon,
|
||||
"image/png",
|
||||
"platform-art",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -7345,7 +7359,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root,
|
||||
DIRECT_CODEX_SPRITESHEET_ASSET_PATH,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
&spritesheet_resource_id,
|
||||
vec![art_spec_resource_id.to_string()],
|
||||
&spritesheet_bytes,
|
||||
@@ -7432,19 +7446,20 @@ mod tests {
|
||||
fn replace_direct_taonier_art_asset_fixture(
|
||||
root: &Path,
|
||||
local_path: &str,
|
||||
kind: &str,
|
||||
kind: GameCreationAppAssetKind,
|
||||
resource_id: &str,
|
||||
reference_resource_ids: Vec<String>,
|
||||
bytes: &[u8],
|
||||
) {
|
||||
let (generation_route, generation_kind) = if kind == "art-spritesheet" {
|
||||
(
|
||||
"/api/external/v1/editor/icon-spritesheets/generations",
|
||||
"icon-spritesheet",
|
||||
)
|
||||
} else {
|
||||
("/api/external/v1/editor/images/generations", "spec")
|
||||
};
|
||||
let (generation_route, generation_kind) =
|
||||
if kind == GameCreationAppAssetKind::IconSpritesheet {
|
||||
(
|
||||
"/api/external/v1/editor/icon-spritesheets/generations",
|
||||
"icon-spritesheet",
|
||||
)
|
||||
} else {
|
||||
("/api/external/v1/editor/images/generations", "spec")
|
||||
};
|
||||
std::fs::write(root.join(local_path), bytes).expect("replace fixture bytes");
|
||||
register_local_asset_at(
|
||||
root,
|
||||
@@ -7473,12 +7488,12 @@ mod tests {
|
||||
register_direct_taonier_art_asset_fixture(
|
||||
root,
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
register_direct_taonier_art_asset_fixture(
|
||||
root,
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7930,7 +7945,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"replacement-spec",
|
||||
Vec::new(),
|
||||
&tiny_replacement_png(32, 64, 192),
|
||||
@@ -7941,7 +7956,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"replacement-background",
|
||||
vec!["replacement-spec".to_string()],
|
||||
&tiny_replacement_png(16, 128, 224),
|
||||
@@ -8029,7 +8044,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"durable-replacement-spec",
|
||||
Vec::new(),
|
||||
&tiny_replacement_png(32, 64, 192),
|
||||
@@ -8042,7 +8057,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"durable-replacement-background",
|
||||
vec!["durable-replacement-spec".to_string()],
|
||||
&tiny_replacement_png(16, 128, 224),
|
||||
@@ -8127,7 +8142,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"uncertain-committed-spec",
|
||||
Vec::new(),
|
||||
&replacement,
|
||||
@@ -8187,7 +8202,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_SPRITESHEET_ASSET_PATH,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"uncertain-committed-spritesheet",
|
||||
vec!["taonier-resource-icon-spec".to_string()],
|
||||
&replacement,
|
||||
@@ -8243,7 +8258,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"late-committed-spec",
|
||||
Vec::new(),
|
||||
&tiny_replacement_png(32, 64, 192),
|
||||
@@ -8297,7 +8312,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"replacement-spec-before-background",
|
||||
Vec::new(),
|
||||
&tiny_replacement_png(32, 64, 192),
|
||||
@@ -8316,7 +8331,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"late-committed-background",
|
||||
vec!["replacement-spec-before-background".to_string()],
|
||||
&tiny_replacement_png(16, 128, 224),
|
||||
@@ -8361,7 +8376,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_ART_SPEC_ASSET_PATH,
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"replacement-spec",
|
||||
Vec::new(),
|
||||
&replacement_spec,
|
||||
@@ -8372,7 +8387,7 @@ mod tests {
|
||||
replace_direct_taonier_art_asset_fixture(
|
||||
root.path(),
|
||||
DIRECT_CODEX_BACKGROUND_ASSET_PATH,
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"replacement-background",
|
||||
vec!["replacement-spec".to_string()],
|
||||
&replacement_background,
|
||||
|
||||
@@ -1210,7 +1210,8 @@ fn bridge_list_registered_assets(root: &Path, arguments: &Value) -> Value {
|
||||
arguments,
|
||||
"kind",
|
||||
DIRECT_TOOL_BRIDGE_MAX_RESOURCE_KIND_CHARS,
|
||||
)?;
|
||||
)?
|
||||
.map(|kind| GameCreationAppAssetKind::parse_with_context(&kind, "asset.list.kind"));
|
||||
let asset_id = bridge_optional_bounded_string(
|
||||
arguments,
|
||||
"assetId",
|
||||
@@ -1230,7 +1231,7 @@ fn bridge_list_registered_assets(root: &Path, arguments: &Value) -> Value {
|
||||
let mut assets = manifest
|
||||
.assets
|
||||
.iter()
|
||||
.filter(|asset| kind.as_ref().is_none_or(|kind| asset.kind == *kind))
|
||||
.filter(|asset| kind.is_none_or(|kind| asset.kind == kind))
|
||||
.filter(|asset| {
|
||||
asset_id
|
||||
.as_ref()
|
||||
|
||||
@@ -463,6 +463,27 @@ pub(crate) fn normalize_platform_art_asset_generation_kind(kind: &str) -> Option
|
||||
})
|
||||
}
|
||||
|
||||
/// 平台美术生成请求的 `assetKind` → manifest 资源 kind。
|
||||
///
|
||||
/// `PlatformArtAssetGenerationOptions::asset_kind` 是**平台请求词汇**(`ui-prototype` /
|
||||
/// `art-spritesheet` / `game-background` / `game-art` …),跟 manifest 的正式
|
||||
/// `GameCreationAppAssetKind` 不是同一套词表。生成结果登记进 manifest 之前必须在这里显式
|
||||
/// 转换,否则 manifest 会落进非 canonical 值,读侧只能靠 shared-contracts 的别名表兜底。
|
||||
///
|
||||
/// 词表外的值不猜:返回 `Unknown`,由调用方按既有未知输入口径处理。
|
||||
pub(crate) fn platform_art_asset_manifest_kind(asset_kind: &str) -> GameCreationAppAssetKind {
|
||||
match asset_kind.trim() {
|
||||
"image" | "game-art" => GameCreationAppAssetKind::Image,
|
||||
"character" | "character-art" => GameCreationAppAssetKind::Character,
|
||||
"spec" | "icon-spec" => GameCreationAppAssetKind::IconSpec,
|
||||
"ui-prototype" | "ui-design" => GameCreationAppAssetKind::UiDesign,
|
||||
"art-spritesheet" | "icon-spritesheet" => GameCreationAppAssetKind::IconSpritesheet,
|
||||
"publication-material" => GameCreationAppAssetKind::PublicationMaterial,
|
||||
"game-background" | "scene" => GameCreationAppAssetKind::Scene,
|
||||
_ => GameCreationAppAssetKind::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::agent) fn recover_persisted_visual_generation_options(
|
||||
root: &Path,
|
||||
pending: &AgentRuntimePendingToolAction,
|
||||
@@ -1625,7 +1646,7 @@ async fn canonical_art_spec_reference_at(
|
||||
.iter()
|
||||
.find(|asset| {
|
||||
asset.local_path == AGENT_RUNTIME_ART_SPEC_PATH
|
||||
&& asset.kind == "icon-spec"
|
||||
&& asset.kind == GameCreationAppAssetKind::IconSpec
|
||||
&& asset.media_type.starts_with("image/")
|
||||
&& asset.source.kind == GameCreationAppAssetSourceKind::Canvas
|
||||
})
|
||||
@@ -6820,7 +6841,9 @@ fn register_platform_art_slice_manifest_entries_at(
|
||||
local_path: registration.local_path.clone(),
|
||||
image_sequence_frames: None,
|
||||
image_sequence_duration_ms: None,
|
||||
category: game_creation_app_asset_category_for_kind("art-spritesheet-slice"),
|
||||
category: game_creation_app_asset_category_for_kind(
|
||||
GameCreationAppAssetKind::Icon.as_str(),
|
||||
),
|
||||
tags: Vec::new(),
|
||||
source,
|
||||
});
|
||||
@@ -7555,7 +7578,7 @@ fn commit_prepared_platform_art_asset_with_before_replace_hook(
|
||||
let registered = match register_local_asset_entry(
|
||||
root,
|
||||
&local_path,
|
||||
&options.asset_kind,
|
||||
platform_art_asset_manifest_kind(&options.asset_kind),
|
||||
&download.media_type,
|
||||
"platform-art",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -10499,7 +10522,7 @@ mod canvas_generation_tests {
|
||||
register_local_asset_at(
|
||||
root,
|
||||
"assets/art-spec.png",
|
||||
"icon-spec",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"image/png",
|
||||
"changed-reference-test",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -12185,7 +12208,7 @@ mod canvas_generation_tests {
|
||||
register_local_asset_entry(
|
||||
root,
|
||||
AGENT_RUNTIME_ART_SPRITESHEET_PATH,
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"image/png",
|
||||
"platform-art",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -12251,7 +12274,7 @@ mod canvas_generation_tests {
|
||||
register_local_asset_entry(
|
||||
root,
|
||||
"assets/art-spritesheet-slices/player.png",
|
||||
"art-spritesheet-slice",
|
||||
GameCreationAppAssetKind::Icon,
|
||||
"image/png",
|
||||
"platform-art",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -13807,7 +13830,10 @@ mod canvas_generation_tests {
|
||||
let manifest = read_manifest_for_project(root).expect("read replacement manifest");
|
||||
assert_eq!(manifest.assets.len(), 1);
|
||||
assert_eq!(manifest.assets[0].local_path, "assets/art-spritesheet.png");
|
||||
assert_eq!(manifest.assets[0].kind, "art-spritesheet");
|
||||
assert_eq!(
|
||||
manifest.assets[0].kind,
|
||||
GameCreationAppAssetKind::IconSpritesheet
|
||||
);
|
||||
assert_eq!(
|
||||
fs::read_dir(root.join("assets"))
|
||||
.expect("read assets directory")
|
||||
|
||||
+12
-6
@@ -2061,17 +2061,23 @@ mod tests {
|
||||
.expect("resume first manifest task fixture");
|
||||
|
||||
for (local_path, kind) in [
|
||||
("assets/art-spec.png", "icon-spec"),
|
||||
("assets/ui-prototype.png", "ui-prototype"),
|
||||
("assets/art-spritesheet.png", "art-spritesheet"),
|
||||
("assets/art-spec.png", GameCreationAppAssetKind::IconSpec),
|
||||
(
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
),
|
||||
(
|
||||
"assets/art-spritesheet.png",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
),
|
||||
] {
|
||||
let (generation_route, generation_kind, reference_resource_ids) = match kind {
|
||||
"icon-spec" => (
|
||||
GameCreationAppAssetKind::IconSpec => (
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
Vec::new(),
|
||||
),
|
||||
"ui-prototype" => (
|
||||
GameCreationAppAssetKind::UiDesign => (
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"ui-design",
|
||||
vec!["resource-icon-spec".to_string()],
|
||||
@@ -2082,7 +2088,7 @@ mod tests {
|
||||
vec!["resource-icon-spec".to_string()],
|
||||
),
|
||||
};
|
||||
let bytes = if kind == "art-spritesheet" {
|
||||
let bytes = if kind == GameCreationAppAssetKind::IconSpritesheet {
|
||||
use image::ImageEncoder;
|
||||
let mut bytes = Vec::new();
|
||||
image::codecs::png::PngEncoder::new(&mut bytes)
|
||||
|
||||
@@ -1216,11 +1216,19 @@ pub(in crate::agent) fn visual_asset_completion_blocker_at_locked(
|
||||
return None;
|
||||
}
|
||||
let (expected_path, expected_kind, label) = match agent_id {
|
||||
"art-director" => (AGENT_RUNTIME_ART_SPEC_PATH, "icon-spec", "统一视觉规范图"),
|
||||
"design-foundation" => ("assets/ui-prototype.png", "ui-prototype", "策划界面原型图"),
|
||||
"art-director" => (
|
||||
AGENT_RUNTIME_ART_SPEC_PATH,
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
"统一视觉规范图",
|
||||
),
|
||||
"design-foundation" => (
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"策划界面原型图",
|
||||
),
|
||||
"art-asset-plan" => (
|
||||
"assets/art-spritesheet.png",
|
||||
"art-spritesheet",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
"首版美术素材图",
|
||||
),
|
||||
_ => return None,
|
||||
|
||||
@@ -1561,10 +1561,14 @@ mod orphaned_external_generation_recovery_tests {
|
||||
fs::create_dir_all(workflow_path.parent().expect("workflow parent"))
|
||||
.expect("workflow directory");
|
||||
let rollback_assets = [
|
||||
("assets/art-spec.png", "icon-spec", b"old-spec".as_slice()),
|
||||
(
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
b"old-spec".as_slice(),
|
||||
),
|
||||
(
|
||||
"assets/direct-game-background.png",
|
||||
"game-background",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
b"old-background".as_slice(),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -563,7 +563,7 @@ pub(crate) fn upload_local_asset_at(
|
||||
register_local_asset_entry(
|
||||
root,
|
||||
&relative_path,
|
||||
uploaded_asset_kind(file_name, media_type).as_str(),
|
||||
uploaded_asset_kind(file_name, media_type),
|
||||
media_type,
|
||||
"upload",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -584,7 +584,7 @@ pub(crate) fn upload_local_asset_at(
|
||||
pub(crate) fn register_local_asset_at(
|
||||
root: &Path,
|
||||
local_path: &str,
|
||||
kind: &str,
|
||||
kind: GameCreationAppAssetKind,
|
||||
media_type: &str,
|
||||
source_kind: &str,
|
||||
source: GameCreationAppAssetSource,
|
||||
@@ -647,7 +647,7 @@ pub(crate) fn register_design_artifacts_at(root: &Path) -> Result<usize, String>
|
||||
register_local_asset_at(
|
||||
root,
|
||||
&relative,
|
||||
"document",
|
||||
GameCreationAppAssetKind::Document,
|
||||
media_type,
|
||||
"document",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -686,6 +686,7 @@ pub(crate) fn import_canvas_asset_at(
|
||||
if resource_id.is_none() && asset_object_id.is_none() {
|
||||
return Err("画板资源 ID 和资产对象 ID 至少需要一个".to_string());
|
||||
}
|
||||
let kind = GameCreationAppAssetKind::parse_with_context(kind.trim(), "canvas.asset_import");
|
||||
|
||||
register_local_asset_at(
|
||||
root,
|
||||
@@ -918,7 +919,12 @@ pub(crate) async fn sync_canvas_project_assets_at(
|
||||
&local_path,
|
||||
json_string_field(resource, "assetKind")
|
||||
.as_deref()
|
||||
.unwrap_or("canvas-resource"),
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|value| {
|
||||
GameCreationAppAssetKind::parse_with_context(value, "canvas.asset_sync")
|
||||
})
|
||||
.unwrap_or(GameCreationAppAssetKind::Image),
|
||||
&download.media_type,
|
||||
"canvas",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -1772,33 +1778,28 @@ pub(crate) fn normalized_zip_entry_name(name: &str) -> Result<String, String> {
|
||||
|
||||
/// 画板导出图层推断出的资源 kind。
|
||||
///
|
||||
/// 返回值必须是 **canonical kind**:这个值会被 `register_local_asset_entry` 原样写入
|
||||
/// manifest,并据以派生落盘 `category`。历史上这里写过非 canonical 的
|
||||
/// `ui` / `animation` / `asset`,只能靠别名表兜底,等于同时维护两套词汇;别名表只用于
|
||||
/// 兼容存量数据,不作为新写入值的来源。
|
||||
/// 直接返回正式枚举成员:这个值会被 `register_local_asset_entry` 原样写入 manifest,
|
||||
/// 并据以派生落盘 `category`。分支字面量写成非 canonical 值在这里已经无法表达,
|
||||
/// 不需要别名表兜底。
|
||||
pub(crate) fn infer_canvas_export_asset_kind(
|
||||
layer: &CanvasExportLayerMetadata,
|
||||
file: &str,
|
||||
) -> &'static str {
|
||||
) -> GameCreationAppAssetKind {
|
||||
let layer_type = layer.visible.layer_type.as_str();
|
||||
let inferred = if file.starts_with("sequences/")
|
||||
|| contains_any(layer_type, &["序列", "动画", "动作"])
|
||||
{
|
||||
"character-animation"
|
||||
if file.starts_with("sequences/") || contains_any(layer_type, &["序列", "动画", "动作"]) {
|
||||
GameCreationAppAssetKind::CharacterAnimation
|
||||
} else if file.starts_with("media/") || contains_any(layer_type, &["音频", "音乐", "音效"])
|
||||
{
|
||||
"audio"
|
||||
GameCreationAppAssetKind::Audio
|
||||
} else if contains_any(layer_type, &["角色"]) {
|
||||
"character"
|
||||
GameCreationAppAssetKind::Character
|
||||
} else if contains_any(layer_type, &["场景", "背景"]) {
|
||||
"scene"
|
||||
GameCreationAppAssetKind::Scene
|
||||
} else if contains_any(layer_type, &["UI", "界面", "图标"]) {
|
||||
"ui-design"
|
||||
GameCreationAppAssetKind::UiDesign
|
||||
} else {
|
||||
"image"
|
||||
};
|
||||
// 防御性归一:分支字面量写错时由覆盖测试暴露,这里再兜一层。
|
||||
shared_contracts::game_creation_app::canonical_game_creation_app_asset_kind(inferred)
|
||||
GameCreationAppAssetKind::Image
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn infer_canvas_export_media_type(file: &str) -> &'static str {
|
||||
@@ -1871,7 +1872,7 @@ pub(crate) fn build_canvas_project_url(
|
||||
pub(crate) fn register_local_asset_entry(
|
||||
root: &Path,
|
||||
local_path: &str,
|
||||
kind: &str,
|
||||
kind: GameCreationAppAssetKind,
|
||||
media_type: &str,
|
||||
id_prefix: &str,
|
||||
source: GameCreationAppAssetSource,
|
||||
@@ -1879,10 +1880,6 @@ pub(crate) fn register_local_asset_entry(
|
||||
let normalized_path = normalize_relative_path(local_path)?;
|
||||
let absolute_path = resolve_local_project_path(root, &normalized_path)?;
|
||||
let manifest_path = root.join(".agent/manifest.json");
|
||||
let kind = GameCreationAppAssetKind::parse_with_context(
|
||||
if kind.is_empty() { "unknown" } else { kind },
|
||||
"register_local_asset_entry",
|
||||
);
|
||||
let media_type = if media_type.is_empty() {
|
||||
"application/octet-stream"
|
||||
} else {
|
||||
@@ -2163,13 +2160,21 @@ mod tests {
|
||||
}
|
||||
|
||||
// 每个分支的代表输入 → 期望的 canonical kind。
|
||||
let cases: [(&str, &str, &str); 6] = [
|
||||
("序列", "sequences/01.png", "character-animation"),
|
||||
("动画", "layer.png", "character-animation"),
|
||||
("音频", "layer.png", "audio"),
|
||||
("角色", "layer.png", "character"),
|
||||
("场景", "layer.png", "scene"),
|
||||
("UI", "layer.png", "ui-design"),
|
||||
let cases: [(&str, &str, GameCreationAppAssetKind); 6] = [
|
||||
(
|
||||
"序列",
|
||||
"sequences/01.png",
|
||||
GameCreationAppAssetKind::CharacterAnimation,
|
||||
),
|
||||
(
|
||||
"动画",
|
||||
"layer.png",
|
||||
GameCreationAppAssetKind::CharacterAnimation,
|
||||
),
|
||||
("音频", "layer.png", GameCreationAppAssetKind::Audio),
|
||||
("角色", "layer.png", GameCreationAppAssetKind::Character),
|
||||
("场景", "layer.png", GameCreationAppAssetKind::Scene),
|
||||
("UI", "layer.png", GameCreationAppAssetKind::UiDesign),
|
||||
// 无匹配时落到 image。
|
||||
];
|
||||
for (layer_type, file, expected) in cases {
|
||||
@@ -2179,20 +2184,20 @@ mod tests {
|
||||
// media/ 前缀同样走音频分支。
|
||||
assert_eq!(
|
||||
infer_canvas_export_asset_kind(&layer("图层"), "media/bgm.mp3"),
|
||||
"audio"
|
||||
GameCreationAppAssetKind::Audio
|
||||
);
|
||||
// 兜底分支是 image 而不是旧的 "asset"。
|
||||
assert_eq!(
|
||||
infer_canvas_export_asset_kind(&layer("图层"), "layer.png"),
|
||||
"image"
|
||||
GameCreationAppAssetKind::Image
|
||||
);
|
||||
|
||||
// 所有分支的返回值都必须在 canonical 目录里,且不能是旧的非 canonical 写法。
|
||||
// 所有分支的返回值都必须是正式枚举成员,且不能是旧的非 canonical 写法。
|
||||
for layer_type in ["序列", "音频", "角色", "场景", "UI", "图层", "其他"] {
|
||||
let kind = infer_canvas_export_asset_kind(&layer(layer_type), "layer.png");
|
||||
assert!(
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_CANONICAL_ASSET_KINDS
|
||||
.contains(&kind),
|
||||
.contains(&kind.as_str()),
|
||||
"非 canonical kind: {kind}(layer_type={layer_type})"
|
||||
);
|
||||
for legacy in ["ui", "animation", "asset"] {
|
||||
|
||||
@@ -2111,7 +2111,7 @@ pub(crate) fn register_local_asset(
|
||||
register_local_asset_at(
|
||||
root,
|
||||
local_path.trim(),
|
||||
kind.trim(),
|
||||
GameCreationAppAssetKind::parse_with_context(kind.trim(), "asset.register"),
|
||||
media_type.trim(),
|
||||
source_kind.trim(),
|
||||
GameCreationAppAssetSource {
|
||||
@@ -2181,7 +2181,7 @@ pub(crate) fn create_ui_design_resource(
|
||||
let asset = match register_local_asset_at(
|
||||
root,
|
||||
&relative_path,
|
||||
crate::ui_editor::persistence::UI_DESIGN_DOC_ASSET_KIND,
|
||||
GameCreationAppAssetKind::UiDesignDoc,
|
||||
crate::ui_editor::persistence::UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
"generated",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -2672,7 +2672,7 @@ pub(crate) fn import_ui_editor_local_fonts(
|
||||
let registered = register_local_asset_entry(
|
||||
root,
|
||||
&relative_path,
|
||||
"font",
|
||||
GameCreationAppAssetKind::Font,
|
||||
validated.metadata.format.media_type(),
|
||||
"font",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -2784,7 +2784,7 @@ mod ui_editor_font_tests {
|
||||
let registered = register_local_asset_entry(
|
||||
root,
|
||||
relative_path,
|
||||
"font",
|
||||
GameCreationAppAssetKind::Font,
|
||||
"font/ttf",
|
||||
"font",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -3202,24 +3202,34 @@ mod agent_asset_import_tests {
|
||||
/// 本用例必须变红(`character` / `scene` / 空值 / 未知值四组断言都会失败)。
|
||||
#[test]
|
||||
fn imported_platform_asset_kind_prefers_payload_type_over_ui_default() {
|
||||
assert_eq!(imported_platform_asset_kind(Some("character")), "character");
|
||||
assert_eq!(imported_platform_asset_kind(Some("scene")), "scene");
|
||||
use GameCreationAppAssetKind as Kind;
|
||||
assert_eq!(
|
||||
imported_platform_asset_kind(Some("character")),
|
||||
Kind::Character
|
||||
);
|
||||
assert_eq!(imported_platform_asset_kind(Some("scene")), Kind::Scene);
|
||||
assert_eq!(
|
||||
imported_platform_asset_kind(Some("character-animation")),
|
||||
"character-animation"
|
||||
Kind::CharacterAnimation
|
||||
);
|
||||
assert_eq!(
|
||||
imported_platform_asset_kind(Some("icon-spec")),
|
||||
Kind::IconSpec
|
||||
);
|
||||
assert_eq!(imported_platform_asset_kind(Some("icon-spec")), "icon-spec");
|
||||
// 平台确实说它是 UI 设计稿时,才允许落 `ui-design`(→ 「UI 交互」);
|
||||
// 大写 `UI` 与旧值 `ui` 都要归一到同一口径,但**不允许由我们替它默认**。
|
||||
assert_eq!(imported_platform_asset_kind(Some("UI")), "ui-design");
|
||||
assert_eq!(imported_platform_asset_kind(Some("ui")), "ui-design");
|
||||
assert_eq!(imported_platform_asset_kind(Some("UI")), Kind::UiDesign);
|
||||
assert_eq!(imported_platform_asset_kind(Some("ui")), Kind::UiDesign);
|
||||
// 缺失 / 空白 / 未知值一律落 canonical `image` 兜底(→ 「待归类」)。
|
||||
assert_eq!(imported_platform_asset_kind(None), "image");
|
||||
assert_eq!(imported_platform_asset_kind(Some(" ")), "image");
|
||||
assert_eq!(imported_platform_asset_kind(Some("mystery")), "image");
|
||||
assert_eq!(imported_platform_asset_kind(None), Kind::Image);
|
||||
assert_eq!(imported_platform_asset_kind(Some(" ")), Kind::Image);
|
||||
assert_eq!(imported_platform_asset_kind(Some("mystery")), Kind::Image);
|
||||
// `kind` 是外部输入,原型链键必须走 `image` 兜底,不能取到原型上的值。
|
||||
assert_eq!(imported_platform_asset_kind(Some("__proto__")), "image");
|
||||
assert_eq!(imported_platform_asset_kind(Some("constructor")), "image");
|
||||
assert_eq!(imported_platform_asset_kind(Some("__proto__")), Kind::Image);
|
||||
assert_eq!(
|
||||
imported_platform_asset_kind(Some("constructor")),
|
||||
Kind::Image
|
||||
);
|
||||
}
|
||||
|
||||
/// 反查门禁:两处平台素材导入必须继续用 `imported_platform_asset_kind` 解析 `kind`,
|
||||
@@ -3748,7 +3758,7 @@ pub(crate) async fn list_editor_assets_for_agent_at(
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
struct AgentLocalProjectFileType {
|
||||
category: &'static str,
|
||||
asset_kind: &'static str,
|
||||
asset_kind: GameCreationAppAssetKind,
|
||||
media_type: &'static str,
|
||||
max_file_size: u64,
|
||||
}
|
||||
@@ -3803,14 +3813,14 @@ fn agent_local_project_file_type(
|
||||
// 错值放大成显式的 `ui-interaction`。
|
||||
media_type.map(|media_type| AgentLocalProjectFileType {
|
||||
category: "image",
|
||||
asset_kind: "image",
|
||||
asset_kind: GameCreationAppAssetKind::Image,
|
||||
media_type,
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
})
|
||||
}
|
||||
"ttf" | "otf" | "woff" | "woff2" => Some(AgentLocalProjectFileType {
|
||||
category: "font",
|
||||
asset_kind: "font",
|
||||
asset_kind: GameCreationAppAssetKind::Font,
|
||||
media_type: match extension.as_str() {
|
||||
"ttf" => "font/ttf",
|
||||
"otf" => "font/otf",
|
||||
@@ -3821,68 +3831,68 @@ fn agent_local_project_file_type(
|
||||
}),
|
||||
"mp3" => Some(AgentLocalProjectFileType {
|
||||
category: "audio",
|
||||
asset_kind: "audio",
|
||||
asset_kind: GameCreationAppAssetKind::Audio,
|
||||
media_type: "audio/mpeg",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"wav" => Some(AgentLocalProjectFileType {
|
||||
category: "audio",
|
||||
asset_kind: "audio",
|
||||
asset_kind: GameCreationAppAssetKind::Audio,
|
||||
media_type: "audio/wav",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"ogg" => Some(AgentLocalProjectFileType {
|
||||
category: "audio",
|
||||
asset_kind: "audio",
|
||||
asset_kind: GameCreationAppAssetKind::Audio,
|
||||
media_type: "audio/ogg",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"flac" => Some(AgentLocalProjectFileType {
|
||||
category: "audio",
|
||||
asset_kind: "audio",
|
||||
asset_kind: GameCreationAppAssetKind::Audio,
|
||||
media_type: "audio/flac",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"m4a" => Some(AgentLocalProjectFileType {
|
||||
category: "audio",
|
||||
asset_kind: "audio",
|
||||
asset_kind: GameCreationAppAssetKind::Audio,
|
||||
media_type: "audio/mp4",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"aac" => Some(AgentLocalProjectFileType {
|
||||
category: "audio",
|
||||
asset_kind: "audio",
|
||||
asset_kind: GameCreationAppAssetKind::Audio,
|
||||
media_type: "audio/aac",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"opus" => Some(AgentLocalProjectFileType {
|
||||
category: "audio",
|
||||
asset_kind: "audio",
|
||||
asset_kind: GameCreationAppAssetKind::Audio,
|
||||
media_type: "audio/opus",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"mp4" => Some(AgentLocalProjectFileType {
|
||||
category: "video",
|
||||
asset_kind: "video",
|
||||
asset_kind: GameCreationAppAssetKind::Video,
|
||||
media_type: "video/mp4",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"webm" => Some(AgentLocalProjectFileType {
|
||||
category: "video",
|
||||
asset_kind: "video",
|
||||
asset_kind: GameCreationAppAssetKind::Video,
|
||||
media_type: "video/webm",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"mov" => Some(AgentLocalProjectFileType {
|
||||
category: "video",
|
||||
asset_kind: "video",
|
||||
asset_kind: GameCreationAppAssetKind::Video,
|
||||
media_type: "video/quicktime",
|
||||
max_file_size: UI_EDITOR_IMAGE_MAX_FILE_SIZE,
|
||||
}),
|
||||
"md" | "markdown" | "mdx" | "txt" | "json" | "yaml" | "yml" | "toml" | "csv" | "ini"
|
||||
| "conf" | "xml" => Some(AgentLocalProjectFileType {
|
||||
category: "document",
|
||||
asset_kind: "document",
|
||||
asset_kind: GameCreationAppAssetKind::Document,
|
||||
media_type: if extension == "json" {
|
||||
"application/json"
|
||||
} else if matches!(extension.as_str(), "yaml" | "yml") {
|
||||
@@ -3899,7 +3909,7 @@ fn agent_local_project_file_type(
|
||||
| "swift" | "php" | "rb" | "lua" | "sh" | "bash" | "zsh" | "sql" | "graphql" | "gql"
|
||||
| "vue" | "svelte" => Some(AgentLocalProjectFileType {
|
||||
category: "code",
|
||||
asset_kind: "code",
|
||||
asset_kind: GameCreationAppAssetKind::Code,
|
||||
media_type: if matches!(extension.as_str(), "html" | "htm") {
|
||||
"text/html"
|
||||
} else if matches!(extension.as_str(), "css" | "scss" | "less") {
|
||||
@@ -4017,7 +4027,7 @@ pub(crate) fn import_local_project_assets_for_agent(
|
||||
}
|
||||
inputs.push((
|
||||
local_path,
|
||||
file_type.asset_kind.to_string(),
|
||||
file_type.asset_kind,
|
||||
file_type.media_type.to_string(),
|
||||
bytes,
|
||||
));
|
||||
@@ -4075,7 +4085,7 @@ pub(crate) fn import_local_project_assets_for_agent(
|
||||
let registered = register_local_asset_entry(
|
||||
root,
|
||||
&local_path,
|
||||
&asset_kind,
|
||||
asset_kind,
|
||||
&media_type,
|
||||
"local",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -4094,7 +4104,7 @@ pub(crate) fn import_local_project_assets_for_agent(
|
||||
imported.push(ImportedAsset {
|
||||
id: registered.id,
|
||||
local_path: registered.local_path,
|
||||
asset_kind: Some(asset_kind),
|
||||
asset_kind: Some(asset_kind.to_string()),
|
||||
});
|
||||
advance_agent_runtime_project_revision_locked(root).map_err(|error| {
|
||||
format!("reconciliation-required: 本地资源已登记,但项目 revision 未能推进:{error}")
|
||||
@@ -4111,15 +4121,14 @@ pub(crate) fn import_local_project_assets_for_agent(
|
||||
/// 唯一一条"这东西是什么"的事实,并把角色立绘、怪物、道具、场景图一并钉进「UI 交互」栏
|
||||
/// (`ui → ui-design → ui-interaction`)。落盘 `category` 一旦是非 `unclassified` 值,
|
||||
/// 读时自愈永远救不回来,所以宁可写 `image`(待归类)也不能写假 `ui`。
|
||||
fn imported_platform_asset_kind(platform_kind: Option<&str>) -> String {
|
||||
fn imported_platform_asset_kind(platform_kind: Option<&str>) -> GameCreationAppAssetKind {
|
||||
match platform_kind.map(str::trim).filter(|kind| !kind.is_empty()) {
|
||||
// 平台侧词汇可能与 canonical 目录不完全一致(例如大写 `UI`),统一过 canonical 归一,
|
||||
// 保证写入侧只产出 canonical `kind`。
|
||||
Some(kind) => {
|
||||
shared_contracts::game_creation_app::canonical_game_creation_app_asset_kind(kind)
|
||||
.to_string()
|
||||
}
|
||||
None => "image".to_string(),
|
||||
// 平台是外部系统,它的 `assetKind` 词汇(含大写 `UI` 这类变体)由共享契约统一收口到
|
||||
// canonical 值;这里只把收口结果转成正式枚举,写入侧不再产出裸字符串。
|
||||
Some(kind) => GameCreationAppAssetKind::from_str_lossy(
|
||||
shared_contracts::game_creation_app::canonical_game_creation_app_asset_kind(kind),
|
||||
),
|
||||
None => GameCreationAppAssetKind::Image,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4284,7 +4293,7 @@ pub(crate) async fn import_account_editor_assets_for_agent(
|
||||
let registered = register_local_asset_entry(
|
||||
root,
|
||||
&local_path,
|
||||
&asset_kind,
|
||||
asset_kind,
|
||||
&media_type,
|
||||
"canvas",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -4402,7 +4411,7 @@ pub(crate) async fn import_ui_editor_remote_assets(
|
||||
let registered = register_local_asset_entry(
|
||||
root,
|
||||
&local_path,
|
||||
&asset_kind,
|
||||
asset_kind,
|
||||
&media_type,
|
||||
"canvas",
|
||||
GameCreationAppAssetSource {
|
||||
|
||||
@@ -1069,9 +1069,15 @@ pub(crate) fn validate_manifest_required_visual_asset(
|
||||
task_id: &str,
|
||||
) -> Result<(), String> {
|
||||
let (expected_path, expected_kind) = match task_id {
|
||||
"art-director" => ("assets/art-spec.png", "icon-spec"),
|
||||
"design-foundation" => ("assets/ui-prototype.png", "ui-prototype"),
|
||||
"art-asset-plan" => ("assets/art-spritesheet.png", "art-spritesheet"),
|
||||
"art-director" => ("assets/art-spec.png", GameCreationAppAssetKind::IconSpec),
|
||||
"design-foundation" => (
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
),
|
||||
"art-asset-plan" => (
|
||||
"assets/art-spritesheet.png",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
),
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let asset = manifest
|
||||
@@ -1141,7 +1147,10 @@ pub(crate) fn validate_manifest_required_visual_asset(
|
||||
let art_spec = manifest
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.local_path == "assets/art-spec.png" && asset.kind == "icon-spec")
|
||||
.find(|asset| {
|
||||
asset.local_path == "assets/art-spec.png"
|
||||
&& asset.kind == GameCreationAppAssetKind::IconSpec
|
||||
})
|
||||
.ok_or_else(|| "缺少当前统一视觉规范图".to_string())?;
|
||||
let art_spec_project_id = art_spec
|
||||
.source
|
||||
|
||||
@@ -19,7 +19,7 @@ fn project_with_two_assets(test_name: &str) -> (PathBuf, String) {
|
||||
let first = register_local_asset_entry(
|
||||
&root,
|
||||
"assets/hero.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"asset",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -39,7 +39,7 @@ fn project_with_two_assets(test_name: &str) -> (PathBuf, String) {
|
||||
register_local_asset_entry(
|
||||
&root,
|
||||
"assets/theme.mp3",
|
||||
"background-music",
|
||||
GameCreationAppAssetKind::BackgroundMusic,
|
||||
"audio/mpeg",
|
||||
"asset",
|
||||
GameCreationAppAssetSource {
|
||||
|
||||
@@ -96,7 +96,7 @@ fn manifest_task_and_asset_mutations_are_serialized_across_the_full_transaction(
|
||||
crate::assets::register_local_asset_entry(
|
||||
&second_root,
|
||||
"assets/concurrent.png",
|
||||
"ui-prototype",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"image/png",
|
||||
"test",
|
||||
GameCreationAppAssetSource {
|
||||
|
||||
@@ -35,7 +35,7 @@ fn register_asset_delete_fixture_asset(root: &Path, relative_path: &str, task_id
|
||||
register_local_asset_at(
|
||||
root,
|
||||
relative_path,
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"generated",
|
||||
generated_asset_source(task_id),
|
||||
|
||||
@@ -9,7 +9,7 @@ fn asset_rename_project_fixture() -> (PathBuf, String) {
|
||||
let asset = register_local_asset_at(
|
||||
&root,
|
||||
"assets/hero.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"canvas",
|
||||
asset_rename_source(),
|
||||
@@ -192,7 +192,7 @@ fn rename_local_project_asset_rejects_extension_changes() {
|
||||
let extensionless = register_local_asset_at(
|
||||
&root,
|
||||
"assets/notes",
|
||||
"asset",
|
||||
GameCreationAppAssetKind::Document,
|
||||
"text/plain",
|
||||
"canvas",
|
||||
asset_rename_source(),
|
||||
|
||||
@@ -1401,7 +1401,11 @@ async fn canvas_replacement_rejects_parent_run_that_terminates_during_external_r
|
||||
);
|
||||
init_local_game_project_at(&root, "project-canvas-repair-atomic", "月光厨房")
|
||||
.expect("project init");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
bind_canvas_visual_asset_fixture_to_current_editor(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
|
||||
@@ -3596,28 +3596,32 @@ fn spawn_mock_external_canvas_generation_failure_server() -> String {
|
||||
base_url
|
||||
}
|
||||
|
||||
fn register_canvas_visual_asset_fixture(root: &Path, local_path: &str, kind: &str) {
|
||||
fn register_canvas_visual_asset_fixture(
|
||||
root: &Path,
|
||||
local_path: &str,
|
||||
kind: GameCreationAppAssetKind,
|
||||
) {
|
||||
let absolute_path = root.join(local_path);
|
||||
fs::create_dir_all(absolute_path.parent().expect("visual asset parent"))
|
||||
.expect("create visual asset fixture directory");
|
||||
let bytes = if kind == "art-spritesheet" {
|
||||
let bytes = if kind == GameCreationAppAssetKind::IconSpritesheet {
|
||||
transparent_test_png_bytes()
|
||||
} else {
|
||||
valid_test_png_bytes()
|
||||
};
|
||||
fs::write(&absolute_path, bytes).expect("write visual asset fixture");
|
||||
let (generation_route, generation_kind, reference_resource_ids) = match kind {
|
||||
"icon-spec" => (
|
||||
GameCreationAppAssetKind::IconSpec => (
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"spec",
|
||||
Vec::new(),
|
||||
),
|
||||
"ui-prototype" => (
|
||||
GameCreationAppAssetKind::UiDesign => (
|
||||
"/api/external/v1/editor/images/generations",
|
||||
"ui-design",
|
||||
vec!["resource-icon-spec".to_string()],
|
||||
),
|
||||
"art-spritesheet" => (
|
||||
GameCreationAppAssetKind::IconSpritesheet => (
|
||||
"/api/external/v1/editor/icon-spritesheets/generations",
|
||||
"icon-spritesheet",
|
||||
vec!["resource-icon-spec".to_string()],
|
||||
|
||||
@@ -369,9 +369,21 @@ fn canonical_visual_completion_requires_persisted_route_kind_and_current_spec_re
|
||||
let root = unique_project_path();
|
||||
init_local_game_project_at(&root, "visual-provenance", "视觉来源门禁")
|
||||
.expect("init visual provenance project");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/ui-prototype.png", "ui-prototype");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spritesheet.png", "art-spritesheet");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
);
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spritesheet.png",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
);
|
||||
let mut manifest = read_manifest_for_project(&root).expect("read visual manifest");
|
||||
for task_id in ["art-director", "design-foundation", "art-asset-plan"] {
|
||||
validate_manifest_required_visual_asset(&root, &manifest, task_id)
|
||||
@@ -704,7 +716,11 @@ async fn background_agent_runtime_can_generate_platform_art_asset() {
|
||||
&canvas_base_url,
|
||||
);
|
||||
init_local_game_project_at(&root, "project-1", "月光厨房").expect("project init");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
bind_canvas_visual_asset_fixture_to_current_editor(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
@@ -854,7 +870,7 @@ async fn background_agent_runtime_can_generate_platform_art_asset() {
|
||||
asset["source"]["referenceResourceIds"],
|
||||
serde_json::json!(["resource-icon-spec"])
|
||||
);
|
||||
assert_eq!(asset["kind"], "art-spritesheet");
|
||||
assert_eq!(asset["kind"], "icon-spritesheet");
|
||||
assert_eq!(asset["localPath"], "assets/art-spritesheet.png");
|
||||
assert_eq!(
|
||||
fs::read(root.join(asset["localPath"].as_str().unwrap())).unwrap(),
|
||||
@@ -1063,7 +1079,11 @@ async fn canonical_art_spec_and_ui_requests_use_the_shared_reference_chain() {
|
||||
assert!(spec_request.contains(r#""assetKind":"icon-spec""#));
|
||||
assert!(spec_request.contains(r#""referenceImageSrcs":[]"#));
|
||||
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
let ui_config_dir = unique_project_path();
|
||||
let ui_request = capture_generation_request(
|
||||
&root,
|
||||
@@ -1105,7 +1125,11 @@ async fn platform_art_external_request_does_not_hold_project_lock_or_overwrite_m
|
||||
);
|
||||
init_local_game_project_at(&root, "project-canvas-request-lock", "月光厨房")
|
||||
.expect("project init");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
bind_canvas_visual_asset_fixture_to_current_editor(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
@@ -1932,7 +1956,7 @@ fn register_local_asset_records_existing_asset_with_canvas_source() {
|
||||
let result = register_local_asset_at(
|
||||
&root,
|
||||
"assets/hero.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"canvas",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -1975,7 +1999,7 @@ fn register_local_asset_records_existing_asset_with_canvas_source() {
|
||||
let updated = register_local_asset_at(
|
||||
&root,
|
||||
"assets/hero.png",
|
||||
"ui",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"image/png",
|
||||
"generated",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -1997,7 +2021,7 @@ fn register_local_asset_records_existing_asset_with_canvas_source() {
|
||||
serde_json::from_str(&fs::read_to_string(root.join(".agent/manifest.json")).unwrap())
|
||||
.expect("manifest json");
|
||||
assert_eq!(manifest["assets"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(manifest["assets"][0]["kind"], "ui");
|
||||
assert_eq!(manifest["assets"][0]["kind"], "ui-design");
|
||||
// kind 变了必须重派生 category:旧分类 `character` 是非 unclassified 值,会被读侧
|
||||
// 无条件信任、自愈也不会触发,资产就永远停在「角色与对象」。
|
||||
assert_eq!(manifest["assets"][0]["category"], "ui-interaction");
|
||||
@@ -2031,7 +2055,7 @@ fn register_local_asset_keeps_explicit_category_when_kind_is_unchanged() {
|
||||
let registered = register_local_asset_at(
|
||||
&root,
|
||||
"ui/UI 设计 1.json",
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
GameCreationAppAssetKind::UiDesignDoc,
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
"ui-workflow",
|
||||
source(),
|
||||
@@ -2053,7 +2077,7 @@ fn register_local_asset_keeps_explicit_category_when_kind_is_unchanged() {
|
||||
register_local_asset_at(
|
||||
&root,
|
||||
"ui/UI 设计 1.json",
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
GameCreationAppAssetKind::UiDesignDoc,
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
"ui-workflow",
|
||||
source(),
|
||||
@@ -2102,7 +2126,7 @@ fn register_local_asset_derives_category_from_real_write_side_kinds() {
|
||||
register_local_asset_at(
|
||||
&root,
|
||||
"ui/UI 设计 1.json",
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
GameCreationAppAssetKind::UiDesignDoc,
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
"ui-workflow",
|
||||
source(),
|
||||
@@ -2111,7 +2135,7 @@ fn register_local_asset_derives_category_from_real_write_side_kinds() {
|
||||
register_local_asset_at(
|
||||
&root,
|
||||
"assets/ui-font.ttf",
|
||||
"font",
|
||||
GameCreationAppAssetKind::Font,
|
||||
"font/ttf",
|
||||
"font",
|
||||
source(),
|
||||
@@ -2136,12 +2160,12 @@ fn register_local_asset_derives_category_from_real_write_side_kinds() {
|
||||
assert_eq!(
|
||||
categories,
|
||||
vec![
|
||||
("font".to_string(), "document".to_string()),
|
||||
(
|
||||
shared_contracts::game_creation_app::GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND
|
||||
.to_string(),
|
||||
"ui-interaction".to_string(),
|
||||
),
|
||||
("font".to_string(), "document".to_string()),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -2167,7 +2191,7 @@ fn register_local_asset_rejects_missing_or_unsafe_path() {
|
||||
assert!(register_local_asset_at(
|
||||
&root,
|
||||
"../outside.png",
|
||||
"asset",
|
||||
GameCreationAppAssetKind::Image,
|
||||
"image/png",
|
||||
"generated",
|
||||
source.clone()
|
||||
@@ -2176,7 +2200,7 @@ fn register_local_asset_rejects_missing_or_unsafe_path() {
|
||||
assert!(register_local_asset_at(
|
||||
&root,
|
||||
"assets/missing.png",
|
||||
"asset",
|
||||
GameCreationAppAssetKind::Image,
|
||||
"image/png",
|
||||
"generated",
|
||||
source
|
||||
@@ -2525,7 +2549,8 @@ async fn generate_platform_art_asset_downloads_and_registers_external_image() {
|
||||
serde_json::from_str(&fs::read_to_string(root.join(".agent/manifest.json")).unwrap())
|
||||
.expect("manifest json");
|
||||
let asset = &manifest["assets"][0];
|
||||
assert_eq!(asset["kind"], "game-art");
|
||||
// 平台请求词汇 `game-art` 在写入边界收口成正式成员 `image`。
|
||||
assert_eq!(asset["kind"], "image");
|
||||
assert_eq!(asset["mediaType"], "image/png");
|
||||
assert_eq!(asset["source"]["kind"], "canvas");
|
||||
assert_eq!(asset["source"]["canvasProjectId"], "canvas-project-1");
|
||||
@@ -2716,7 +2741,11 @@ async fn generate_local_project_asset_command_generates_art_spritesheet_from_the
|
||||
"{missing_reference}"
|
||||
);
|
||||
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
bind_canvas_visual_asset_fixture_to_current_editor(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
@@ -2745,7 +2774,8 @@ async fn generate_local_project_asset_command_generates_art_spritesheet_from_the
|
||||
.iter()
|
||||
.find(|entry| entry["id"].as_str() == Some(asset.id.as_str()))
|
||||
.expect("registered toolbar spritesheet asset");
|
||||
assert_eq!(entry["kind"], "art-spritesheet");
|
||||
// 平台请求词汇 `art-spritesheet` 登记时收口成正式成员 `icon-spritesheet`。
|
||||
assert_eq!(entry["kind"], "icon-spritesheet");
|
||||
assert_eq!(
|
||||
entry["source"]["generationRoute"],
|
||||
"/api/external/v1/editor/icon-spritesheets/generations"
|
||||
|
||||
@@ -6105,7 +6105,7 @@ fn local_project_image_preview_obeys_auto_file_read_policy() {
|
||||
register_local_asset_at(
|
||||
&root,
|
||||
"assets/preview.png",
|
||||
"ui-prototype",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"image/png",
|
||||
"canvas",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -6219,7 +6219,7 @@ fn local_project_resource_previews_require_registered_safe_resources() {
|
||||
register_local_asset_at(
|
||||
&root,
|
||||
"game/design.md",
|
||||
"document",
|
||||
GameCreationAppAssetKind::Document,
|
||||
"text/markdown",
|
||||
"generated",
|
||||
source(),
|
||||
@@ -6228,7 +6228,7 @@ fn local_project_resource_previews_require_registered_safe_resources() {
|
||||
register_local_asset_at(
|
||||
&root,
|
||||
"assets/icon.svg",
|
||||
"icon",
|
||||
GameCreationAppAssetKind::Icon,
|
||||
"image/svg+xml",
|
||||
"generated",
|
||||
source(),
|
||||
@@ -6237,7 +6237,7 @@ fn local_project_resource_previews_require_registered_safe_resources() {
|
||||
register_local_asset_at(
|
||||
&root,
|
||||
"assets/bgm.mp3",
|
||||
"bgm",
|
||||
GameCreationAppAssetKind::BackgroundMusic,
|
||||
"audio/mpeg",
|
||||
"generated",
|
||||
source(),
|
||||
|
||||
@@ -752,8 +752,16 @@ async fn background_agent_runtime_can_schedule_ready_manifest_tasks() {
|
||||
async fn background_agent_runtime_can_schedule_ready_tasks_from_tool() {
|
||||
let root = unique_project_path();
|
||||
init_local_game_project_at(&root, "project-1", "月光厨房").expect("project init");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/ui-prototype.png", "ui-prototype");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
);
|
||||
update_manifest_task_status_at(&root, "art-director", GameCreationAppTaskStatus::Completed)
|
||||
.expect("complete art spec dependency fixture");
|
||||
write_project_permission_policy_at(
|
||||
@@ -1805,7 +1813,11 @@ async fn background_agent_runtime_image_inspect_sends_two_images_without_persist
|
||||
async fn design_ui_image_inspect_fails_scene_and_persists_canonical_checks() {
|
||||
let root = unique_project_path();
|
||||
init_local_game_project_at(&root, "project-1", "场景图拒绝测试").expect("project init");
|
||||
register_canvas_visual_asset_fixture(&root, AGENT_RUNTIME_UI_PROTOTYPE_PATH, "ui-prototype");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
AGENT_RUNTIME_UI_PROTOTYPE_PATH,
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
);
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let base_url = spawn_mock_llm_server_responses_with_capture(
|
||||
vec![ui_prototype_assessment_fixture(false)],
|
||||
@@ -2157,9 +2169,21 @@ fn seed_refresh_preserves_completed_visual_tasks_when_registered_file_is_missing
|
||||
);
|
||||
let root = unique_project_path();
|
||||
init_local_game_project_at(&root, "project-1", "旧视觉任务升级测试").expect("project init");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/ui-prototype.png", "ui-prototype");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spritesheet.png", "art-spritesheet");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
);
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spritesheet.png",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
);
|
||||
|
||||
let mut manifest = read_manifest_for_project(&root).expect("manifest with visual assets");
|
||||
for task_id in ["art-director", "design-foundation", "art-asset-plan"] {
|
||||
|
||||
+20
-4
@@ -468,9 +468,21 @@ fn autonomous_visual_gate_degrades_to_text_without_key_and_requires_images_with_
|
||||
);
|
||||
}
|
||||
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spec.png", "icon-spec");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/ui-prototype.png", "ui-prototype");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spritesheet.png", "art-spritesheet");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spec.png",
|
||||
GameCreationAppAssetKind::IconSpec,
|
||||
);
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
);
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spritesheet.png",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
);
|
||||
for task_id in ["art-director", "design-foundation", "art-asset-plan"] {
|
||||
update_manifest_task_status_at(&root, task_id, GameCreationAppTaskStatus::Completed)
|
||||
.expect("complete visual owner task with registered image");
|
||||
@@ -3654,7 +3666,11 @@ async fn autonomous_static_art_asset_delivery_uses_runtime_owner_validation_with
|
||||
br#"{"assets":[{"path":"assets/art-spritesheet.png","kind":"art-spritesheet"}]}"#,
|
||||
)
|
||||
.expect("write static art manifest after the parent completion baseline");
|
||||
register_canvas_visual_asset_fixture(&root, "assets/art-spritesheet.png", "art-spritesheet");
|
||||
register_canvas_visual_asset_fixture(
|
||||
&root,
|
||||
"assets/art-spritesheet.png",
|
||||
GameCreationAppAssetKind::IconSpritesheet,
|
||||
);
|
||||
prepare_agent_runtime_project_mutation_locked(
|
||||
&root,
|
||||
"art-asset-plan",
|
||||
|
||||
@@ -98,11 +98,12 @@ pub(super) use crate::{
|
||||
AgentRuntimePendingToolAction, AgentRuntimePlanUpdate, AgentRuntimePlanUpdateStep,
|
||||
AgentRuntimeProviderActionBatchPreparation, AgentRuntimeState, AgentRuntimeTaskLink,
|
||||
AgentRuntimeTaskRecord, AgentRuntimeToolAction, AgentRuntimeToolObservation,
|
||||
AgentRuntimeToolPlan, AgentRuntimeToolPolicyBlock, GameCreationAppTaskStatus,
|
||||
LocalConversationMessage, ProjectAgentPermissionPolicy, ProjectPermissionPolicy,
|
||||
SupervisorCollaborationPolicy, AGENT_RUNTIME_ACTION_EXECUTION_MODE_AUTO,
|
||||
AGENT_RUNTIME_ACTION_EXECUTION_MODE_CONFIRMATION, AGENT_RUNTIME_ACTION_FINGERPRINT_VERSION,
|
||||
AGENT_RUNTIME_ACTION_RECEIPT_RECORD_TYPE, AGENT_RUNTIME_AUTONOMOUS_LIVENESS_OBSERVATION_LIMIT,
|
||||
AgentRuntimeToolPlan, AgentRuntimeToolPolicyBlock, GameCreationAppAssetKind,
|
||||
GameCreationAppTaskStatus, LocalConversationMessage, ProjectAgentPermissionPolicy,
|
||||
ProjectPermissionPolicy, SupervisorCollaborationPolicy,
|
||||
AGENT_RUNTIME_ACTION_EXECUTION_MODE_AUTO, AGENT_RUNTIME_ACTION_EXECUTION_MODE_CONFIRMATION,
|
||||
AGENT_RUNTIME_ACTION_FINGERPRINT_VERSION, AGENT_RUNTIME_ACTION_RECEIPT_RECORD_TYPE,
|
||||
AGENT_RUNTIME_AUTONOMOUS_LIVENESS_OBSERVATION_LIMIT,
|
||||
AGENT_RUNTIME_AUTONOMOUS_PRE_MUTATION_LOOP_LIMIT,
|
||||
AGENT_RUNTIME_AUTONOMOUS_SCAFFOLD_MAX_OUTPUT_TOKENS,
|
||||
AGENT_RUNTIME_AUTONOMOUS_TRUNCATED_SCAFFOLD_MAX_OUTPUT_TOKENS,
|
||||
|
||||
@@ -42,7 +42,7 @@ fn replacement_generated_asset_source(task_id: &str) -> GameCreationAppAssetSour
|
||||
fn register_replacement_fixture_asset(
|
||||
root: &Path,
|
||||
relative_path: &str,
|
||||
kind: &str,
|
||||
kind: GameCreationAppAssetKind,
|
||||
media_type: &str,
|
||||
task_id: &str,
|
||||
) -> String {
|
||||
@@ -146,14 +146,14 @@ fn replacement_rewrites_the_version_binding_without_adding_a_version() {
|
||||
let source_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/hero-a.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-a",
|
||||
);
|
||||
let untouched_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/scene.png",
|
||||
"scene",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"image/png",
|
||||
"art-scene",
|
||||
);
|
||||
@@ -162,7 +162,7 @@ fn replacement_rewrites_the_version_binding_without_adding_a_version() {
|
||||
let replacement_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/hero-b.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-b",
|
||||
);
|
||||
@@ -280,14 +280,14 @@ fn replacement_drops_source_binding_when_replacement_is_already_bound() {
|
||||
let source_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/legacy.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-legacy",
|
||||
);
|
||||
let already_bound_replacement = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/final.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-final",
|
||||
);
|
||||
@@ -352,21 +352,21 @@ fn replacement_drops_source_binding_when_replacement_is_already_bound() {
|
||||
fn replacement_blocks_category_and_subtype_mismatch_but_only_hints_size_spec() {
|
||||
struct BlockingCase {
|
||||
name: &'static str,
|
||||
source_kind: &'static str,
|
||||
target_kind: &'static str,
|
||||
source_kind: GameCreationAppAssetKind,
|
||||
target_kind: GameCreationAppAssetKind,
|
||||
expected_reason: &'static str,
|
||||
}
|
||||
let blocking_cases = [
|
||||
BlockingCase {
|
||||
name: "分类不同",
|
||||
source_kind: "character",
|
||||
target_kind: "scene",
|
||||
source_kind: GameCreationAppAssetKind::Character,
|
||||
target_kind: GameCreationAppAssetKind::Scene,
|
||||
expected_reason: "分类不同",
|
||||
},
|
||||
BlockingCase {
|
||||
name: "类型不同",
|
||||
source_kind: "character",
|
||||
target_kind: "character-animation",
|
||||
source_kind: GameCreationAppAssetKind::Character,
|
||||
target_kind: GameCreationAppAssetKind::CharacterAnimation,
|
||||
expected_reason: "类型不同",
|
||||
},
|
||||
];
|
||||
@@ -420,7 +420,7 @@ fn replacement_blocks_category_and_subtype_mismatch_but_only_hints_size_spec() {
|
||||
let source_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/hero.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-hero",
|
||||
);
|
||||
@@ -428,7 +428,7 @@ fn replacement_blocks_category_and_subtype_mismatch_but_only_hints_size_spec() {
|
||||
let webp_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/hero.webp",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/webp",
|
||||
"art-hero-webp",
|
||||
);
|
||||
@@ -486,14 +486,14 @@ fn replacement_hints_known_frame_size_and_duration_differences() {
|
||||
let source_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/seq-source.png",
|
||||
"character-animation",
|
||||
GameCreationAppAssetKind::CharacterAnimation,
|
||||
"image/png",
|
||||
"anim-source",
|
||||
);
|
||||
let target_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/seq-target.png",
|
||||
"character-animation",
|
||||
GameCreationAppAssetKind::CharacterAnimation,
|
||||
"image/png",
|
||||
"anim-target",
|
||||
);
|
||||
@@ -551,14 +551,14 @@ fn replacement_heals_persisted_unclassified_category_like_the_shared_contract()
|
||||
let source_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/ui-a.png",
|
||||
"ui-design",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"image/png",
|
||||
"ui-a",
|
||||
);
|
||||
let healed_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/ui-b.png",
|
||||
"ui-design",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"image/png",
|
||||
"ui-b",
|
||||
);
|
||||
@@ -611,7 +611,7 @@ fn replacement_rejects_unresolvable_source_and_target() {
|
||||
let bound_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/bound.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-bound",
|
||||
);
|
||||
@@ -620,7 +620,7 @@ fn replacement_rejects_unresolvable_source_and_target() {
|
||||
let unbound_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/late.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-late",
|
||||
);
|
||||
@@ -672,14 +672,14 @@ fn replacement_enforces_revision_and_identity_cas() {
|
||||
let source_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/cas-source.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-cas-source",
|
||||
);
|
||||
let target_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/cas-target.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-cas-target",
|
||||
);
|
||||
@@ -722,35 +722,35 @@ fn replacement_candidates_report_authoritative_compatibility() {
|
||||
let source_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/pick-source.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-pick-source",
|
||||
);
|
||||
let compatible_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/pick-compatible.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-pick-compatible",
|
||||
);
|
||||
let category_mismatch = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/pick-scene.png",
|
||||
"scene",
|
||||
GameCreationAppAssetKind::Scene,
|
||||
"image/png",
|
||||
"art-pick-scene",
|
||||
);
|
||||
let subtype_mismatch = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/pick-animation.png",
|
||||
"character-animation",
|
||||
GameCreationAppAssetKind::CharacterAnimation,
|
||||
"image/png",
|
||||
"art-pick-animation",
|
||||
);
|
||||
let format_mismatch = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/pick-webp.webp",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/webp",
|
||||
"art-pick-webp",
|
||||
);
|
||||
@@ -814,7 +814,7 @@ fn replacement_candidates_report_authoritative_compatibility() {
|
||||
let late_asset = register_replacement_fixture_asset(
|
||||
&root,
|
||||
"assets/pick-late.png",
|
||||
"character",
|
||||
GameCreationAppAssetKind::Character,
|
||||
"image/png",
|
||||
"art-pick-late",
|
||||
);
|
||||
|
||||
@@ -819,7 +819,7 @@ mod tests {
|
||||
let asset = register_local_asset_at(
|
||||
directory.path(),
|
||||
relative_path,
|
||||
UI_DESIGN_DOC_ASSET_KIND,
|
||||
GameCreationAppAssetKind::UiDesignDoc,
|
||||
UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
"test",
|
||||
GameCreationAppAssetSource {
|
||||
|
||||
@@ -122,7 +122,7 @@ pub(crate) fn ensure_ui_design_resource_for_prototype(
|
||||
let asset = match register_local_asset_at(
|
||||
root,
|
||||
&relative_path,
|
||||
UI_DESIGN_DOC_ASSET_KIND,
|
||||
GameCreationAppAssetKind::UiDesignDoc,
|
||||
UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
"generated",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -248,7 +248,7 @@ mod tests {
|
||||
register_local_asset_entry(
|
||||
directory.path(),
|
||||
"assets/ui-prototype.png",
|
||||
"ui-design",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"image/png",
|
||||
"canvas",
|
||||
GameCreationAppAssetSource {
|
||||
|
||||
@@ -750,7 +750,7 @@ fn ensure_page_ui_resource(
|
||||
let registered = register_local_asset_at(
|
||||
root,
|
||||
&relative_path,
|
||||
UI_DESIGN_DOC_ASSET_KIND,
|
||||
GameCreationAppAssetKind::UiDesignDoc,
|
||||
UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
"ui-workflow",
|
||||
GameCreationAppAssetSource {
|
||||
@@ -1464,7 +1464,7 @@ mod tests {
|
||||
fn fixture_asset(
|
||||
root: &Path,
|
||||
relative_path: &str,
|
||||
kind: &str,
|
||||
kind: GameCreationAppAssetKind,
|
||||
resource_id: &str,
|
||||
) -> GameCreationAppAssetManifestEntry {
|
||||
register_local_asset_at(
|
||||
@@ -1607,8 +1607,18 @@ mod tests {
|
||||
let design_path = root.join("assets/home.png");
|
||||
fixture_png(&source_path);
|
||||
fixture_png(&design_path);
|
||||
let source = fixture_asset(root, "assets/ui-prototype.png", "ui-design", "source-ui");
|
||||
let design = fixture_asset(root, "assets/home.png", "ui-design", "design-home");
|
||||
let source = fixture_asset(
|
||||
root,
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"source-ui",
|
||||
);
|
||||
let design = fixture_asset(
|
||||
root,
|
||||
"assets/home.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"design-home",
|
||||
);
|
||||
let page = |operation| UiWorkflowRunInput {
|
||||
operation,
|
||||
source_asset_id: source.id.clone(),
|
||||
@@ -1658,9 +1668,24 @@ mod tests {
|
||||
fixture_png(&root.join("assets/ui-prototype.png"));
|
||||
fixture_png(&root.join("assets/home.png"));
|
||||
fixture_png(&root.join("assets/start-button.png"));
|
||||
let source = fixture_asset(root, "assets/ui-prototype.png", "ui-design", "source-ui");
|
||||
let design = fixture_asset(root, "assets/home.png", "ui-design", "design-home");
|
||||
let sprite = fixture_asset(root, "assets/start-button.png", "ui-icon", "start-button");
|
||||
let source = fixture_asset(
|
||||
root,
|
||||
"assets/ui-prototype.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"source-ui",
|
||||
);
|
||||
let design = fixture_asset(
|
||||
root,
|
||||
"assets/home.png",
|
||||
GameCreationAppAssetKind::UiDesign,
|
||||
"design-home",
|
||||
);
|
||||
let sprite = fixture_asset(
|
||||
root,
|
||||
"assets/start-button.png",
|
||||
GameCreationAppAssetKind::Icon,
|
||||
"start-button",
|
||||
);
|
||||
let font_path = root.join("assets/ui-font.ttf");
|
||||
fs::copy(
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../public/fusion-pixel.ttf"),
|
||||
@@ -1670,7 +1695,7 @@ mod tests {
|
||||
let font = register_local_asset_at(
|
||||
root,
|
||||
"assets/ui-font.ttf",
|
||||
"font",
|
||||
GameCreationAppAssetKind::Font,
|
||||
"font/ttf",
|
||||
"workflow-test",
|
||||
GameCreationAppAssetSource {
|
||||
|
||||
Reference in New Issue
Block a user