From 2c9be7c262b746b14d3f73f32dd9513d53efb5b9 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 14 Jul 2025 21:51:32 -0400 Subject: [PATCH] fix aspect ratio controls on image gen (first pass) (#723) This doesn't support all aspect ratios for all models. We'll need to come back and offer per-model options. --- .../image/enqueue_text_to_image_command.rs | 27 +++++-- .../image/generic/handle_image_artcraft.rs | 50 +++++++++--- .../image/gpt_image_1/handle_image_sora.rs | 12 ++- .../extract_featured_image.rs | 62 +++++++-------- .../src/common_extractors/extract_text.rs | 60 +++++++-------- .../src/common_extractors/extract_title.rs | 60 +++++++-------- .../promptbox/src/lib/PromptBox2D.tsx | 29 ++----- .../promptbox/src/lib/PromptBox3D.tsx | 24 ++---- .../promptbox/src/lib/PromptBoxImage.tsx | 17 +++++ .../src/lib/enqueue/EnqueueTextToImage.ts | 30 ++++++-- frontend/package-lock.json | 76 ++++++++----------- 11 files changed, 254 insertions(+), 193 deletions(-) diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image/enqueue_text_to_image_command.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image/enqueue_text_to_image_command.rs index 5d54e00255..8398efcc75 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image/enqueue_text_to_image_command.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image/enqueue_text_to_image_command.rs @@ -36,8 +36,25 @@ pub struct EnqueueTextToImageRequest { /// The model to use. pub model: Option, + + /// Aspect ratio. + pub aspect_ratio: Option, } +// TODO(bt,2025-07-14): Support other aspect ratios / resolutions - +// Flux Dev has: 4:3, 16:9, 3:4, 9:16, and custom. +// Flux Schnell has: 4:3, 16:9, 3:4, 9:16, and custom. +// Flux Pro has: 4:3, 16:9, 3:4, 9:16, and custom. +// Flux Pro Ulra has: 4:3, 16:9, 3:4, 9:16, and custom. + +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +pub enum TextToImageSize { + Auto, + Square, + Wide, + Tall, +} #[derive(Serialize)] pub struct EnqueueTextToImageSuccessResponse { @@ -65,8 +82,8 @@ pub enum EnqueueTextToImageErrorType { #[tauri::command] pub async fn enqueue_text_to_image_command( - app: AppHandle, request: EnqueueTextToImageRequest, + app: AppHandle, app_data_root: State<'_, AppDataRoot>, app_env_configs: State<'_, AppEnvConfigs>, provider_priority_store: State<'_, ProviderPriorityStore>, @@ -81,8 +98,8 @@ pub async fn enqueue_text_to_image_command( info!("enqueue_text_to_image called"); let result = handle_request( - &app, request, + &app, &app_data_root, &provider_priority_store, &task_database, @@ -146,8 +163,8 @@ pub async fn enqueue_text_to_image_command( pub async fn handle_request( - app: &AppHandle, request: EnqueueTextToImageRequest, + app: &AppHandle, app_data_root: &AppDataRoot, provider_priority_store: &ProviderPriorityStore, task_database: &TaskDatabase, @@ -160,8 +177,8 @@ pub async fn handle_request( ) -> Result { let result = dispatch_request( - &app, request, + &app, &app_data_root, &provider_priority_store, &fal_creds_manager, @@ -190,8 +207,8 @@ pub async fn handle_request( } pub async fn dispatch_request( - app: &AppHandle, request: EnqueueTextToImageRequest, + app: &AppHandle, app_data_root: &AppDataRoot, provider_priority_store: &ProviderPriorityStore, fal_creds_manager: &FalCredentialManager, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image/generic/handle_image_artcraft.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image/generic/handle_image_artcraft.rs index 4db69aa4f8..d9f32acecc 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image/generic/handle_image_artcraft.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image/generic/handle_image_artcraft.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image::enqueue_text_to_image_command::EnqueueTextToImageRequest; +use crate::core::commands::enqueue::image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; use crate::core::commands::enqueue::image::internal_image_error::InternalImageError; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; @@ -9,10 +9,10 @@ use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::data_dir::app_data_root::AppDataRoot; use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; use anyhow::anyhow; -use artcraft_api_defs::generate::image::generate_flux_1_dev_text_to_image::GenerateFlux1DevTextToImageRequest; -use artcraft_api_defs::generate::image::generate_flux_1_schnell_text_to_image::GenerateFlux1SchnellTextToImageRequest; -use artcraft_api_defs::generate::image::generate_flux_pro_11_text_to_image::GenerateFluxPro11TextToImageRequest; -use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::GenerateFluxPro11UltraTextToImageRequest; +use artcraft_api_defs::generate::image::generate_flux_1_dev_text_to_image::{GenerateFlux1DevTextToImageAspectRatio, GenerateFlux1DevTextToImageRequest}; +use artcraft_api_defs::generate::image::generate_flux_1_schnell_text_to_image::{GenerateFlux1SchnellTextToImageAspectRatio, GenerateFlux1SchnellTextToImageRequest}; +use artcraft_api_defs::generate::image::generate_flux_pro_11_text_to_image::{GenerateFluxPro11TextToImageAspectRatio, GenerateFluxPro11TextToImageRequest}; +use artcraft_api_defs::generate::image::generate_flux_pro_11_ultra_text_to_image::{GenerateFluxPro11UltraTextToImageAspectRatio, GenerateFluxPro11UltraTextToImageRequest}; use enums::common::generation_provider::GenerationProvider; use enums::tauri::tasks::task_type::TaskType; use fal_client::requests::queue::image_gen::enqueue_flux_pro_11_ultra_text_to_image::{enqueue_flux_pro_11_ultra_text_to_image, FluxPro11UltraTextToImageArgs}; @@ -66,10 +66,18 @@ pub async fn handle_image_artcraft( Some(ImageModel::Flux1Dev) => { info!("enqueue Flux 1 Dev"); selected_model = GenerationModel::Flux1Dev; + let aspect_ratio = request.aspect_ratio + .map(|aspect| match aspect { + // TODO(bt,2025-07-14): Support other aspect ratios. + TextToImageSize::Tall => GenerateFlux1DevTextToImageAspectRatio::PortraitNineBySixteen, + TextToImageSize::Wide => GenerateFlux1DevTextToImageAspectRatio::LandscapeSixteenByNine, + TextToImageSize::Auto => GenerateFlux1DevTextToImageAspectRatio::SquareHd, + TextToImageSize::Square => GenerateFlux1DevTextToImageAspectRatio::SquareHd, + }); let request = GenerateFlux1DevTextToImageRequest { uuid_idempotency_token, prompt: request.prompt, - aspect_ratio: None, + aspect_ratio, num_images: None, }; let result = generate_flux_1_dev_text_to_image( @@ -91,10 +99,18 @@ pub async fn handle_image_artcraft( Some(ImageModel::Flux1Schnell) => { info!("enqueue Flux 1 Schnell"); selected_model = GenerationModel::Flux1Schnell; + let aspect_ratio = request.aspect_ratio + .map(|aspect| match aspect { + // TODO(bt,2025-07-14): Support other aspect ratios. + TextToImageSize::Tall => GenerateFlux1SchnellTextToImageAspectRatio::PortraitNineBySixteen, + TextToImageSize::Wide => GenerateFlux1SchnellTextToImageAspectRatio::LandscapeSixteenByNine, + TextToImageSize::Auto => GenerateFlux1SchnellTextToImageAspectRatio::SquareHd, + TextToImageSize::Square => GenerateFlux1SchnellTextToImageAspectRatio::SquareHd, + }); let request = GenerateFlux1SchnellTextToImageRequest { uuid_idempotency_token, prompt: request.prompt, - aspect_ratio: None, + aspect_ratio, num_images: None, }; let result = generate_flux_1_schnell_text_to_image( @@ -116,10 +132,18 @@ pub async fn handle_image_artcraft( Some(ImageModel::FluxPro11) => { info!("enqueue Flux Pro 1.1"); selected_model = GenerationModel::FluxPro11; + let aspect_ratio = request.aspect_ratio + .map(|aspect| match aspect { + // TODO(bt,2025-07-14): Support other aspect ratios. + TextToImageSize::Tall => GenerateFluxPro11TextToImageAspectRatio::PortraitNineBySixteen, + TextToImageSize::Wide => GenerateFluxPro11TextToImageAspectRatio::LandscapeSixteenByNine, + TextToImageSize::Auto => GenerateFluxPro11TextToImageAspectRatio::Square, + TextToImageSize::Square => GenerateFluxPro11TextToImageAspectRatio::Square, + }); let request = GenerateFluxPro11TextToImageRequest { uuid_idempotency_token, prompt: request.prompt, - aspect_ratio: None, + aspect_ratio, num_images: None, }; let result = generate_flux_pro_11_text_to_image( @@ -141,10 +165,18 @@ pub async fn handle_image_artcraft( Some(ImageModel::FluxPro11Ultra) => { info!("enqueue Flux Pro 1.1 Ultra"); selected_model = GenerationModel::FluxPro11Ultra; + let aspect_ratio = request.aspect_ratio + .map(|aspect| match aspect { + // TODO(bt,2025-07-14): Support other aspect ratios. + TextToImageSize::Tall => GenerateFluxPro11UltraTextToImageAspectRatio::PortraitNineBySixteen, + TextToImageSize::Wide => GenerateFluxPro11UltraTextToImageAspectRatio::LandscapeSixteenByNine, + TextToImageSize::Auto => GenerateFluxPro11UltraTextToImageAspectRatio::Square, + TextToImageSize::Square => GenerateFluxPro11UltraTextToImageAspectRatio::Square, + }); let request = GenerateFluxPro11UltraTextToImageRequest { uuid_idempotency_token, prompt: request.prompt, - aspect_ratio: None, + aspect_ratio, num_images: None, }; let result = generate_flux_pro_11_ultra_text_to_image( diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image/gpt_image_1/handle_image_sora.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image/gpt_image_1/handle_image_sora.rs index a8f6d1484e..34a206843b 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image/gpt_image_1/handle_image_sora.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image/gpt_image_1/handle_image_sora.rs @@ -1,4 +1,4 @@ -use crate::core::commands::enqueue::image::enqueue_text_to_image_command::EnqueueTextToImageRequest; +use crate::core::commands::enqueue::image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; use crate::core::commands::enqueue::image::internal_image_error::InternalImageError; use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; @@ -46,12 +46,20 @@ pub async fn handle_image_sora( } let prompt = request.prompt.as_deref().unwrap_or(""); + + let size = match request.aspect_ratio { + None => ImageSize::Square, + Some(TextToImageSize::Auto) => ImageSize::Square, + Some(TextToImageSize::Square) => ImageSize::Square, + Some(TextToImageSize::Tall) => ImageSize::Tall, + Some(TextToImageSize::Wide) => ImageSize::Wide, + }; let result = simple_image_gen_with_session_auto_renew(SimpleImageGenAutoRenewRequest { prompt: prompt.to_string(), num_images: NumImages::One, - image_size: ImageSize::Square, + image_size: size, credentials: &creds, request_timeout: Some(SORA_SIMPLE_IMAGE_GEN_TIMEOUT), }).await; diff --git a/crates/lib/web_scrapers/src/common_extractors/extract_featured_image.rs b/crates/lib/web_scrapers/src/common_extractors/extract_featured_image.rs index 43a979853d..a9fe506e78 100644 --- a/crates/lib/web_scrapers/src/common_extractors/extract_featured_image.rs +++ b/crates/lib/web_scrapers/src/common_extractors/extract_featured_image.rs @@ -15,34 +15,34 @@ pub fn extract_featured_image(document: &Html, image_selector: &Selector) -> Opt None } -#[cfg(test)] -mod tests { - use scraper::{Html, Selector}; - - use crate::common_extractors::extract_featured_image::extract_featured_image; - use crate::sites::cnn::cnn_article_scraper::CNN_FEATURED_IMAGE_SELECTOR; - use crate::sites::techcrunch::techcrunch_article_scraper::TECHCRUNCH_FEATURED_IMAGE_SELECTOR; - - #[test] - fn test_extract_cnn_with_video() { - let html = include_str!("../../../../../test_data/html_scraping/cnn_article_with_video.html"); - let document = Html::parse_document(&html); - - let maybe_image = extract_featured_image(&document, &CNN_FEATURED_IMAGE_SELECTOR); - - assert_eq!(Some("https://media.cnn.com/api/v1/images/stellar/prod/220426143055-bill-ford-executive-chairman-ford-motor-company.jpg?c=16x9&q=w_850,c_fill"), - maybe_image.as_deref()); - } - - #[test] - fn test_extract_techcrunch() { - let html = include_str!("../../../../../test_data/html_scraping/techcrunch_article.html"); - let document = Html::parse_document(&html); - - let selector = Selector::parse(".article__title").expect("selector should parse"); - let maybe_image = extract_featured_image(&document, &TECHCRUNCH_FEATURED_IMAGE_SELECTOR); - - assert_eq!(Some("https://techcrunch.com/wp-content/uploads/2023/02/Screen-Shot-2023-02-02-at-1.46.55-PM.png?w=650"), - maybe_image.as_deref()); - } -} +//#[cfg(test)] +//mod tests { +// use scraper::{Html, Selector}; +// +// use crate::common_extractors::extract_featured_image::extract_featured_image; +// use crate::sites::cnn::cnn_article_scraper::CNN_FEATURED_IMAGE_SELECTOR; +// use crate::sites::techcrunch::techcrunch_article_scraper::TECHCRUNCH_FEATURED_IMAGE_SELECTOR; +// +// #[test] +// fn test_extract_cnn_with_video() { +// let html = include_str!("../../../../../test_data/html_scraping/cnn_article_with_video.html"); +// let document = Html::parse_document(&html); +// +// let maybe_image = extract_featured_image(&document, &CNN_FEATURED_IMAGE_SELECTOR); +// +// assert_eq!(Some("https://media.cnn.com/api/v1/images/stellar/prod/220426143055-bill-ford-executive-chairman-ford-motor-company.jpg?c=16x9&q=w_850,c_fill"), +// maybe_image.as_deref()); +// } +// +// #[test] +// fn test_extract_techcrunch() { +// let html = include_str!("../../../../../test_data/html_scraping/techcrunch_article.html"); +// let document = Html::parse_document(&html); +// +// let selector = Selector::parse(".article__title").expect("selector should parse"); +// let maybe_image = extract_featured_image(&document, &TECHCRUNCH_FEATURED_IMAGE_SELECTOR); +// +// assert_eq!(Some("https://techcrunch.com/wp-content/uploads/2023/02/Screen-Shot-2023-02-02-at-1.46.55-PM.png?w=650"), +// maybe_image.as_deref()); +// } +//} diff --git a/crates/lib/web_scrapers/src/common_extractors/extract_text.rs b/crates/lib/web_scrapers/src/common_extractors/extract_text.rs index d9d29f2598..8ad2ce4520 100644 --- a/crates/lib/web_scrapers/src/common_extractors/extract_text.rs +++ b/crates/lib/web_scrapers/src/common_extractors/extract_text.rs @@ -18,33 +18,33 @@ pub fn extract_text(document: &Html, text_selector: &Selector) -> Option Some(pieces.join(" ").trim().to_string()) } -#[cfg(test)] -mod tests { - use scraper::Html; - - use crate::common_extractors::extract_text::extract_text; - use crate::sites::cnn::cnn_article_scraper::CNN_TITLE_SELECTOR; - use crate::sites::techcrunch::techcrunch_article_scraper::TECHCRUNCH_TITLE_SELECTOR; - - #[test] - fn test_extract_text_cnn() { - let html = include_str!("../../../../../test_data/html_scraping/cnn_article_with_video.html"); - let document = Html::parse_document(&html); - - let maybe_text = extract_text(&document, &CNN_TITLE_SELECTOR); - - assert_eq!(Some("Ford Mustang Mach-E has a mile of wires it doesn’t need. That’s a big deal"), - maybe_text.as_deref()); - } - - #[test] - fn test_extract_text_techcrunch() { - let html = include_str!("../../../../../test_data/html_scraping/techcrunch_article.html"); - let document = Html::parse_document(&html); - - let maybe_text = extract_text(&document, &TECHCRUNCH_TITLE_SELECTOR); - - assert_eq!(Some("‘Nothing, Forever,’ an AI ‘Seinfeld’ spoof, is the next ‘Twitch Plays Pokémon’"), - maybe_text.as_deref()); - } -} +//#[cfg(test)] +//mod tests { +// use scraper::Html; +// +// use crate::common_extractors::extract_text::extract_text; +// use crate::sites::cnn::cnn_article_scraper::CNN_TITLE_SELECTOR; +// use crate::sites::techcrunch::techcrunch_article_scraper::TECHCRUNCH_TITLE_SELECTOR; +// +// #[test] +// fn test_extract_text_cnn() { +// let html = include_str!("../../../../../test_data/html_scraping/cnn_article_with_video.html"); +// let document = Html::parse_document(&html); +// +// let maybe_text = extract_text(&document, &CNN_TITLE_SELECTOR); +// +// assert_eq!(Some("Ford Mustang Mach-E has a mile of wires it doesn’t need. That’s a big deal"), +// maybe_text.as_deref()); +// } +// +// #[test] +// fn test_extract_text_techcrunch() { +// let html = include_str!("../../../../../test_data/html_scraping/techcrunch_article.html"); +// let document = Html::parse_document(&html); +// +// let maybe_text = extract_text(&document, &TECHCRUNCH_TITLE_SELECTOR); +// +// assert_eq!(Some("‘Nothing, Forever,’ an AI ‘Seinfeld’ spoof, is the next ‘Twitch Plays Pokémon’"), +// maybe_text.as_deref()); +// } +//} diff --git a/crates/lib/web_scrapers/src/common_extractors/extract_title.rs b/crates/lib/web_scrapers/src/common_extractors/extract_title.rs index 1eceaafae4..a1ef30c50a 100644 --- a/crates/lib/web_scrapers/src/common_extractors/extract_title.rs +++ b/crates/lib/web_scrapers/src/common_extractors/extract_title.rs @@ -18,33 +18,33 @@ pub fn extract_title(document: &Html, title_selector: &Selector) -> Option image.mediaToken - // ), - // maybe_number_of_samples: 1, - // }, - //}); - //console.log("Generate response:", generateResponse); - //toast.success("Please wait while we process your image."); - - // TODO: Aspect ratio - // const aspectRatio = getCurrentSoraRemixAspectRatio(); + const aspectRatio = getCurrentAspectRatio(); const generateResponse = await EnqueueContextualEditImage({ model: EnqueueContextualEditImageModel.GptImage1, @@ -336,7 +323,7 @@ export const PromptBox2D = ({ disable_system_prompt: !useSystemPrompt, prompt: prompt, image_count: 1, - //aspect_ratio: aspectRatio, + aspect_ratio: aspectRatio, }); console.log("generateResponse", generateResponse); @@ -445,16 +432,16 @@ export const PromptBox2D = ({ return iconElement.props.icon; }; - const getCurrentSoraRemixAspectRatio = (): SoraImageRemixAspectRatio => { + const getCurrentAspectRatio = (): EnqueueContextualEditImageSize => { const selected = aspectRatioList.find((item) => item.selected); switch (selected?.label) { case "3:2": - return SoraImageRemixAspectRatio.Wide; + return EnqueueContextualEditImageSize.Wide; case "2:3": - return SoraImageRemixAspectRatio.Tall; + return EnqueueContextualEditImageSize.Tall; case "1:1": default: - return SoraImageRemixAspectRatio.Square; + return EnqueueContextualEditImageSize.Square; } }; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx b/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx index b8a0a0371f..3ce07f1825 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx @@ -53,6 +53,7 @@ import { CommandSuccessStatus, EnqueueContextualEditImage, EnqueueContextualEditImageModel, + EnqueueContextualEditImageSize, GetAppPreferences, SoraImageRemix, SoraImageRemixAspectRatio, @@ -446,18 +447,7 @@ export const PromptBox3D = ({ console.log("snapshotResult", snapshotResult); - const aspectRatio = getCurrentSoraRemixAspectRatio(); - - //const generateResponse = await SoraImageRemix({ - // snapshot_media_token: snapshotResult.data!, - // disable_system_prompt: !useSystemPrompt, - // prompt: prompt, - // maybe_additional_images: referenceImages.map( - // (image) => image.mediaToken - // ), - // maybe_number_of_samples: 1, - // aspect_ratio: aspectRatio, - //}); + const aspectRatio = getCurrentAspectRatio(); const generateResponse = await EnqueueContextualEditImage({ model: EnqueueContextualEditImageModel.GptImage1, @@ -468,7 +458,7 @@ export const PromptBox3D = ({ disable_system_prompt: !useSystemPrompt, prompt: prompt, image_count: 1, - //aspect_ratio: aspectRatio, + aspect_ratio: aspectRatio, }); console.log("generateResponse", generateResponse); @@ -525,16 +515,16 @@ export const PromptBox3D = ({ } }; - const getCurrentSoraRemixAspectRatio = (): SoraImageRemixAspectRatio => { + const getCurrentAspectRatio = (): EnqueueContextualEditImageSize => { switch (cameraAspectRatio.value) { case CameraAspectRatio.HORIZONTAL_3_2: - return SoraImageRemixAspectRatio.Wide; + return EnqueueContextualEditImageSize.Wide; case CameraAspectRatio.VERTICAL_2_3: case CameraAspectRatio.VERTICAL_9_16: - return SoraImageRemixAspectRatio.Tall; + return EnqueueContextualEditImageSize.Tall; case CameraAspectRatio.SQUARE_1_1: default: - return SoraImageRemixAspectRatio.Square; + return EnqueueContextualEditImageSize.Square; } }; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx index f5ac59cff1..7b2417d309 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx @@ -9,6 +9,7 @@ import { Modal } from "@storyteller/ui-modal"; import { EnqueueTextToImage, EnqueueTextToImageModel, + EnqueueTextToImageSize, } from "@storyteller/tauri-api"; import { faMessageXmark, @@ -205,9 +206,12 @@ export const PromptBoxImage = ({ setIsEnqueueing(false); }, 10000); + const aspectRatio = getCurrentAspectRatio(); + const generateResponse = await EnqueueTextToImage({ prompt: prompt, model: modelInfo, + aspect_ratio: aspectRatio, }); console.log("PromptBoxImage - generateResponse", generateResponse); @@ -224,6 +228,19 @@ export const PromptBoxImage = ({ return iconElement.props.icon; }; + const getCurrentAspectRatio = (): EnqueueTextToImageSize => { + const selected = aspectRatioList.find((item) => item.selected); + switch (selected?.label) { + case "3:2": + return EnqueueTextToImageSize.Wide; + case "2:3": + return EnqueueTextToImageSize.Tall; + case "1:1": + default: + return EnqueueTextToImageSize.Square; + } + }; + const getModelByName = (name: string): EnqueueTextToImageModel => { switch (name) { case "Flux Pro Ultra": diff --git a/frontend/libs/tauri-api/src/lib/enqueue/EnqueueTextToImage.ts b/frontend/libs/tauri-api/src/lib/enqueue/EnqueueTextToImage.ts index 009d77012b..397d100841 100644 --- a/frontend/libs/tauri-api/src/lib/enqueue/EnqueueTextToImage.ts +++ b/frontend/libs/tauri-api/src/lib/enqueue/EnqueueTextToImage.ts @@ -16,6 +16,13 @@ export enum EnqueueTextToImageErrorType { export interface EnqueueTextToImageRequest { prompt?: string; model?: ModelInfo | EnqueueTextToImageModel; + aspect_ratio?: EnqueueTextToImageSize; +} + +interface EnqueueTextToImageRawRequest { + prompt?: string; + model?: EnqueueTextToImageModel | string; // TODO: Shouldn't allow string + aspect_ratio?: EnqueueTextToImageSize; } export enum EnqueueTextToImageModel { @@ -24,6 +31,13 @@ export enum EnqueueTextToImageModel { Recraft3 = "recraft_3", } +export enum EnqueueTextToImageSize { + Auto = "auto", + Square = "square", + Wide = "wide", + Tall = "tall", +} + export interface EnqueueTextToImageError extends CommandResult { error_type: EnqueueTextToImageErrorType; error_message?: string; @@ -52,11 +66,17 @@ export const EnqueueTextToImage = async (request: EnqueueTextToImageRequest) : P } } - const result = await invoke("enqueue_text_to_image_command", { - request: { - prompt: request.prompt, - model: modelName, - } + let mutableRequest : EnqueueTextToImageRawRequest = { + prompt: request.prompt, + model: modelName, + }; + + if (!!request.aspect_ratio) { + mutableRequest.aspect_ratio = request.aspect_ratio; + } + + const result = await invoke("enqueue_text_to_image_command", { + request: mutableRequest, }); return (result as EnqueueTextToImageResult); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2d7cd8c08e..4845085b5f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4327,7 +4327,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -9856,7 +9856,7 @@ "version": "1.13.3", "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.13.3.tgz", "integrity": "sha512-OGsvXIid2Go21kiNqeTIn79jcaX4l0G93X2rAnas4LFoDyA9wAwVK7xZdm+QsKoMn5Mus2yFLCc4OtX2dD/PWA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 10" @@ -9874,7 +9874,7 @@ "version": "1.9.2", "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.9.2.tgz", "integrity": "sha512-BBjg0QNuEEmJSoU/++JOXhrjWdu3PTyYeJWsvchsI0Aqtj8ICkz/DqlwtXbmZVZ5vuDPpTfFlwDBZe81zgShMA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@swc-node/core": "^1.13.1", @@ -9897,7 +9897,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.5.1.tgz", "integrity": "sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "source-map-support": "^0.5.21", @@ -9908,7 +9908,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -9919,7 +9919,7 @@ "version": "1.5.29", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.5.29.tgz", "integrity": "sha512-nvTtHJI43DUSOAf3h9XsqYg8YXKc0/N4il9y4j0xAkO0ekgDNo+3+jbw6MInawjKJF9uulyr+f5bAutTsOKVlw==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -9961,7 +9961,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -9978,7 +9977,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -9995,7 +9993,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -10012,7 +10009,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10029,7 +10025,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10046,7 +10041,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10063,7 +10057,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10080,7 +10073,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10097,7 +10089,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10114,7 +10105,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10128,7 +10118,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@swc/helpers": { @@ -10144,7 +10134,7 @@ "version": "0.1.21", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -10664,7 +10654,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/prop-types": { @@ -10696,7 +10686,7 @@ "version": "19.0.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.0.tgz", "integrity": "sha512-1KfiQKsH1o00p9m5ag12axHQSb3FOU9H20UTrujVSkNhuCrRHiQWFqgEnTNK5ZNfnzZv8UWrnXVqCmCF9fgY3w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/react": "*" @@ -13342,7 +13332,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", @@ -13358,7 +13348,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", @@ -13375,7 +13365,7 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, + "devOptional": true, "license": "ISC", "engines": { "node": ">= 6" @@ -13840,7 +13830,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -14248,7 +14238,7 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/columnify": { @@ -15447,7 +15437,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -18516,7 +18506,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -18672,7 +18662,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/is-async-function": { @@ -19501,7 +19491,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -19645,7 +19635,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -22724,7 +22714,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -22756,7 +22746,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -22775,7 +22765,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/parse-ms": { @@ -22858,7 +22848,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/path-scurry": { @@ -22893,7 +22883,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -22982,7 +22972,7 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 6" @@ -24385,7 +24375,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", @@ -24417,7 +24407,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=4" @@ -25775,7 +25765,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -26057,7 +26047,7 @@ "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -26131,14 +26121,14 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/terser/node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -26691,7 +26681,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc",