543 lines
20 KiB
Rust
543 lines
20 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum WoodenFishGenerationStatus {
|
|
Draft,
|
|
Generating,
|
|
Ready,
|
|
Failed,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum WoodenFishActionType {
|
|
CompileDraft,
|
|
RegenerateHitObject,
|
|
GenerateHitSound,
|
|
ReplaceHitSound,
|
|
UpdateWorkMeta,
|
|
UpdateFloatingWords,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum WoodenFishRunStatus {
|
|
Playing,
|
|
Finished,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishImageAsset {
|
|
pub asset_id: String,
|
|
pub image_src: String,
|
|
pub image_object_key: String,
|
|
pub asset_object_id: String,
|
|
pub generation_provider: String,
|
|
pub prompt: String,
|
|
pub width: u32,
|
|
pub height: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishAudioAsset {
|
|
pub asset_id: String,
|
|
pub audio_src: String,
|
|
pub audio_object_key: String,
|
|
pub asset_object_id: String,
|
|
pub source: String,
|
|
#[serde(default)]
|
|
pub prompt: Option<String>,
|
|
#[serde(default)]
|
|
pub duration_ms: Option<u32>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishWorkspaceCreateRequest {
|
|
pub template_id: String,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
pub theme_tags: Vec<String>,
|
|
pub hit_object_prompt: String,
|
|
#[serde(default)]
|
|
pub hit_object_reference_image_src: Option<String>,
|
|
#[serde(default)]
|
|
pub hit_sound_prompt: Option<String>,
|
|
#[serde(default)]
|
|
pub hit_sound_asset: Option<WoodenFishAudioAsset>,
|
|
pub floating_words: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishActionRequest {
|
|
pub action_type: WoodenFishActionType,
|
|
#[serde(default, skip_deserializing)]
|
|
pub profile_id: Option<String>,
|
|
#[serde(default)]
|
|
pub work_title: Option<String>,
|
|
#[serde(default)]
|
|
pub work_description: Option<String>,
|
|
#[serde(default)]
|
|
pub theme_tags: Option<Vec<String>>,
|
|
#[serde(default)]
|
|
pub hit_object_prompt: Option<String>,
|
|
#[serde(default)]
|
|
pub hit_object_reference_image_src: Option<String>,
|
|
#[serde(default, skip_deserializing)]
|
|
pub hit_object_asset: Option<WoodenFishImageAsset>,
|
|
#[serde(default)]
|
|
#[serde(skip_deserializing)]
|
|
pub background_asset: Option<WoodenFishImageAsset>,
|
|
#[serde(default)]
|
|
#[serde(skip_deserializing)]
|
|
pub back_button_asset: Option<WoodenFishImageAsset>,
|
|
#[serde(default)]
|
|
pub hit_sound_prompt: Option<String>,
|
|
#[serde(default)]
|
|
pub hit_sound_asset: Option<WoodenFishAudioAsset>,
|
|
#[serde(default)]
|
|
pub floating_words: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishWordCounter {
|
|
pub text: String,
|
|
pub count: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishDraftResponse {
|
|
pub template_id: String,
|
|
pub template_name: String,
|
|
#[serde(default)]
|
|
pub profile_id: Option<String>,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
pub theme_tags: Vec<String>,
|
|
pub hit_object_prompt: String,
|
|
#[serde(default)]
|
|
pub hit_object_reference_image_src: Option<String>,
|
|
#[serde(default)]
|
|
pub hit_sound_prompt: Option<String>,
|
|
pub floating_words: Vec<String>,
|
|
#[serde(default)]
|
|
pub hit_object_asset: Option<WoodenFishImageAsset>,
|
|
#[serde(default)]
|
|
pub background_asset: Option<WoodenFishImageAsset>,
|
|
#[serde(default)]
|
|
pub back_button_asset: Option<WoodenFishImageAsset>,
|
|
#[serde(default)]
|
|
pub hit_sound_asset: Option<WoodenFishAudioAsset>,
|
|
#[serde(default)]
|
|
pub cover_image_src: Option<String>,
|
|
pub generation_status: WoodenFishGenerationStatus,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishSessionSnapshotResponse {
|
|
pub session_id: String,
|
|
pub owner_user_id: String,
|
|
pub status: WoodenFishGenerationStatus,
|
|
#[serde(default)]
|
|
pub draft: Option<WoodenFishDraftResponse>,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishSessionResponse {
|
|
pub session: WoodenFishSessionSnapshotResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishActionResponse {
|
|
pub action_type: WoodenFishActionType,
|
|
pub session: WoodenFishSessionSnapshotResponse,
|
|
#[serde(default)]
|
|
pub work: Option<WoodenFishWorkProfileResponse>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishWorkSummaryResponse {
|
|
pub runtime_kind: String,
|
|
pub work_id: String,
|
|
pub profile_id: String,
|
|
pub owner_user_id: String,
|
|
#[serde(default)]
|
|
pub source_session_id: Option<String>,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
pub theme_tags: Vec<String>,
|
|
#[serde(default)]
|
|
pub cover_image_src: Option<String>,
|
|
pub publication_status: String,
|
|
pub play_count: u32,
|
|
pub updated_at: String,
|
|
#[serde(default)]
|
|
pub published_at: Option<String>,
|
|
pub publish_ready: bool,
|
|
pub generation_status: WoodenFishGenerationStatus,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishWorkProfileResponse {
|
|
pub summary: WoodenFishWorkSummaryResponse,
|
|
pub draft: WoodenFishDraftResponse,
|
|
pub hit_object_asset: WoodenFishImageAsset,
|
|
#[serde(default)]
|
|
pub background_asset: Option<WoodenFishImageAsset>,
|
|
#[serde(default)]
|
|
pub back_button_asset: Option<WoodenFishImageAsset>,
|
|
pub hit_sound_asset: WoodenFishAudioAsset,
|
|
pub floating_words: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishWorkDetailResponse {
|
|
pub item: WoodenFishWorkProfileResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishWorkMutationResponse {
|
|
pub item: WoodenFishWorkProfileResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishGalleryCardResponse {
|
|
pub public_work_code: String,
|
|
pub work_id: String,
|
|
pub profile_id: String,
|
|
pub owner_user_id: String,
|
|
pub author_display_name: String,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
#[serde(default)]
|
|
pub cover_image_src: Option<String>,
|
|
pub theme_tags: Vec<String>,
|
|
pub publication_status: String,
|
|
pub play_count: u32,
|
|
pub updated_at: String,
|
|
#[serde(default)]
|
|
pub published_at: Option<String>,
|
|
pub generation_status: WoodenFishGenerationStatus,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishGalleryResponse {
|
|
pub items: Vec<WoodenFishGalleryCardResponse>,
|
|
pub has_more: bool,
|
|
#[serde(default)]
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishGalleryDetailResponse {
|
|
pub item: WoodenFishWorkProfileResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishRuntimeRunSnapshotResponse {
|
|
pub run_id: String,
|
|
pub profile_id: String,
|
|
pub owner_user_id: String,
|
|
pub status: WoodenFishRunStatus,
|
|
pub total_tap_count: u32,
|
|
pub word_counters: Vec<WoodenFishWordCounter>,
|
|
pub started_at_ms: u64,
|
|
pub updated_at_ms: u64,
|
|
#[serde(default)]
|
|
pub finished_at_ms: Option<u64>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishRunResponse {
|
|
pub run: WoodenFishRuntimeRunSnapshotResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishStartRunRequest {
|
|
pub profile_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishCheckpointRunRequest {
|
|
pub total_tap_count: u32,
|
|
pub word_counters: Vec<WoodenFishWordCounter>,
|
|
pub client_event_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WoodenFishFinishRunRequest {
|
|
pub total_tap_count: u32,
|
|
pub word_counters: Vec<WoodenFishWordCounter>,
|
|
pub client_event_id: String,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn wooden_fish_workspace_request_uses_camel_case_and_default_words() {
|
|
let payload = serde_json::to_value(WoodenFishWorkspaceCreateRequest {
|
|
template_id: "wooden-fish".to_string(),
|
|
work_title: "今日敲木鱼".to_string(),
|
|
work_description: "轻松敲击".to_string(),
|
|
theme_tags: vec!["休闲".to_string()],
|
|
hit_object_prompt: "卡通木鱼".to_string(),
|
|
hit_object_reference_image_src: None,
|
|
hit_sound_prompt: Some("清脆木鱼声".to_string()),
|
|
hit_sound_asset: None,
|
|
floating_words: vec![
|
|
"幸运".to_string(),
|
|
"健康".to_string(),
|
|
"财富".to_string(),
|
|
"姻缘".to_string(),
|
|
"幸福".to_string(),
|
|
"事业".to_string(),
|
|
"成功".to_string(),
|
|
"功德".to_string(),
|
|
],
|
|
})
|
|
.expect("payload should serialize");
|
|
|
|
assert_eq!(payload["templateId"], json!("wooden-fish"));
|
|
assert_eq!(payload["hitObjectPrompt"], json!("卡通木鱼"));
|
|
assert_eq!(payload["hitSoundPrompt"], json!("清脆木鱼声"));
|
|
assert_eq!(payload["floatingWords"][7], json!("功德"));
|
|
}
|
|
|
|
#[test]
|
|
fn wooden_fish_runtime_snapshot_counts_words_inside_one_run() {
|
|
let payload = serde_json::to_value(WoodenFishRuntimeRunSnapshotResponse {
|
|
run_id: "wooden-fish-run-1".to_string(),
|
|
profile_id: "wooden-fish-profile-1".to_string(),
|
|
owner_user_id: "user-1".to_string(),
|
|
status: WoodenFishRunStatus::Playing,
|
|
total_tap_count: 3,
|
|
word_counters: vec![
|
|
WoodenFishWordCounter {
|
|
text: "幸运".to_string(),
|
|
count: 2,
|
|
},
|
|
WoodenFishWordCounter {
|
|
text: "功德".to_string(),
|
|
count: 1,
|
|
},
|
|
],
|
|
started_at_ms: 100,
|
|
updated_at_ms: 130,
|
|
finished_at_ms: None,
|
|
})
|
|
.expect("payload should serialize");
|
|
|
|
assert_eq!(payload["status"], json!("playing"));
|
|
assert_eq!(payload["totalTapCount"], json!(3));
|
|
assert_eq!(payload["wordCounters"][0]["text"], json!("幸运"));
|
|
}
|
|
|
|
#[test]
|
|
fn wooden_fish_action_request_serializes_audio_and_image_fields() {
|
|
let payload = serde_json::to_value(WoodenFishActionRequest {
|
|
action_type: WoodenFishActionType::ReplaceHitSound,
|
|
profile_id: Some("wooden-fish-profile-1".to_string()),
|
|
work_title: None,
|
|
work_description: None,
|
|
theme_tags: None,
|
|
hit_object_prompt: Some("卡通铜钹".to_string()),
|
|
hit_object_reference_image_src: Some("/uploads/reference.png".to_string()),
|
|
hit_object_asset: Some(WoodenFishImageAsset {
|
|
asset_id: "image-1".to_string(),
|
|
image_src: "/generated-wooden-fish-assets/profile/hit-object/image.png".to_string(),
|
|
image_object_key: "generated-wooden-fish-assets/profile/hit-object/image.png"
|
|
.to_string(),
|
|
asset_object_id: "image-object-1".to_string(),
|
|
generation_provider: "image2".to_string(),
|
|
prompt: "卡通铜钹".to_string(),
|
|
width: 1024,
|
|
height: 1024,
|
|
}),
|
|
background_asset: Some(WoodenFishImageAsset {
|
|
asset_id: "background-1".to_string(),
|
|
image_src: "/generated-wooden-fish-assets/profile/background/image.png".to_string(),
|
|
image_object_key: "generated-wooden-fish-assets/profile/background/image.png"
|
|
.to_string(),
|
|
asset_object_id: "background-object-1".to_string(),
|
|
generation_provider: "image2".to_string(),
|
|
prompt: "赛博莲花背景".to_string(),
|
|
width: 1024,
|
|
height: 1536,
|
|
}),
|
|
back_button_asset: Some(WoodenFishImageAsset {
|
|
asset_id: "back-button-1".to_string(),
|
|
image_src: "/generated-wooden-fish-assets/profile/back-button/image.png"
|
|
.to_string(),
|
|
image_object_key: "generated-wooden-fish-assets/profile/back-button/image.png"
|
|
.to_string(),
|
|
asset_object_id: "back-button-object-1".to_string(),
|
|
generation_provider: "image2".to_string(),
|
|
prompt: "赛博莲花返回按钮".to_string(),
|
|
width: 1024,
|
|
height: 1024,
|
|
}),
|
|
hit_sound_prompt: Some("短促木鱼声".to_string()),
|
|
hit_sound_asset: Some(WoodenFishAudioAsset {
|
|
asset_id: "sound-1".to_string(),
|
|
audio_src: "/generated/wooden-fish.mp3".to_string(),
|
|
audio_object_key: "generated/wooden-fish.mp3".to_string(),
|
|
asset_object_id: "asset-object-1".to_string(),
|
|
source: "upload".to_string(),
|
|
prompt: None,
|
|
duration_ms: Some(800),
|
|
}),
|
|
floating_words: Some(vec!["功德".to_string()]),
|
|
})
|
|
.expect("payload should serialize");
|
|
|
|
assert_eq!(payload["actionType"], json!("replace-hit-sound"));
|
|
assert_eq!(payload["profileId"], json!("wooden-fish-profile-1"));
|
|
assert_eq!(payload["hitObjectPrompt"], json!("卡通铜钹"));
|
|
assert_eq!(
|
|
payload["hitObjectAsset"]["imageObjectKey"],
|
|
json!("generated-wooden-fish-assets/profile/hit-object/image.png")
|
|
);
|
|
assert_eq!(payload["backgroundAsset"]["height"], json!(1536));
|
|
assert_eq!(payload["backButtonAsset"]["width"], json!(1024));
|
|
assert_eq!(payload["hitSoundAsset"]["source"], json!("upload"));
|
|
assert_eq!(payload["hitSoundAsset"]["durationMs"], json!(800));
|
|
}
|
|
|
|
#[test]
|
|
fn wooden_fish_action_request_ignores_client_hit_object_asset() {
|
|
let payload = serde_json::from_value::<WoodenFishActionRequest>(json!({
|
|
"actionType": "compile-draft",
|
|
"hitObjectPrompt": "卡通铜钹",
|
|
"hitObjectAsset": {
|
|
"assetId": "client-image",
|
|
"imageSrc": "/generated-wooden-fish-assets/client/image.png",
|
|
"imageObjectKey": "generated-wooden-fish-assets/client/image.png",
|
|
"assetObjectId": "client-asset-object",
|
|
"generationProvider": "client",
|
|
"prompt": "跳过生成",
|
|
"width": 1024,
|
|
"height": 1024
|
|
}
|
|
}))
|
|
.expect("payload should deserialize");
|
|
|
|
assert_eq!(payload.action_type, WoodenFishActionType::CompileDraft);
|
|
assert_eq!(payload.hit_object_prompt.as_deref(), Some("卡通铜钹"));
|
|
assert_eq!(payload.hit_object_asset, None);
|
|
}
|
|
|
|
#[test]
|
|
fn wooden_fish_work_profile_keeps_summary_and_runtime_assets() {
|
|
let image = WoodenFishImageAsset {
|
|
asset_id: "image-1".to_string(),
|
|
image_src: "/generated/wooden-fish.png".to_string(),
|
|
image_object_key: "generated/wooden-fish.png".to_string(),
|
|
asset_object_id: "image-object-1".to_string(),
|
|
generation_provider: "image2".to_string(),
|
|
prompt: "卡通木鱼".to_string(),
|
|
width: 1024,
|
|
height: 1024,
|
|
};
|
|
let audio = WoodenFishAudioAsset {
|
|
asset_id: "sound-1".to_string(),
|
|
audio_src: "/generated/wooden-fish.mp3".to_string(),
|
|
audio_object_key: "generated/wooden-fish.mp3".to_string(),
|
|
asset_object_id: "sound-object-1".to_string(),
|
|
source: "generated".to_string(),
|
|
prompt: Some("清脆木鱼".to_string()),
|
|
duration_ms: Some(600),
|
|
};
|
|
let back_button = WoodenFishImageAsset {
|
|
asset_id: "back-button-1".to_string(),
|
|
image_src: "/generated/wooden-fish-back-button.png".to_string(),
|
|
image_object_key: "generated/wooden-fish-back-button.png".to_string(),
|
|
asset_object_id: "back-button-object-1".to_string(),
|
|
generation_provider: "image2".to_string(),
|
|
prompt: "主题返回按钮".to_string(),
|
|
width: 1024,
|
|
height: 1024,
|
|
};
|
|
let profile = WoodenFishWorkProfileResponse {
|
|
summary: WoodenFishWorkSummaryResponse {
|
|
runtime_kind: "wooden-fish".to_string(),
|
|
work_id: "wooden-fish-profile-1".to_string(),
|
|
profile_id: "wooden-fish-profile-1".to_string(),
|
|
owner_user_id: "user-1".to_string(),
|
|
source_session_id: Some("wooden-fish-session-1".to_string()),
|
|
work_title: "敲木鱼".to_string(),
|
|
work_description: String::new(),
|
|
theme_tags: vec!["休闲".to_string()],
|
|
cover_image_src: Some(image.image_src.clone()),
|
|
publication_status: "draft".to_string(),
|
|
play_count: 0,
|
|
updated_at: "2026-05-20T00:00:00Z".to_string(),
|
|
published_at: None,
|
|
publish_ready: true,
|
|
generation_status: WoodenFishGenerationStatus::Ready,
|
|
},
|
|
draft: WoodenFishDraftResponse {
|
|
template_id: "wooden-fish".to_string(),
|
|
template_name: "敲木鱼".to_string(),
|
|
profile_id: Some("wooden-fish-profile-1".to_string()),
|
|
work_title: "敲木鱼".to_string(),
|
|
work_description: String::new(),
|
|
theme_tags: vec!["休闲".to_string()],
|
|
hit_object_prompt: "卡通木鱼".to_string(),
|
|
hit_object_reference_image_src: None,
|
|
hit_sound_prompt: Some("清脆木鱼".to_string()),
|
|
floating_words: vec!["功德".to_string()],
|
|
hit_object_asset: Some(image.clone()),
|
|
background_asset: None,
|
|
back_button_asset: Some(back_button.clone()),
|
|
hit_sound_asset: Some(audio.clone()),
|
|
cover_image_src: Some(image.image_src.clone()),
|
|
generation_status: WoodenFishGenerationStatus::Ready,
|
|
},
|
|
hit_object_asset: image,
|
|
background_asset: None,
|
|
back_button_asset: Some(back_button),
|
|
hit_sound_asset: audio,
|
|
floating_words: vec!["功德".to_string()],
|
|
};
|
|
|
|
let payload = serde_json::to_value(profile).expect("profile should serialize");
|
|
|
|
assert_eq!(payload["summary"]["runtimeKind"], json!("wooden-fish"));
|
|
assert_eq!(
|
|
payload["hitObjectAsset"]["generationProvider"],
|
|
json!("image2")
|
|
);
|
|
assert_eq!(payload["hitSoundAsset"]["source"], json!("generated"));
|
|
assert_eq!(
|
|
payload["backButtonAsset"]["imageSrc"],
|
|
json!("/generated/wooden-fish-back-button.png")
|
|
);
|
|
}
|
|
}
|