Increase VectorEngine timeouts and add image UI

Add VectorEngine image generation config and raise request timeouts (env + scripts) from 180000 to 1000000ms. Introduce a reusable CreativeImageInputPanel component with tests and wire up mobile keyboard-focus helpers; update generation views and related tests (CustomWorldGenerationView, BarkBattle editor, Match3D, Puzzle flows). Improve API error handling / VectorEngine request guidance (packages/shared http.ts and docs), and apply multiple backend/frontend fixes for puzzle/match3d/prompt handling. Also include extensive docs and decision-log updates describing UI/UX decisions and verification steps.
This commit is contained in:
2026-05-15 02:40:59 +08:00
parent 4642855fd0
commit 74fd9a33ac
87 changed files with 5508 additions and 1261 deletions

View File

@@ -544,6 +544,16 @@ fn update_match3d_work_tx(
input: Match3DWorkUpdateInput,
) -> Result<Match3DWorkSnapshot, String> {
let current = find_owned_work(ctx, &input.profile_id, &input.owner_user_id)?;
let next = build_updated_match3d_work_row(&current, &input)?;
let snapshot = build_work_snapshot(&next)?;
replace_work(ctx, &current, next);
Ok(snapshot)
}
fn build_updated_match3d_work_row(
current: &Match3DWorkProfileRow,
input: &Match3DWorkUpdateInput,
) -> Result<Match3DWorkProfileRow, String> {
let tags = parse_tags(&input.tags_json)?;
let config = Match3DCreatorConfigSnapshot {
theme_text: clean_string(&input.theme_text, "经典消除"),
@@ -563,7 +573,7 @@ fn update_match3d_work_tx(
author_display_name: current.author_display_name.clone(),
game_name: clean_string(&input.game_name, "未命名抓大鹅"),
theme_text: config.theme_text.clone(),
summary_text: clean_string(&input.summary_text, "经典消除玩法"),
summary_text: input.summary_text.trim().to_string(),
tags_json: to_json_string(&tags),
cover_image_src: input.cover_image_src.trim().to_string(),
cover_asset_id: input.cover_asset_id.trim().to_string(),
@@ -576,9 +586,7 @@ fn update_match3d_work_tx(
published_at: current.published_at,
generated_item_assets_json: current.generated_item_assets_json.clone(),
};
let snapshot = build_work_snapshot(&next)?;
replace_work(ctx, &current, next);
Ok(snapshot)
Ok(next)
}
fn publish_match3d_work_tx(
@@ -1881,6 +1889,65 @@ mod tests {
);
}
#[test]
fn match3d_work_update_preserves_assets_and_allows_empty_summary() {
let existing = Match3DWorkProfileRow {
profile_id: "profile-1".to_string(),
owner_user_id: "user-1".to_string(),
source_session_id: "session-1".to_string(),
author_display_name: "作者".to_string(),
game_name: "水果抓大鹅".to_string(),
theme_text: "水果".to_string(),
summary_text: "保留描述".to_string(),
tags_json: "[\"水果\"]".to_string(),
cover_image_src: "/old-cover.png".to_string(),
cover_asset_id: "cover-asset-1".to_string(),
clear_count: 12,
difficulty: 4,
config_json: to_json_string(&Match3DCreatorConfigSnapshot {
theme_text: "水果".to_string(),
reference_image_src: None,
clear_count: 12,
difficulty: 4,
asset_style_id: None,
asset_style_label: None,
asset_style_prompt: None,
generate_click_sound: false,
}),
publication_status: MATCH3D_PUBLICATION_DRAFT.to_string(),
play_count: 2,
updated_at: Timestamp::from_micros_since_unix_epoch(1),
published_at: None,
generated_item_assets_json: Some(
r#"[{"itemId":"match3d-item-1","itemName":"草莓","imageSrc":"/generated-match3d-assets/session/profile/items/item/image.png","status":"image_ready"}]"#
.to_string(),
),
};
let input = Match3DWorkUpdateInput {
profile_id: existing.profile_id.clone(),
owner_user_id: existing.owner_user_id.clone(),
game_name: existing.game_name.clone(),
theme_text: existing.theme_text.clone(),
summary_text: " ".to_string(),
tags_json: existing.tags_json.clone(),
cover_image_src: "/new-cover.png".to_string(),
cover_asset_id: existing.cover_asset_id.clone(),
clear_count: existing.clear_count,
difficulty: existing.difficulty,
updated_at_micros: 2,
};
let next = build_updated_match3d_work_row(&existing, &input).unwrap();
assert_eq!(next.summary_text, "");
assert_eq!(next.cover_image_src, "/new-cover.png");
assert_eq!(next.clear_count, 12);
assert_eq!(next.difficulty, 4);
assert_eq!(
next.generated_item_assets_json.as_deref(),
existing.generated_item_assets_json.as_deref()
);
}
#[test]
fn match3d_publish_ready_requires_five_image_views_per_item() {
let base_work = Match3DWorkProfileRow {