Add generationStatus and match3d/runtime fixes
Introduce persistent generationStatus to work summaries (puzzle & match3d) and propagate generation recovery rules across docs and frontend/backends so "generating" is restored from server-side work summary rather than ephemeral front-end notices. Update API server image/asset handling (improve match3d material sheet green/alpha decontamination and promote generatedItemAssets background fields) and add runtime improvements: alpha-based hotspot hit-testing, tray insertion/three-match animation behavior, and session re-read on client-side VectorEngine timeouts/lock-screen interruptions. Many docs, tests and related frontend modules updated/added to reflect these contract and behavior changes.
This commit is contained in:
@@ -32,6 +32,10 @@ pub(super) fn map_match3d_agent_session_response_with_assets(
|
||||
) -> Match3DAgentSessionSnapshotResponse {
|
||||
let mut response = map_match3d_agent_session_response(session);
|
||||
if let Some(draft) = response.draft.as_mut() {
|
||||
if generated_item_assets.is_empty() {
|
||||
return response;
|
||||
}
|
||||
|
||||
draft.generated_item_assets = generated_item_assets
|
||||
.iter()
|
||||
.cloned()
|
||||
@@ -129,7 +133,15 @@ pub(super) fn map_match3d_config_response(
|
||||
pub(super) fn map_match3d_draft_response(
|
||||
draft: Match3DResultDraftRecord,
|
||||
) -> Match3DResultDraftResponse {
|
||||
Match3DResultDraftResponse {
|
||||
// 中文注释:session draft 自身也可能携带生成素材快照,不能只依赖 work detail 回读补齐 UI 背景和容器图。
|
||||
let generated_item_assets = parse_match3d_generated_item_assets(
|
||||
draft.generated_item_assets_json.as_deref(),
|
||||
)
|
||||
.into_iter()
|
||||
.map(Match3DGeneratedItemAsset::from)
|
||||
.collect::<Vec<_>>();
|
||||
let background_asset = find_match3d_generated_background_asset(&generated_item_assets);
|
||||
let mut response = Match3DResultDraftResponse {
|
||||
profile_id: draft.profile_id,
|
||||
game_name: draft.game_name,
|
||||
theme_text: draft.theme_text,
|
||||
@@ -147,8 +159,24 @@ pub(super) fn map_match3d_draft_response(
|
||||
background_image_src: None,
|
||||
background_image_object_key: None,
|
||||
generated_background_asset: None,
|
||||
generated_item_assets: Vec::new(),
|
||||
generated_item_assets: generated_item_assets
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(map_match3d_generated_item_asset_for_agent)
|
||||
.collect(),
|
||||
};
|
||||
|
||||
if response
|
||||
.cover_image_src
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.unwrap_or_default()
|
||||
.is_empty()
|
||||
{
|
||||
response.cover_image_src = resolve_match3d_default_cover_image_src(&generated_item_assets);
|
||||
}
|
||||
apply_match3d_background_asset_to_agent_draft(&mut response, background_asset);
|
||||
response
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_generated_item_asset_for_agent(
|
||||
@@ -365,6 +393,45 @@ pub(super) fn build_match3d_work_profile_record_with_assets(
|
||||
item
|
||||
}
|
||||
|
||||
fn match3d_text_present(value: Option<&String>) -> bool {
|
||||
value.is_some_and(|value| !value.trim().is_empty())
|
||||
}
|
||||
|
||||
fn match3d_item_asset_has_image(asset: &Match3DGeneratedItemAssetJson) -> bool {
|
||||
match3d_text_present(asset.image_src.as_ref())
|
||||
|| match3d_text_present(asset.image_object_key.as_ref())
|
||||
|| asset.image_views.iter().any(|view| {
|
||||
match3d_text_present(view.image_src.as_ref())
|
||||
|| match3d_text_present(view.image_object_key.as_ref())
|
||||
})
|
||||
}
|
||||
|
||||
fn match3d_background_asset_has_image(asset: &Match3DGeneratedBackgroundAsset) -> bool {
|
||||
match3d_text_present(asset.image_src.as_ref())
|
||||
|| match3d_text_present(asset.image_object_key.as_ref())
|
||||
|| match3d_text_present(asset.container_image_src.as_ref())
|
||||
|| match3d_text_present(asset.container_image_object_key.as_ref())
|
||||
}
|
||||
|
||||
fn resolve_match3d_work_generation_status(
|
||||
item: &Match3DWorkProfileRecord,
|
||||
assets: &[Match3DGeneratedItemAssetJson],
|
||||
background_asset: Option<&Match3DGeneratedBackgroundAsset>,
|
||||
) -> Option<String> {
|
||||
if item.publication_status.eq_ignore_ascii_case("published") {
|
||||
return Some("ready".to_string());
|
||||
}
|
||||
|
||||
if assets.is_empty()
|
||||
|| !assets.iter().any(match3d_item_asset_has_image)
|
||||
|| !background_asset.is_some_and(match3d_background_asset_has_image)
|
||||
{
|
||||
return Some("generating".to_string());
|
||||
}
|
||||
|
||||
Some("ready".to_string())
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_message_response(
|
||||
message: Match3DAgentMessageRecord,
|
||||
) -> Match3DAgentMessageResponse {
|
||||
@@ -383,6 +450,11 @@ pub(super) fn map_match3d_work_summary_response(
|
||||
let generated_item_asset_json =
|
||||
parse_match3d_generated_item_assets(item.generated_item_assets_json.as_deref());
|
||||
let background_asset = find_match3d_generated_background_asset_json(&generated_item_asset_json);
|
||||
let generation_status = resolve_match3d_work_generation_status(
|
||||
&item,
|
||||
&generated_item_asset_json,
|
||||
background_asset.as_ref(),
|
||||
);
|
||||
let generated_background_asset = background_asset
|
||||
.clone()
|
||||
.map(map_match3d_background_asset_for_work);
|
||||
@@ -408,6 +480,7 @@ pub(super) fn map_match3d_work_summary_response(
|
||||
updated_at: item.updated_at,
|
||||
published_at: item.published_at,
|
||||
publish_ready: item.publish_ready,
|
||||
generation_status,
|
||||
background_prompt: background_asset.as_ref().map(|asset| asset.prompt.clone()),
|
||||
background_image_src: background_asset
|
||||
.as_ref()
|
||||
|
||||
Reference in New Issue
Block a user