Image editor: hide raw Prompt, use Resolution

Remove backend-assembled raw Prompt and copy action from image info; render a lightweight generationInputs snapshot (user panel inputs + reference thumbnails) stored on canvas layers and shown in the image info dialog. Unify canvas display and info to use originalWidth/originalHeight (Resolution) instead of saved Size and hydrate legacy layout width/height only as fallback. Add model/aspectRatio/imageSize options for character/icon generation (frontend state, tests, and client payloads). Increase Axum JSON body limit for character animation endpoint to 12MB for compatibility and prefer submitting persisted objectKey over large Data URLs. Update tests, docs, and related server/frontend code to reflect these behaviors and validations.
This commit is contained in:
2026-06-16 17:06:21 +08:00
parent 7eeff10c67
commit 3a3cc89280
14 changed files with 1041 additions and 135 deletions

View File

@@ -1469,6 +1469,53 @@ mod tests {
assert!(!body_text.contains("length limit exceeded"));
}
#[tokio::test]
async fn editor_character_animation_accepts_character_image_body_above_default_limit() {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
let source_image_src = format!("data:image/png;base64,{}", "A".repeat(3 * 1024 * 1024));
let request_body = serde_json::json!({
"sourceLayerId": "layer-character-large",
"sourceImageSrc": source_image_src,
"sourceWidth": 1024,
"sourceHeight": 1024,
"promptText": "待机呼吸循环。",
"resolution": "480p",
"ratio": "same",
"frameCount": 32,
"durationSeconds": 4,
"priceMudPoints": 40,
"model": "seedance2.0"
})
.to_string();
assert!(request_body.len() > 2 * 1024 * 1024);
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/api/editor/character-animations/generations")
.header("content-type", "application/json")
.body(Body::from(request_body))
.expect("request should build"),
)
.await
.expect("request should succeed");
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
let body = response
.into_body()
.collect()
.await
.expect("response body should collect")
.to_bytes();
let body_text = String::from_utf8_lossy(&body);
assert!(
body_text.contains("ARK_CHARACTER_VIDEO_BASE_URL"),
"handler should parse the oversized character source image before checking Ark config: {body_text}"
);
assert!(!body_text.contains("length limit exceeded"));
}
#[tokio::test]
async fn password_entry_rejects_unknown_phone_without_registration() {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));

View File

@@ -1251,7 +1251,7 @@ fn build_editor_icon_spritesheet_prompt(icon_descriptions: &[String]) -> String
fn build_editor_character_image_prompt(role_setting: &str) -> String {
vec![
"基于图1的角色美术视觉规范指导生成游戏角色形象图。画面中心构图角色主体完整置于画面中央禁止镜头透视禁止特写。背景固定为纯绿色绿幕只作为抠像底色禁止生成美术视觉规范、出现建筑、室内布景、风景、地面道具、漂浮物、烟雾叙事元素、文字或其他角色以外的场景内容。".to_string(),
"严格基于图1的角色美术视觉规范指导中的美术风格、角色头身比、角色朝向等特征生成游戏角色形象图。画面中心构图,角色主体完整置于画面中央,禁止镜头透视,禁止特写。背景固定为纯绿色绿幕,只作为抠像底色,禁止生成美术视觉规范、出现建筑、室内布景、风景、地面道具、漂浮物、烟雾叙事元素、文字或其他角色以外的场景内容。".to_string(),
format!("角色设定:{}", role_setting.trim()),
]
.join("\n")

View File

@@ -49,6 +49,7 @@ use crate::{
};
const HYPER3D_IMAGE_TO_MODEL_BODY_LIMIT_BYTES: usize = 56 * 1024 * 1024;
const EDITOR_CHARACTER_ANIMATION_BODY_LIMIT_BYTES: usize = 12 * 1024 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct PlayFlowDomainAdapter {
@@ -455,7 +456,10 @@ fn play_flow_support_router(state: AppState) -> Router<AppState> {
)
.route(
"/api/editor/character-animations/generations",
post(generate_editor_character_animation),
post(generate_editor_character_animation).layer(DefaultBodyLimit::max(
// 中文注释:画板角色动画首版仍兼容角色图 Data URL 入参,避免大于 Axum 默认 2MB 的角色图在 handler 前被拦截。
EDITOR_CHARACTER_ANIMATION_BODY_LIMIT_BYTES,
)),
)
.route(
"/api/assets/character-animation/jobs/{task_id}",