From 00288aacf2cd9c8d209d9fe3459a9c33967e540a Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 30 Apr 2026 22:06:15 -0400 Subject: [PATCH] refactor wip --- ...ndle_artcraft_gpt_image_1_text_to_image.rs | 112 ++++++++ .../artcraft/handle_text_to_image_artcraft.rs | 46 ++++ ...andle_text_to_image_artcraft_via_router.rs | 159 +++++++++++ .../generate/generate_image/artcraft/mod.rs | 4 + .../generate_image/generate_image_command.rs | 4 +- .../generate_image/grok/handle_grok.rs | 92 +++++++ .../generate/generate_image/grok/mod.rs | 1 + .../midjourney/handle_midjourney.rs | 140 ++++++++++ .../generate/generate_image/midjourney/mod.rs | 1 + .../commands/generate/generate_image/mod.rs | 3 +- .../handle_gpt_image_1_sora_text_to_image.rs | 135 ++++++++++ .../sora/handle_text_to_image_sora.rs | 21 ++ .../generate/generate_image/sora/mod.rs | 2 + ...est.rs => tauri_generate_image_request.rs} | 94 +------ .../generate_image/tauri_image_model.rs | 92 +++++++ frontend/libs/tauri-api/src/index.ts | 1 + .../src/lib/enqueue/GenerateVideoCommand.ts | 192 +++++++++++++ frontend/package-lock.json | 253 ++++++------------ 18 files changed, 1088 insertions(+), 264 deletions(-) create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_artcraft_gpt_image_1_text_to_image.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft_via_router.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/mod.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/handle_grok.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/mod.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/handle_midjourney.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/mod.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_gpt_image_1_sora_text_to_image.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_text_to_image_sora.rs create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/mod.rs rename crates/desktop/artcraft/src/core/commands/generate/generate_image/{request.rs => tauri_generate_image_request.rs} (55%) create mode 100644 crates/desktop/artcraft/src/core/commands/generate/generate_image/tauri_image_model.rs create mode 100644 frontend/libs/tauri-api/src/lib/enqueue/GenerateVideoCommand.ts diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_artcraft_gpt_image_1_text_to_image.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_artcraft_gpt_image_1_text_to_image.rs new file mode 100644 index 0000000000..4265016619 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_artcraft_gpt_image_1_text_to_image.rs @@ -0,0 +1,112 @@ +use crate::core::commands::enqueue::generate_error::GenerateError; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::state::provider_priority::ProviderPriorityStore; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use artcraft_api_defs::generate::image::text::generate_gpt_image_1_text_to_image::{GenerateGptImage1TextToImageImageQuality, GenerateGptImage1TextToImageImageSize, GenerateGptImage1TextToImageNumImages, GenerateGptImage1TextToImageRequest}; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use uuid_utils::uuid::generate_random_uuid; +use log::{error, info}; +use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session; +use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest}; +use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages}; +use std::time::Duration; +use artcraft_client::endpoints::generate::image::edit::gpt_image_1_edit_image::gpt_image_1_edit_image; +use artcraft_client::endpoints::generate::image::text::generate_gpt_image_1_text_to_image::generate_gpt_image_1_text_to_image; +use tauri::AppHandle; + +pub async fn handle_artcraft_gpt_image_1_text_to_image( + request: &EnqueueTextToImageRequest, + app_env_configs: &AppEnvConfigs, + storyteller_creds_manager: &StorytellerCredentialManager, +) -> Result { + + let creds = match storyteller_creds_manager.get_credentials()? { + Some(creds) => creds, + None => { + return Err(GenerateError::needs_storyteller_credentials()); + }, + }; + + info!("Calling Artcraft gpt-image-1 ..."); + + let uuid_idempotency_token = generate_random_uuid(); + + let image_quality = Some(GenerateGptImage1TextToImageImageQuality::High); + + //let image_quality = request.image_quality + // .map(|quality| match quality { + // EditImageQuality::Auto => GenerateGptImage1TextToImageImageQuality::Auto, + // EditImageQuality::High => GenerateGptImage1TextToImageImageQuality::High, + // EditImageQuality::Medium => GenerateGptImage1TextToImageImageQuality::Medium, + // EditImageQuality::Low => GenerateGptImage1TextToImageImageQuality::Low, + // }); + + let image_size = request.aspect_ratio + .map(|size| match size { + TextToImageSize::Auto => GenerateGptImage1TextToImageImageSize::Square, + TextToImageSize::Square => GenerateGptImage1TextToImageImageSize::Square, + TextToImageSize::Tall => GenerateGptImage1TextToImageImageSize::Vertical, + TextToImageSize::Wide => GenerateGptImage1TextToImageImageSize::Horizontal, + }); + + let num_images = match request.number_images { + None => None, + Some(1) => Some(GenerateGptImage1TextToImageNumImages::One), + Some(2) => Some(GenerateGptImage1TextToImageNumImages::Two), + Some(3) => Some(GenerateGptImage1TextToImageNumImages::Three), + Some(4) => Some(GenerateGptImage1TextToImageNumImages::Four), + // TODO: Error + //Some(other) => { + // return Err(InternalImageError::InvalidNumberOfRequestedImages { + // min: 1, + // max: 4, + // requested: other, + // }); + //}, + _ => Some(GenerateGptImage1TextToImageNumImages::One), // Default to one image if invalid number + }; + + let request = GenerateGptImage1TextToImageRequest { + uuid_idempotency_token, + prompt: request.prompt.clone(), + image_size, + num_images, + image_quality, + }; + + let result = generate_gpt_image_1_text_to_image( + &app_env_configs.storyteller_host, + Some(&creds), + request, + ).await; + + let job_id = match result { + Ok(enqueued) => { + // TODO(bt,2025-07-05): Enqueue job token? + info!("Successfully enqueued Artcraft gpt-image-1. Job token: {}", + enqueued.inference_job_token); + enqueued.inference_job_token + } + Err(err) => { + error!("Failed to use Artcraft gpt-image-1: {:?}", err); + return Err(GenerateError::from(err)); + } + }; + + Ok(TaskEnqueueSuccess { + provider: GenerationProvider::Artcraft, + model: Some(GenerationModel::GptImage1), + provider_job_id: Some(job_id.to_string()), + task_type: TaskType::ImageGeneration, + }) +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft.rs new file mode 100644 index 0000000000..f23c44292e --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft.rs @@ -0,0 +1,46 @@ +use crate::core::commands::enqueue::generate_error::GenerateError; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::events::generation_events::common::GenerationModel; +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 artcraft_router::api::common_image_model::CommonImageModel; +use enums::common::generation_provider::GenerationProvider; +use tauri::AppHandle; + +pub async fn handle_text_to_image_artcraft( + model: TextToImageModel, + request: &EnqueueTextToImageRequest, + app: &AppHandle, + app_data_root: &AppDataRoot, + app_env_configs: &AppEnvConfigs, + storyteller_creds_manager: &StorytellerCredentialManager, +) -> Result { + + match model { + TextToImageModel::Midjourney | TextToImageModel::GrokImage => { + Err(GenerateError::BadProviderForModel { + provider: GenerationProvider::Artcraft, + model: text_to_image_model_to_model_type(model), + }) + } + TextToImageModel::Recraft3 => { + Err(GenerateError::NotYetImplemented(format!("not yet implemented in Artcraft"))) + } + TextToImageModel::Flux1Dev => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::Flux1Dev, GenerationModel::Flux1Dev).await, + TextToImageModel::Flux1Schnell => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::Flux1Schnell, GenerationModel::Flux1Schnell).await, + TextToImageModel::FluxPro11 => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::FluxPro11, GenerationModel::FluxPro11).await, + TextToImageModel::FluxPro11Ultra => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::FluxPro11Ultra, GenerationModel::FluxPro11Ultra).await, + TextToImageModel::GptImage1 => handle_artcraft_gpt_image_1_text_to_image(request, app_env_configs, storyteller_creds_manager).await, + TextToImageModel::GptImage1p5 => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::GptImage1p5, GenerationModel::GptImage1p5).await, + TextToImageModel::GptImage2 => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::GptImage2, GenerationModel::GptImage2).await, + TextToImageModel::Gemini25Flash | TextToImageModel::NanoBanana => { + handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::NanoBanana, GenerationModel::NanoBanana).await + }, + TextToImageModel::NanoBanana2 => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::NanoBanana2, GenerationModel::NanoBanana2).await, + TextToImageModel::NanoBananaPro => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::NanoBananaPro, GenerationModel::NanoBananaPro).await, + TextToImageModel::Seedream4 => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::Seedream4, GenerationModel::Seedream4).await, + TextToImageModel::Seedream4p5 => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::Seedream4p5, GenerationModel::Seedream4p5).await, + TextToImageModel::Seedream5Lite => handle_text_to_image_artcraft_via_router(request, app_env_configs, storyteller_creds_manager, CommonImageModel::Seedream5Lite, GenerationModel::Seedream5Lite).await, + } +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft_via_router.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft_via_router.rs new file mode 100644 index 0000000000..9094a42422 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/handle_text_to_image_artcraft_via_router.rs @@ -0,0 +1,159 @@ +use crate::core::api_adapters::aspect_ratio::common_aspect_ratio::CommonAspectRatio as CommonAspectRatio2; +use crate::core::api_adapters::resolution::common_resolution::CommonResolution as CommonResolution2; +use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason}; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{ + EnqueueTextToImageRequest, TextToImageResolution, TextToImageSize, +}; +use crate::core::events::generation_events::common::GenerationModel; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use artcraft_router::api::common_aspect_ratio::CommonAspectRatio as RouterCommonAspectRatio; +use artcraft_router::api::common_image_model::CommonImageModel; +use artcraft_router::api::common_resolution::CommonResolution as RouterCommonResolution; +use artcraft_router::api::common_quality::CommonQuality as RouterCommonQuality; +use artcraft_router::api::image_list_ref::ImageListRef; +use artcraft_router::api::provider::Provider; +use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy; +use artcraft_router::client::router_artcraft_client::RouterArtcraftClient; +use artcraft_router::client::router_client::RouterClient; +use artcraft_router::generate::generate_image::generate_image_request::GenerateImageRequest; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use log::{error, info}; + +pub(super) async fn handle_text_to_image_artcraft_via_router( + request: &EnqueueTextToImageRequest, + app_env_configs: &AppEnvConfigs, + storyteller_creds_manager: &StorytellerCredentialManager, + model: CommonImageModel, + generation_model: GenerationModel, +) -> Result { + let creds = match storyteller_creds_manager.get_credentials()? { + Some(creds) => creds, + None => return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsStorytellerCredentials)), + }; + + let client = RouterClient::Artcraft(RouterArtcraftClient::new( + app_env_configs.storyteller_host.clone(), + creds, + )); + + let image_inputs = request.image_media_tokens.clone().map(ImageListRef::MediaFileTokens); + + let aspect_ratio = get_aspect_ratio_t2i(request); + let resolution = get_resolution_t2i(request); + let quality = get_quality_t2i(request); + + let router_request = GenerateImageRequest { + model, + provider: Provider::Artcraft, + prompt: request.prompt.clone(), + image_inputs, + resolution, + aspect_ratio, + quality, + image_batch_count: request.number_images.map(|n| n as u16), + request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade, + generation_mode_mismatch_strategy: None, + idempotency_token: None, + horizontal_angle: None, + vertical_angle: None, + zoom: None, + }; + + let plan = router_request.build()?; + + info!("Image Generation Plan: {:?}", plan); + + let response = match plan.generate_image(&client).await { + Ok(resp) => { + info!("Successfully enqueued."); + resp + } + Err(err) => { + error!("Failed to enqueue: {:?}", err); + return Err(GenerateError::from(err)); + } + }; + + let job_id = response + .get_artcraft_payload() + .map(|p| p.inference_job_token.to_string()) + .ok_or(GenerateError::ResponseHadNoJobTokens)?; + + Ok(TaskEnqueueSuccess { + task_type: TaskType::ImageGeneration, + model: Some(generation_model), + provider: GenerationProvider::Artcraft, + provider_job_id: Some(job_id), + }) +} + +fn get_aspect_ratio_t2i(request: &EnqueueTextToImageRequest) -> Option { + if let Some(ar) = request.common_aspect_ratio { + return Some(convert_desktop_aspect_ratio(ar)); + } + if let Some(ar) = request.aspect_ratio { + return Some(match ar { + TextToImageSize::Auto => RouterCommonAspectRatio::Auto, + TextToImageSize::Square => RouterCommonAspectRatio::Square, + TextToImageSize::Wide => RouterCommonAspectRatio::Wide, + TextToImageSize::Tall => RouterCommonAspectRatio::Tall, + }); + } + None +} + +fn get_resolution_t2i(request: &EnqueueTextToImageRequest) -> Option { + if let Some(res) = request.common_resolution { + return Some(convert_desktop_resolution(res)); + } + if let Some(res) = request.image_resolution { + return Some(match res { + TextToImageResolution::OneK => RouterCommonResolution::OneK, + TextToImageResolution::TwoK => RouterCommonResolution::TwoK, + TextToImageResolution::FourK => RouterCommonResolution::FourK, + }); + } + None +} + +fn get_quality_t2i(request: &EnqueueTextToImageRequest) -> Option { + request.quality.map(|quality| quality.to_artcraft_router_type()) +} + +fn convert_desktop_aspect_ratio(ar: CommonAspectRatio2) -> RouterCommonAspectRatio { + match ar { + CommonAspectRatio2::Auto => RouterCommonAspectRatio::Auto, + CommonAspectRatio2::Square => RouterCommonAspectRatio::Square, + CommonAspectRatio2::WideThreeByTwo => RouterCommonAspectRatio::WideThreeByTwo, + CommonAspectRatio2::WideFourByThree => RouterCommonAspectRatio::WideFourByThree, + CommonAspectRatio2::WideFiveByFour => RouterCommonAspectRatio::WideFiveByFour, + CommonAspectRatio2::WideSixteenByNine => RouterCommonAspectRatio::WideSixteenByNine, + CommonAspectRatio2::WideTwentyOneByNine => RouterCommonAspectRatio::WideTwentyOneByNine, + CommonAspectRatio2::TallTwoByThree => RouterCommonAspectRatio::TallTwoByThree, + CommonAspectRatio2::TallThreeByFour => RouterCommonAspectRatio::TallThreeByFour, + CommonAspectRatio2::TallFourByFive => RouterCommonAspectRatio::TallFourByFive, + CommonAspectRatio2::TallNineBySixteen => RouterCommonAspectRatio::TallNineBySixteen, + CommonAspectRatio2::TallNineByTwentyOne => RouterCommonAspectRatio::TallNineByTwentyOne, + CommonAspectRatio2::Wide => RouterCommonAspectRatio::Wide, + CommonAspectRatio2::Tall => RouterCommonAspectRatio::Tall, + CommonAspectRatio2::Auto2k => RouterCommonAspectRatio::Auto2k, + CommonAspectRatio2::Auto4k => RouterCommonAspectRatio::Auto4k, + CommonAspectRatio2::SquareHd => RouterCommonAspectRatio::SquareHd, + } +} + +fn convert_desktop_resolution(res: CommonResolution2) -> RouterCommonResolution { + match res { + CommonResolution2::OneK => RouterCommonResolution::OneK, + CommonResolution2::TwoK => RouterCommonResolution::TwoK, + CommonResolution2::ThreeK => RouterCommonResolution::ThreeK, + CommonResolution2::FourK => RouterCommonResolution::FourK, + CommonResolution2::HalfK => RouterCommonResolution::HalfK, + CommonResolution2::FourEightyP => RouterCommonResolution::FourEightyP, + CommonResolution2::SevenTwentyP => RouterCommonResolution::SevenTwentyP, + CommonResolution2::TenEightyP => RouterCommonResolution::TenEightyP, + } +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/mod.rs new file mode 100644 index 0000000000..572b124965 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/artcraft/mod.rs @@ -0,0 +1,4 @@ +pub (super) mod handle_text_to_image_artcraft; + +mod handle_text_to_image_artcraft_via_router; +mod handle_artcraft_gpt_image_1_text_to_image; diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/generate_image_command.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/generate_image_command.rs index 6874764b4c..52a7c2e308 100644 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/generate_image_command.rs +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/generate_image_command.rs @@ -7,10 +7,10 @@ use crate::core::commands::enqueue::image_inpaint::enqueue_image_inpaint_command use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{ enqueue_text_to_image_command, EnqueueTextToImageRequest, TextToImageModel, }; -use crate::core::commands::generate::generate_image::request::{ +use crate::core::commands::generate::generate_image::tauri_generate_image_request::{ TauriGenerateImageErrorType, TauriGenerateImageRequest, TauriGenerateImageResponse, - TauriImageModel, }; +use crate::core::commands::generate::generate_image::tauri_image_model::TauriImageModel; use crate::core::commands::response::failure_response_wrapper::{CommandErrorResponseWrapper, CommandErrorStatus}; use crate::core::commands::response::shorthand::Response; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/handle_grok.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/handle_grok.rs new file mode 100644 index 0000000000..8318f5f6e6 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/handle_grok.rs @@ -0,0 +1,92 @@ +use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_grok_image::aspect_ratio_to_grok_image; +use crate::core::commands::enqueue::generate_error::GenerateError; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; +use crate::core::events::generation_events::common::GenerationModel; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::services::grok::state::grok_credential_manager::GrokCredentialManager; +use crate::services::grok::state::grok_image_prompt_queue::{GrokImagePromptQueue, PromptItem}; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use grok_client::requests::image_websocket::messages::websocket_client_message::ClientMessageAspectRatio; +use uuid_utils::uuid::generate_random_uuid; +use tauri::AppHandle; + +pub async fn handle_grok( + app: &AppHandle, + request: &EnqueueTextToImageRequest, + app_env_configs: &AppEnvConfigs, + creds_manager: &GrokCredentialManager, + grok_image_prompt_queue: &GrokImagePromptQueue, +) -> Result { + + + //// TODO: We can request population of the user info if absent or expired. + //let websocket = get_websocket( + // app, + // creds_manager, + // websocket_manager, + //).await?; + // + //let _result = prompt_websocket_image(PromptWebsocketImageArgs { + // websocket_wrapped: &websocket, + // prompt: request.prompt.as_deref().unwrap_or_else(||""), + //}).await?; + // + //let request_id = listen_for_websocket_request_id(ListenForWebsocketRequestIdArgs { + // websocket: &websocket, + // timeout: Duration::from_millis(10_000), + //}).await?; + // + //let request_id = match request_id.request_id { + // Some(request_id) => request_id.to_string(), + // None => { + // warn!("No request id from Grok image generation (websocket)"); + // return Err(GenerateError::ProviderFailure(ProviderFailureReason::GrokJobEnqueueFailed)); + // } + //}; + // + //info!("Successfully enqueued MidJourney. Job token: {}", request_id); + + let job_id = generate_random_uuid(); + + let prompt = request.prompt + .as_deref() + .map(|prompt| prompt.trim().to_string()) + .unwrap_or_else(|| "".to_string()); + + let aspect_ratio = get_aspect_ratio(request); + + grok_image_prompt_queue.enqueue(PromptItem { + task_id: job_id.clone(), + prompt, + aspect_ratio: aspect_ratio.unwrap_or(ClientMessageAspectRatio::Square), + })?; + + Ok(TaskEnqueueSuccess { + provider: GenerationProvider::Grok, + model: Some(GenerationModel::GrokImage), + provider_job_id: Some(job_id), + task_type: TaskType::ImageGeneration, + }) +} + +fn get_aspect_ratio(request: &EnqueueTextToImageRequest) -> Option { + if let Some(common_aspect_ratio) = request.common_aspect_ratio { + // Handle modern aspect ratio + let aspect = aspect_ratio_to_grok_image(common_aspect_ratio); + return Some(aspect); + } + + if let Some(aspect_ratio) = request.aspect_ratio { + // Handle deprecated aspect ratio + return match aspect_ratio { + TextToImageSize::Auto => Some(ClientMessageAspectRatio::Square), + TextToImageSize::Square => Some(ClientMessageAspectRatio::Square), + TextToImageSize::Wide => Some(ClientMessageAspectRatio::WideThreeByTwo), + TextToImageSize::Tall => Some(ClientMessageAspectRatio::TallTwoByThree), + } + } + + None +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/mod.rs new file mode 100644 index 0000000000..181e756f1a --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/grok/mod.rs @@ -0,0 +1 @@ +pub (super) mod handle_grok; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/handle_midjourney.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/handle_midjourney.rs new file mode 100644 index 0000000000..848ebe8105 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/handle_midjourney.rs @@ -0,0 +1,140 @@ +use crate::core::commands::enqueue::generate_error::{GenerateError, MissingCredentialsReason, ProviderFailureReason}; +use crate::core::commands::enqueue::image_edit::enqueue_edit_image_command::{EditImageQuality, EditImageSize}; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::functional_events::canvas_background_removal_complete_event::CanvasBackgroundRemovalCompleteEvent; +use crate::core::events::functional_events::show_provider_login_modal_event::ShowProviderLoginModalEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::core::events::warning_events::flash_user_input_error_event::FlashUserInputErrorEvent; +use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; +use crate::core::state::data_dir::app_data_root::AppDataRoot; +use crate::core::state::provider_priority::ProviderPriorityStore; +use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use uuid_utils::uuid::generate_random_uuid; +use log::{error, info}; +use midjourney_client::client::midjourney_hostname::MidjourneyHostname; +use midjourney_client::endpoints::submit_job::{submit_job, SubmitJobRequest}; +use midjourney_client::error::midjourney_api_error::MidjourneyApiError; +use midjourney_client::recipes::channel_id::ChannelId; +use midjourney_client::recipes::text_to_image::{text_to_image, TextToImageError, TextToImageRequest}; +use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session; +use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest}; +use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages}; +use std::time::Duration; +use tauri::AppHandle; +use tokens::tokens::media_files::MediaFileToken; + +pub async fn handle_midjourney( + app: &AppHandle, + request: &EnqueueTextToImageRequest, + app_env_configs: &AppEnvConfigs, + mj_creds_manager: &MidjourneyCredentialManager, +) -> Result { + + let creds = match mj_creds_manager.maybe_copy_cookie_store() { + Ok(Some(creds)) => creds, + Ok(None) => { + error!("Midjourney credentials not found."); + ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Midjourney, &app); + return Err(GenerateError::needs_midjourney_credentials()); + } + Err(err) => { + error!("Error reading Midjourney credentials: {:?}", err); + ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Midjourney, &app); + return Err(GenerateError::needs_midjourney_credentials()); + }, + }; + + // TODO: We can request population of the user info if absent or expired. + + let user_info = match mj_creds_manager.maybe_copy_user_info() { + Ok(Some(user_info)) => user_info, + Ok(None) => { + return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsMidjourneyUserInfo)); + } + Err(err) => { + error!("Error reading Midjourney user info: {:?}", err); + return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsMidjourneyUserInfo)); + }, + }; + + let channel_id = match user_info.user_id { + Some(user_id) => ChannelId::UserId(user_id), + None => { + error!("Midjourney user info does not contain a user ID."); + return Err(GenerateError::MissingCredentials(MissingCredentialsReason::NeedsMidjourneyUserId)); + } + }; + + info!("Calling midjourney ..."); + + let cookie_header = creds.to_cookie_string(); + + let prompt = request.prompt + .as_deref() + .unwrap_or(""); + + let result = text_to_image(TextToImageRequest { + prompt, + channel_id: &channel_id, + hostname: MidjourneyHostname::Standard, + cookie_header, + }).await; + + let result = match result { + Ok(result) => result, + Err(err) => { + error!("Failed to use MidJourney: {:?}", err); + return Err(GenerateError::from(err)); + } + }; + + let job_id = match result.maybe_job_id { + Some(job_id) => job_id, + None => { + error!("Failed to enqueue MidJourney: No job ID returned."); + return handle_midjourney_errors(app, result.maybe_errors); + } + }; + + info!("Successfully enqueued MidJourney. Job token: {}", job_id); + + Ok(TaskEnqueueSuccess { + provider: GenerationProvider::Midjourney, + model: Some(GenerationModel::Midjourney), + provider_job_id: Some(job_id), + task_type: TaskType::ImageGeneration, + }) +} + +fn handle_midjourney_errors( + app: &AppHandle, + maybe_errors: Option> +) -> Result { + if let Some(errors) = maybe_errors { + if !errors.is_empty() { + let messages: Vec = errors.iter() + .map(|e| format!("{:?}", e)) + .collect(); + + let combined_message = messages.join("; "); + + let event = FlashUserInputErrorEvent { + message: format!("Midjourney Error: {}", combined_message), + }; + + if let Err(err) = event.send(&app) { + error!("Failed to send FlashUserInputErrorEvent: {:?}", err); // Fail open + } + } + } + + Err(GenerateError::ProviderFailure(ProviderFailureReason::MidjourneyJobEnqueueFailed)) +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/mod.rs new file mode 100644 index 0000000000..18048875b6 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/midjourney/mod.rs @@ -0,0 +1 @@ +pub (super) mod handle_midjourney; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs index c4ab767b7f..ccf608c644 100644 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/mod.rs @@ -1,2 +1,3 @@ pub mod generate_image_command; -pub mod request; +pub mod tauri_generate_image_request; +pub mod tauri_image_model; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_gpt_image_1_sora_text_to_image.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_gpt_image_1_sora_text_to_image.rs new file mode 100644 index 0000000000..82a1abedbf --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_gpt_image_1_sora_text_to_image.rs @@ -0,0 +1,135 @@ +use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_grok_image::aspect_ratio_to_grok_image; +use crate::core::api_adapters::aspect_ratio::convert::aspect_ratio_to_sora_native_gpt_image_1::aspect_ratio_to_sora_native_gpt_image_1; +use crate::core::commands::enqueue::generate_error::GenerateError; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::{EnqueueTextToImageRequest, TextToImageSize}; +use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; +use crate::core::events::functional_events::show_provider_login_modal_event::ShowProviderLoginModalEvent; +use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider}; +use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use enums::common::generation_provider::GenerationProvider; +use enums::tauri::tasks::task_type::TaskType; +use errors::AnyhowResult; +use grok_client::requests::image_websocket::messages::websocket_client_message::ClientMessageAspectRatio; +use log::{error, info}; +use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session; +use openai_sora_client::recipes::simple_image_gen_with_session_auto_renew::{simple_image_gen_with_session_auto_renew, SimpleImageGenAutoRenewRequest}; +use openai_sora_client::requests::image_gen::common::{ImageSize, NumImages}; +use std::time::Duration; +use tauri::AppHandle; + +const SORA_SIMPLE_IMAGE_GEN_TIMEOUT : Duration = Duration::from_millis(1000 * 30); // 30 seconds + +pub async fn handle_gpt_image_1_sora_text_to_image( + request: &EnqueueTextToImageRequest, + app: &AppHandle, + sora_creds_manager: &SoraCredentialManager, + sora_task_queue: &SoraTaskQueue, +) -> Result { + + let mut creds = match sora_creds_manager.get_credentials() { + Ok(Some(creds)) => creds, + Ok(None) => { + error!("Sora credentials not found."); + ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Sora, &app); + return Err(GenerateError::needs_sora_credentials()); + } + Err(err) => { + error!("Error reading Sora credentials: {:?}", err); + ShowProviderLoginModalEvent::send_for_provider(GenerationProvider::Sora, &app); + return Err(GenerateError::needs_sora_credentials()); + } + }; + + let credential_updated = maybe_upgrade_or_renew_session(&mut creds) + .await + .map_err(|err| { + error!("Failed to upgrade or renew session: {:?}", err); + err + })?; + + if credential_updated { + info!("Storing updated credentials"); + sora_creds_manager.set_credentials(&creds)?; + } + + let prompt = request.prompt.as_deref().unwrap_or(""); + + let aspect_ratio = get_aspect_ratio(request) + .unwrap_or(ImageSize::Square); + + let num_images = match request.number_images { + None => NumImages::One, + Some(1) => NumImages::One, + Some(2) => NumImages::Two, + Some(3) => NumImages::Two, // NB: There is no option for three images! + Some(4) => NumImages::Four, + _ => NumImages::One, + }; + + let result = + simple_image_gen_with_session_auto_renew(SimpleImageGenAutoRenewRequest { + prompt: prompt.to_string(), + num_images, + image_size: aspect_ratio, + credentials: &creds, + request_timeout: Some(SORA_SIMPLE_IMAGE_GEN_TIMEOUT), + }).await; + + let (response, maybe_new_creds) = + match result { + Ok((response, maybe_new_creds)) => (response, maybe_new_creds), + Err(err) => { + let event = GenerationEnqueueFailureEvent { + action: GenerationAction::GenerateImage, + service: GenerationServiceProvider::Sora, + model: None, + reason: None, + }; + + if let Err(err) = event.send(app) { + error!("Failed to emit event: {:?}", err); // Fail open. + } + + return Err(GenerateError::from(err)); + } + }; + + if let Some(new_creds) = maybe_new_creds { + info!("Storing updated credentials."); + sora_creds_manager.set_credentials(&new_creds)?; + } + + info!("New Sora Task ID: {:?} ", response.task_id); + + sora_task_queue.insert(&response.task_id)?; + + Ok(TaskEnqueueSuccess { + provider: GenerationProvider::Sora, + task_type: TaskType::ImageGeneration, + model: Some(GenerationModel::GptImage1), + provider_job_id: Some(response.task_id.to_string()), + }) +} + +fn get_aspect_ratio(request: &EnqueueTextToImageRequest) -> Option { + if let Some(common_aspect_ratio) = request.common_aspect_ratio { + // Handle modern aspect ratio + let aspect = aspect_ratio_to_sora_native_gpt_image_1(common_aspect_ratio); + return Some(aspect); + } + + if let Some(aspect_ratio) = request.aspect_ratio { + // Handle deprecated aspect ratio + return match aspect_ratio { + TextToImageSize::Auto => Some(ImageSize::Square), + TextToImageSize::Square => Some(ImageSize::Square), + TextToImageSize::Wide => Some(ImageSize::Wide), + TextToImageSize::Tall => Some(ImageSize::Tall), + } + } + + None +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_text_to_image_sora.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_text_to_image_sora.rs new file mode 100644 index 0000000000..a632ed8eca --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/handle_text_to_image_sora.rs @@ -0,0 +1,21 @@ +use crate::core::commands::enqueue::generate_error::GenerateError; +use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; +use crate::core::commands::enqueue::text_to_image::enqueue_text_to_image_command::EnqueueTextToImageRequest; +use crate::core::commands::generate::generate_image::sora::handle_gpt_image_1_sora_text_to_image::handle_gpt_image_1_sora_text_to_image; +use crate::services::sora::state::sora_credential_manager::SoraCredentialManager; +use crate::services::sora::state::sora_task_queue::SoraTaskQueue; +use tauri::AppHandle; + +pub async fn handle_text_to_image_sora( + request: &EnqueueTextToImageRequest, + app: &AppHandle, + sora_creds_manager: &SoraCredentialManager, + sora_task_queue: &SoraTaskQueue, +) -> Result { + handle_gpt_image_1_sora_text_to_image( + request, + app, + sora_creds_manager, + sora_task_queue, + ).await +} diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/mod.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/mod.rs new file mode 100644 index 0000000000..9a9529d300 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/sora/mod.rs @@ -0,0 +1,2 @@ +pub (super) mod handle_text_to_image_sora; +mod handle_gpt_image_1_sora_text_to_image; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/request.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/tauri_generate_image_request.rs similarity index 55% rename from crates/desktop/artcraft/src/core/commands/generate/generate_image/request.rs rename to crates/desktop/artcraft/src/core/commands/generate/generate_image/tauri_generate_image_request.rs index f30a4cd400..759eb4274e 100644 --- a/crates/desktop/artcraft/src/core/commands/generate/generate_image/request.rs +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/tauri_generate_image_request.rs @@ -1,106 +1,14 @@ use artcraft_router::api::common_aspect_ratio::CommonAspectRatio; use artcraft_router::api::common_resolution::CommonResolution; -use enums::common::generation::common_model_type::CommonModelType; use enums::common::generation::common_quality::CommonQuality; use enums::common::generation_provider::GenerationProvider; use enums::tauri::ux::tauri_command_caller::TauriCommandCaller; use serde_derive::{Deserialize, Serialize}; use tokens::tokens::media_files::MediaFileToken; +use crate::core::commands::generate::generate_image::tauri_image_model::TauriImageModel; use crate::core::commands::response::success_response_wrapper::SerializeMarker; -// ── Model ── - -/// Unified image model enum covering text-to-image, image edit, and inpainting. -/// -/// This is used in the Tauri command bridge. -/// Don't change the serializations without coordinating with the frontend. -#[derive(Deserialize, Debug, Copy, Clone)] -#[serde(rename_all = "snake_case")] -pub enum TauriImageModel { - // Text-to-image models - - #[serde(rename = "flux_1_dev")] - Flux1Dev, - #[serde(rename = "flux_1_schnell")] - Flux1Schnell, - #[serde(rename = "flux_pro_11")] - FluxPro11, - #[serde(rename = "flux_pro_11_ultra")] - FluxPro11Ultra, - #[serde(rename = "grok_image")] - GrokImage, - #[serde(rename = "recraft_3")] - Recraft3, - #[serde(rename = "gpt_image_1")] - GptImage1, - #[serde(rename = "gpt_image_1p5")] - GptImage1p5, - #[serde(rename = "gpt_image_2")] - GptImage2, - #[serde(rename = "gemini_25_flash")] - Gemini25Flash, - #[serde(rename = "nano_banana")] - NanoBanana, - #[serde(rename = "nano_banana_2")] - NanoBanana2, - #[serde(rename = "nano_banana_pro")] - NanoBananaPro, - #[serde(rename = "seedream_4")] - Seedream4, - #[serde(rename = "seedream_4p5")] - Seedream4p5, - #[serde(rename = "seedream_5_lite")] - Seedream5Lite, - #[serde(rename = "midjourney")] - Midjourney, - - // Image edit models - - #[serde(rename = "flux_pro_kontext_max")] - FluxProKontextMax, - #[serde(rename = "qwen_edit_2511_angles")] - QwenEdit2511Angles, - #[serde(rename = "flux_2_lora_angles")] - Flux2LoraAngles, - - // Inpainting models - - #[serde(rename = "flux_dev_juggernaut")] - FluxDevJuggernaut, - #[serde(rename = "flux_pro_1")] - FluxPro1, -} - -impl TauriImageModel { - pub fn to_common_model_type(&self) -> CommonModelType { - match self { - Self::Flux1Dev => CommonModelType::Flux1Dev, - Self::Flux1Schnell => CommonModelType::Flux1Schnell, - Self::FluxPro11 => CommonModelType::FluxPro11, - Self::FluxPro11Ultra => CommonModelType::FluxPro11Ultra, - Self::GrokImage => CommonModelType::GrokImage, - Self::Recraft3 => CommonModelType::Recraft3, - Self::GptImage1 => CommonModelType::GptImage1, - Self::GptImage1p5 => CommonModelType::GptImage1p5, - Self::GptImage2 => CommonModelType::GptImage2, - Self::Gemini25Flash => CommonModelType::NanoBanana, - Self::NanoBanana => CommonModelType::NanoBanana, - Self::NanoBanana2 => CommonModelType::NanoBanana2, - Self::NanoBananaPro => CommonModelType::NanoBananaPro, - Self::Seedream4 => CommonModelType::Seedream4, - Self::Seedream4p5 => CommonModelType::Seedream4p5, - Self::Seedream5Lite => CommonModelType::Seedream5Lite, - Self::Midjourney => CommonModelType::Midjourney, - Self::FluxProKontextMax => CommonModelType::FluxProKontextMax, - Self::QwenEdit2511Angles => CommonModelType::QwenEdit2511Angles, - Self::Flux2LoraAngles => CommonModelType::Flux2LoraAngles, - Self::FluxDevJuggernaut => CommonModelType::FluxDevJuggernaut, - Self::FluxPro1 => CommonModelType::FluxPro1, - } - } -} - // ── Request ── #[derive(Deserialize, Debug)] diff --git a/crates/desktop/artcraft/src/core/commands/generate/generate_image/tauri_image_model.rs b/crates/desktop/artcraft/src/core/commands/generate/generate_image/tauri_image_model.rs new file mode 100644 index 0000000000..c8fb4dd99b --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/generate/generate_image/tauri_image_model.rs @@ -0,0 +1,92 @@ +use enums::common::generation::common_model_type::CommonModelType; +use serde_derive::Deserialize; + +/// Unified image model enum covering text-to-image, image edit, and inpainting. +/// +/// This is used in the Tauri command bridge. +/// Don't change the serializations without coordinating with the frontend. +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +pub enum TauriImageModel { + // Text-to-image models + + #[serde(rename = "flux_1_dev")] + Flux1Dev, + #[serde(rename = "flux_1_schnell")] + Flux1Schnell, + #[serde(rename = "flux_pro_11")] + FluxPro11, + #[serde(rename = "flux_pro_11_ultra")] + FluxPro11Ultra, + #[serde(rename = "grok_image")] + GrokImage, + #[serde(rename = "recraft_3")] + Recraft3, + #[serde(rename = "gpt_image_1")] + GptImage1, + #[serde(rename = "gpt_image_1p5")] + GptImage1p5, + #[serde(rename = "gpt_image_2")] + GptImage2, + #[serde(rename = "gemini_25_flash")] + Gemini25Flash, + #[serde(rename = "nano_banana")] + NanoBanana, + #[serde(rename = "nano_banana_2")] + NanoBanana2, + #[serde(rename = "nano_banana_pro")] + NanoBananaPro, + #[serde(rename = "seedream_4")] + Seedream4, + #[serde(rename = "seedream_4p5")] + Seedream4p5, + #[serde(rename = "seedream_5_lite")] + Seedream5Lite, + #[serde(rename = "midjourney")] + Midjourney, + + // Image edit models + + #[serde(rename = "flux_pro_kontext_max")] + FluxProKontextMax, + #[serde(rename = "qwen_edit_2511_angles")] + QwenEdit2511Angles, + #[serde(rename = "flux_2_lora_angles")] + Flux2LoraAngles, + + // Inpainting models + + #[serde(rename = "flux_dev_juggernaut")] + FluxDevJuggernaut, + #[serde(rename = "flux_pro_1")] + FluxPro1, +} + +impl TauriImageModel { + pub fn to_common_model_type(&self) -> CommonModelType { + match self { + Self::Flux1Dev => CommonModelType::Flux1Dev, + Self::Flux1Schnell => CommonModelType::Flux1Schnell, + Self::FluxPro11 => CommonModelType::FluxPro11, + Self::FluxPro11Ultra => CommonModelType::FluxPro11Ultra, + Self::GrokImage => CommonModelType::GrokImage, + Self::Recraft3 => CommonModelType::Recraft3, + Self::GptImage1 => CommonModelType::GptImage1, + Self::GptImage1p5 => CommonModelType::GptImage1p5, + Self::GptImage2 => CommonModelType::GptImage2, + Self::Gemini25Flash => CommonModelType::NanoBanana, + Self::NanoBanana => CommonModelType::NanoBanana, + Self::NanoBanana2 => CommonModelType::NanoBanana2, + Self::NanoBananaPro => CommonModelType::NanoBananaPro, + Self::Seedream4 => CommonModelType::Seedream4, + Self::Seedream4p5 => CommonModelType::Seedream4p5, + Self::Seedream5Lite => CommonModelType::Seedream5Lite, + Self::Midjourney => CommonModelType::Midjourney, + Self::FluxProKontextMax => CommonModelType::FluxProKontextMax, + Self::QwenEdit2511Angles => CommonModelType::QwenEdit2511Angles, + Self::Flux2LoraAngles => CommonModelType::Flux2LoraAngles, + Self::FluxDevJuggernaut => CommonModelType::FluxDevJuggernaut, + Self::FluxPro1 => CommonModelType::FluxPro1, + } + } +} diff --git a/frontend/libs/tauri-api/src/index.ts b/frontend/libs/tauri-api/src/index.ts index 73fbaae175..0cefd775f6 100644 --- a/frontend/libs/tauri-api/src/index.ts +++ b/frontend/libs/tauri-api/src/index.ts @@ -13,6 +13,7 @@ export * from "./lib/enqueue/EnqueueImageTo3dObject.js"; export * from "./lib/enqueue/EnqueueImageTo3dWorld.js"; export * from "./lib/enqueue/EnqueueImageToGaussian.js"; export * from "./lib/enqueue/EnqueueImageToVideo.js"; +export * from "./lib/enqueue/GenerateVideoCommand.js"; export * from "./lib/enqueue/EnqueueTextToImage.js"; export * from "./lib/generate/GenerateImage.js"; export * from "./lib/generate/GenerateVideo.js"; diff --git a/frontend/libs/tauri-api/src/lib/enqueue/GenerateVideoCommand.ts b/frontend/libs/tauri-api/src/lib/enqueue/GenerateVideoCommand.ts new file mode 100644 index 0000000000..f07aa9c6d1 --- /dev/null +++ b/frontend/libs/tauri-api/src/lib/enqueue/GenerateVideoCommand.ts @@ -0,0 +1,192 @@ +import { invoke } from "@tauri-apps/api/core"; +import { CommandResult } from "../common/CommandStatus"; +import { Model } from "@storyteller/model-list"; +import { GenerationProvider } from "@storyteller/api-enums"; + +export enum GenerateVideoErrorType { + /// Caller didn't specify a model + ModelNotSpecified = "model_not_specified", + /// Generic server error + ServerError = "server_error", + /// No Fal API key available + NeedsFalApiKey = "needs_fal_api_key", + /// Fal had an API error + FalError = "fal_error", +} + +export interface GenerateVideoRequest { + // Required. The model to use. + model?: Model; + + // The provider to use. + provider?: GenerationProvider; + + // Optional. Starting frame. + start_frame_image_media_token?: string; + + // Optional. Starting frame (legacy field name). + image_media_token?: string; + + // Optional. Ending frame. + end_frame_image_media_token?: string; + + // Optional. Text prompt to direct the video. + prompt?: string; + + // Optional frontend state to return later. + frontend_caller?: string; + + // Optional frontend state to return later. + frontend_subscriber_id?: string; + + // Optional. Orientation of the video for Sora 2. + sora_orientation?: "portrait" | "landscape"; + + // Optional. Aspect Ratio for the video for Grok Video. + grok_aspect_ratio?: "portrait" | "landscape" | "square"; + + // Optional. Whether to generate audio alongside the video (used by some models like Veo2) + generate_audio?: boolean; + + // Optional. Common aspect ratio for other models. + // Not all are supported by all models, but Artcraft will compensate. + aspect_ratio?: string; // TODO: Typesafety. + + // Optional. Duration in seconds. + duration_seconds?: number; + + // Optional. Reference image media tokens (for reference mode). + reference_image_media_tokens?: string[]; + + // Optional. Reference video media tokens (for video-to-video reference). + reference_video_media_tokens?: string[]; + + // Optional. Reference audio media tokens (for audio reference). + reference_audio_media_tokens?: string[]; + + // Optional. Character tokens for character consistency (from @-mentions). + reference_character_tokens?: string[]; +} + +interface RawGenerateVideoRequest { + model?: string; + provider?: GenerationProvider; + start_frame_image_media_token?: string; + image_media_token?: string; + end_frame_image_media_token?: string; + prompt?: string; + frontend_caller?: string; + frontend_subscriber_id?: string; + sora_orientation?: "portrait" | "landscape"; + grok_aspect_ratio?: "portrait" | "landscape" | "square"; + generate_audio?: boolean; + aspect_ratio?: string; + duration_seconds?: number; + reference_image_media_tokens?: string[]; + reference_video_media_tokens?: string[]; + reference_audio_media_tokens?: string[]; + reference_character_tokens?: string[]; +} + +export interface GenerateVideoError extends CommandResult { + error_type: GenerateVideoErrorType; + error_message?: string; +} + +export type GenerateVideoPayload = Record; + +export interface GenerateVideoSuccess extends CommandResult { + payload: GenerateVideoPayload; +} + +export type GenerateVideoResult = + | GenerateVideoSuccess + | GenerateVideoError; + +export const GenerateVideoCommand = async ( + request: GenerateVideoRequest, +): Promise => { + const mutableRequest: RawGenerateVideoRequest = { + model: request.model?.tauriId, + start_frame_image_media_token: request.start_frame_image_media_token, + image_media_token: request.image_media_token, + }; + + if (request.provider) { + mutableRequest.provider = request.provider; + } + + if (request.prompt) { + mutableRequest.prompt = request.prompt; + } + + if (request.end_frame_image_media_token) { + mutableRequest.end_frame_image_media_token = + request.end_frame_image_media_token; + } + + if (request.frontend_caller) { + mutableRequest.frontend_caller = request.frontend_caller; + } + + if (request.frontend_subscriber_id) { + mutableRequest.frontend_subscriber_id = request.frontend_subscriber_id; + } + + if (request.sora_orientation) { + mutableRequest.sora_orientation = request.sora_orientation; + } + + if (request.grok_aspect_ratio) { + mutableRequest.grok_aspect_ratio = request.grok_aspect_ratio; + } + + if (typeof request.generate_audio === "boolean") { + mutableRequest.generate_audio = request.generate_audio; + } + + if (request.aspect_ratio) { + mutableRequest.aspect_ratio = request.aspect_ratio; + } + + if (typeof request.duration_seconds === "number") { + mutableRequest.duration_seconds = request.duration_seconds; + } + + if ( + request.reference_image_media_tokens && + request.reference_image_media_tokens.length > 0 + ) { + mutableRequest.reference_image_media_tokens = + request.reference_image_media_tokens; + } + + if ( + request.reference_video_media_tokens && + request.reference_video_media_tokens.length > 0 + ) { + mutableRequest.reference_video_media_tokens = + request.reference_video_media_tokens; + } + + if ( + request.reference_audio_media_tokens && + request.reference_audio_media_tokens.length > 0 + ) { + mutableRequest.reference_audio_media_tokens = + request.reference_audio_media_tokens; + } + + if ( + request.reference_character_tokens && + request.reference_character_tokens.length > 0 + ) { + mutableRequest.reference_character_tokens = request.reference_character_tokens; + } + + const result = await invoke("generate_video_command", { + request: mutableRequest, + }); + + return result as GenerateVideoResult; +}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6275dc9c8e..2e5bbc8123 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -349,6 +349,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -364,6 +365,7 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -823,6 +825,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2582,6 +2585,7 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -2655,6 +2659,7 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -2799,6 +2804,7 @@ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", "license": "MIT", + "peer": true, "dependencies": { "@emotion/memoize": "^0.8.1" } @@ -2828,7 +2834,6 @@ "os": [ "aix" ], - "peer": true, "engines": { "node": ">=12" } @@ -2846,7 +2851,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -2864,7 +2868,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -2882,7 +2885,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -2900,7 +2902,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } @@ -2918,7 +2919,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } @@ -2936,7 +2936,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -2954,7 +2953,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -2972,7 +2970,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -2990,7 +2987,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3008,7 +3004,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3026,7 +3021,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3044,7 +3038,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3062,7 +3055,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3080,7 +3072,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3098,7 +3089,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3116,7 +3106,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -3150,7 +3139,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -3184,7 +3172,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -3218,7 +3205,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=12" } @@ -3236,7 +3222,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -3254,7 +3239,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -3272,7 +3256,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -3500,6 +3483,7 @@ "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", "license": "MIT", + "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "6.7.2" }, @@ -3869,7 +3853,6 @@ "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -3914,6 +3897,7 @@ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "license": "MIT", + "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -5414,6 +5398,7 @@ "integrity": "sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@module-federation/runtime": "0.21.6", "@module-federation/webpack-bundler-runtime": "0.21.6" @@ -5607,6 +5592,7 @@ "integrity": "sha512-kzFn3ObUeBp5vaEtN1WMxhTYBuYEErxugu1RzFUERD21X3BZ+b4cWwdFJuBDlsmVjctIg/QSOoZoPXRKAO0foA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@module-federation/runtime": "0.15.0", "@module-federation/webpack-bundler-runtime": "0.15.0" @@ -5721,6 +5707,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -8089,7 +8076,6 @@ "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.85.2.tgz", "integrity": "sha512-kauC/oPaxklU4Y+u9gBfCBJm51qX6WBZq4xx0USCdimtp+G8+554kpygfSWIjoqCJa2o06bWxBEjesiuCv+LzA==", "license": "MIT", - "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -8099,7 +8085,6 @@ "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.2.tgz", "integrity": "sha512-XCginmxh0//++EXVOEJHBVZxHla294FzLCFF6jXwAUjvXVhqyIKyxhABfz+r4OOmaiuWk4Rtd4arqdAzeHeprg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.29.0", @@ -8121,7 +8106,6 @@ "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.85.2.tgz", "integrity": "sha512-3KLgSg1kHvBpr93zMaQhvfYTgnCw7yZRED+3J4dMcYjfSjtD0Wf8SofU6uBmAw9JaVYvP43lpdwUpI4p0+ABsg==", "license": "MIT", - "peer": true, "dependencies": { "@react-native/dev-middleware": "0.85.2", "debug": "^4.4.0", @@ -8152,7 +8136,6 @@ "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.85.2.tgz", "integrity": "sha512-j+0b9H5f5hGTLQxHIhJU/b/W6ijuxJF+ZTLHB0se2kzUBNxFKd7DkIc6753qk3CJdiv55vxG3XDgmlpbHxOpmA==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -8162,7 +8145,6 @@ "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.85.2.tgz", "integrity": "sha512-r5BkhqPMfg3LmaZS5zadHmBNVH5h4bhSpv4BEPGfK4gat9HABAMzUzybi+2wpgU3SoHxnyKGdExEJvoqVcjeRg==", "license": "MIT", - "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "debug": "^4.4.0", @@ -8177,7 +8159,6 @@ "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.85.2.tgz", "integrity": "sha512-3J+NaDUg+QEfDeLAUzgaWhpaxEg78g+KwbydlDCewh2G6WnHpsty8XooruxNHzyAsqVWywZMrzmbn78Ctc1O9Q==", "license": "MIT", - "peer": true, "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.85.2", @@ -8201,7 +8182,6 @@ "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "license": "MIT", - "peer": true, "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -8218,7 +8198,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.3.0" }, @@ -8240,7 +8219,6 @@ "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.85.2.tgz", "integrity": "sha512-YXBOLeAqFrv7XwUeBPTKZeOV1FIxn4AW7UAEitScf3ibC8bu8+6NpJu4HWgbNQHg7vDbbTZVbcOl8EwGxsSq2w==", "license": "MIT", - "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -8250,7 +8228,6 @@ "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.2.tgz", "integrity": "sha512-esGEAmKVM40DV/yVmNljCKZTIeUo7qXqc+Hwffkv3TG+b3E24xyFovHrbP98gGxZr2ZsEyx+2sKLdXF5asY5nw==", "license": "MIT", - "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -8259,8 +8236,7 @@ "version": "0.85.2", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.85.2.tgz", "integrity": "sha512-svuOLtjbFGXDdHsriHXuND5FgHg7XlkOXCbH/8+X4t76YLH6qSTffSIQQrKLDL5mn4EFU+Oh/PNO0/FfpnTOTg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@react-oauth/google": { "version": "0.13.4", @@ -8418,6 +8394,7 @@ "integrity": "sha512-gfc4Hu2Sysr+GyU/F12d2uvEfQwUwWvsOjBtiemhdN1xGOn1+FyYzlLl/aJ7AL9oYko8sDqOPyJCiApWJJGCCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.8", "@babel/generator": "^7.21.5", @@ -9246,6 +9223,7 @@ "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.17.2.tgz", "integrity": "sha512-dTrAG1SgOLgz1DFBDsLHN0V34YqLsHEReVHYOI4UV/p+ALbn/BRQMw1MaUuqGXmX21ZTuCzzPegtTLNEOc8ixA==", "license": "MIT", + "peer": true, "dependencies": { "@remix-run/router": "1.23.0", "@types/cookie": "^0.6.0", @@ -9838,6 +9816,7 @@ "integrity": "sha512-2mR+2YBydlgZ7Q0Rpd6bCC3MBnV9TS0x857K0zIhbDj4BQOqaWVy1n7fx/B3MrS8TR0QCuzKfyDAjNz+XTyJVQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@module-federation/runtime-tools": "0.21.6", "@rspack/binding": "1.6.6", @@ -10552,6 +10531,7 @@ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -10734,6 +10714,7 @@ "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.8" @@ -10948,6 +10929,7 @@ "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3" } @@ -11289,6 +11271,7 @@ "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -11784,6 +11767,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -11806,6 +11790,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.0.tgz", "integrity": "sha512-MY3oPudxvMYyesqs/kW1Bh8y9VqSmf+tzqw3ae8a9DZW68pUe3zAdHeI1jc6iAysuRdACnVknHP8AhwD4/dxtg==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -12145,6 +12130,7 @@ "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -14354,6 +14340,7 @@ "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz", "integrity": "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==", "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -14415,6 +14402,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -14497,6 +14485,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -14570,8 +14559,7 @@ "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ansi-colors": { "version": "4.1.3", @@ -14864,8 +14852,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/assertion-error": { "version": "2.0.1", @@ -15166,7 +15153,6 @@ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz", "integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==", "license": "MIT", - "peer": true, "dependencies": { "hermes-parser": "0.33.3" } @@ -15386,6 +15372,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -15418,7 +15405,6 @@ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "node-int64": "^0.4.0" } @@ -15820,7 +15806,6 @@ "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -15849,7 +15834,6 @@ "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.3.0.tgz", "integrity": "sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -16258,7 +16242,6 @@ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -16274,7 +16257,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -16284,7 +16266,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -16294,7 +16275,6 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -16312,15 +16292,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/connect/node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -16333,7 +16311,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -16669,7 +16646,8 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -17372,7 +17350,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -17428,6 +17407,7 @@ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -17520,7 +17500,6 @@ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", "license": "MIT", - "peer": true, "dependencies": { "stackframe": "^1.3.4" } @@ -17732,6 +17711,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -18156,6 +18136,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -18212,6 +18193,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -18313,6 +18295,7 @@ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -19048,14 +19031,14 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -19229,7 +19212,6 @@ "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", "integrity": "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==", "license": "(MIT OR Apache-2.0)", - "peer": true, "bin": { "dotslash": "bin/dotslash" }, @@ -19242,7 +19224,6 @@ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bser": "2.1.1" } @@ -19356,6 +19337,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -19549,8 +19531,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/follow-redirects": { "version": "1.15.11", @@ -20421,22 +20402,19 @@ "version": "250829098.0.10", "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.10.tgz", "integrity": "sha512-TcRlZ0/TlyfJqquRFAWoyElVNnkdYRi/sEp4/Qy8/GYxjg8j2cS9D4MjuaQ+qimkmLN7AmO+44IznRf06mAr0w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/hermes-estree": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/hermes-parser": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", "license": "MIT", - "peer": true, "dependencies": { "hermes-estree": "0.33.3" } @@ -20458,6 +20436,7 @@ "version": "4.12.7", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -20775,7 +20754,6 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", "license": "MIT", - "peer": true, "dependencies": { "queue": "6.0.2" }, @@ -21948,6 +21926,7 @@ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -21977,7 +21956,6 @@ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -21995,7 +21973,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -22008,7 +21985,6 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -22022,15 +21998,13 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -22046,7 +22020,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -22104,8 +22077,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/jsdom": { "version": "22.1.0", @@ -22113,6 +22085,7 @@ "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -22422,7 +22395,8 @@ "url": "https://github.com/sponsors/lavrton" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/language-subtag-registry": { "version": "0.3.23", @@ -22479,7 +22453,6 @@ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -22503,7 +22476,6 @@ "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" @@ -22514,7 +22486,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -22523,13 +22494,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lightningcss": { "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -23197,8 +23168,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", @@ -23521,7 +23491,6 @@ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "tmpl": "1.0.5" } @@ -23540,8 +23509,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -23789,8 +23757,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/merge-descriptors": { "version": "1.0.3", @@ -23837,7 +23804,6 @@ "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.3.tgz", "integrity": "sha512-1h3lbVrE6hGf1e/764HfhPGg/bGrWMJDDh7G2rc4gFYZboVuI40BlG/y+UhtbhQDNlO/csMvrcnK0YrTlHUVew==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", @@ -23892,7 +23858,6 @@ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.3.tgz", "integrity": "sha512-svAA+yMLpeMiGcz/jKJs4oHpIGEx4nBqNEJ5AGj4CYIg1efvK+A0TjR6tgIuc6tKO5e8JmN/1lglpN2+f3/z/w==", "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", @@ -23908,15 +23873,13 @@ "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/metro-babel-transformer/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", - "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -23926,7 +23889,6 @@ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.3.tgz", "integrity": "sha512-0QElxwLaHqLZf+Xqio8QrjVbuXP/8sJfQBGSPiITlKDVXrVLefuzYVSH9Sj+QL6lrPj2gYZd/iwQh1yZuVKnLA==", "license": "MIT", - "peer": true, "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", @@ -23942,7 +23904,6 @@ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.3.tgz", "integrity": "sha512-TnSL1Fdvrw+2glTdBSRmA5TL8l/i16ECjsrUdf3E5HncA+sNx8KcwDG8r+3ct1UhfYcusJypzZqTN55FZZcwGg==", "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -23955,7 +23916,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 14" } @@ -23965,7 +23925,6 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", - "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -23979,7 +23938,6 @@ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.3.tgz", "integrity": "sha512-JmCzZWOETR+O22q8oPBWyQppx3roU9EbkbGzD8Gf1jukQ4b5T1fTzqqHruu6K4sTiNq5zVQySmKF6bp4kVARew==", "license": "MIT", - "peer": true, "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", @@ -23999,7 +23957,6 @@ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.3.tgz", "integrity": "sha512-cc0pvAa80ai1nDmqqz0P59a+0ZqCZ/YHU/3jEekZL6spFnYDfX8iDLdn9FR6kX+67rmzKxHNrbrSRFLX2AYocw==", "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -24014,7 +23971,6 @@ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.3.tgz", "integrity": "sha512-1cL4m4Jv1yRUt9RJExZQLfccscdlMNOcRG6LHLtmJhf3BG9j3MujPVc7CIpKYdFl+KUl+sdjge6oO3+meKCHQA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", @@ -24035,7 +23991,6 @@ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.3.tgz", "integrity": "sha512-3ofrG2OQyJbO9RNhCfOcl8QU7EE2WrSsnN5dFkuZaJO5+4Imujr9bUXmspeNlXRsOVk0F/rVRbEFH98lFSCkBQ==", "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" @@ -24049,7 +24004,6 @@ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.3.tgz", "integrity": "sha512-pjEzGDtoM8DTHAIPK/9u9ZxszEiuRohYUVImWvgbnB91V4gqYJpQcoEYUugf2NIm1lrX5HNu0OvNqWmPBnGYjA==", "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -24062,7 +24016,6 @@ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.3.tgz", "integrity": "sha512-o7HLRfMyVk9N2dUZ9VjQfB6xxUItL9Pi9WcqxURE7MEKOH6wbGt9/E92YdYLluTOtkzYAEVfdC6h6lcxqA+hMQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" @@ -24076,7 +24029,6 @@ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.3.tgz", "integrity": "sha512-jS48CeSzw78M8y6VE0f9uy3lVmfbOS677j2VCxnlmlYmnahcXuC6IhoN9K6LynNvos9517yUadcfgioju38xYQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", @@ -24097,7 +24049,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -24107,7 +24058,6 @@ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.3.tgz", "integrity": "sha512-J9Tpo8NCycYrozRvBIUyOwGAu4xkawOsAppmTscFiaegK0WvuDGwIM53GbzVSnytCHjVAF0io5GQxpkrKTuc7g==", "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", @@ -24128,7 +24078,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -24138,7 +24087,6 @@ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.3.tgz", "integrity": "sha512-8S3baq2XhBaafHEH5Q8sJW6tmzsEJk80qKc3RU/nZV1MsnYq94RdjTUR6AyKjQd6Rfsk1BtBxhtiNnk7mgslCg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", @@ -24156,7 +24104,6 @@ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.3.tgz", "integrity": "sha512-Wjba7PyYktNRsHbPmkx2J2UX32rAzcDXjCu49zPHeF/viJlYJhwRaNePQcHaCRqQ+kmgQT4ThprsnJfDj71ZMA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", @@ -24181,7 +24128,6 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", - "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -24194,22 +24140,19 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/metro/node_modules/hermes-estree": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/metro/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", - "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -24219,7 +24162,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -24236,7 +24178,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -24246,7 +24187,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -24256,7 +24196,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.3.0" }, @@ -25585,8 +25524,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/node-machine-id": { "version": "1.1.12", @@ -25770,8 +25708,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/nwsapi": { "version": "2.2.22", @@ -25786,6 +25723,7 @@ "integrity": "sha512-2wL/2fSmIbRWn6zXaQ/g3kj5DfEaTw/aJkPr6ozJh8BUq5iYKE+tS9oh0PjsVVwN6Pybe80Lu+mn9RgWyeV3xw==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -25943,7 +25881,6 @@ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.3.tgz", "integrity": "sha512-J7554Ef8bzmKaDY365Afq6PF+qtdnY/d5PKUQFrsKlZHV/N3OGZewVrvDrQDyX5V5NJjTpcAKtlrFZcDr+HvpQ==", "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -26721,6 +26658,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", @@ -27026,6 +26964,7 @@ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -27109,6 +27048,7 @@ "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -27260,7 +27200,6 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", "license": "MIT", - "peer": true, "dependencies": { "asap": "~2.0.6" } @@ -27443,7 +27382,6 @@ "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "~2.0.3" } @@ -27602,6 +27540,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -27650,7 +27589,6 @@ "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", "license": "MIT", - "peer": true, "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -27661,7 +27599,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.3.0" }, @@ -27683,6 +27620,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -28036,7 +27974,6 @@ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.6.0.tgz", "integrity": "sha512-90abYK2q5/qDM+GACs9zRvc5KhEEpEWqWlHSd64zTPNxg+9wCJvTfyD9x2so7hlQhjRYO1Fa6flR3BC/kpTFkA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", @@ -28085,7 +28022,6 @@ "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", "license": "MIT", - "peer": true, "dependencies": { "@types/react-reconciler": "^0.28.9" }, @@ -28108,7 +28044,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -28135,7 +28070,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -28146,7 +28080,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -28156,7 +28089,6 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -28170,8 +28102,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-spring/node_modules/react-konva": { "version": "19.2.3", @@ -28192,7 +28123,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@types/react-reconciler": "^0.33.0", "its-fine": "^2.0.0", @@ -28210,7 +28140,6 @@ "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.33.0.tgz", "integrity": "sha512-HZOXsKT0tGI9LlUw2LuedXsVeB88wFa536vVL0M6vE8zN63nI+sSr1ByxmPToP5K5bukaVscyeCJcF9guVNJ1g==", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "*" } @@ -28220,7 +28149,6 @@ "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", "license": "MIT", - "peer": true, "dependencies": { "@types/react-reconciler": "^0.28.9" }, @@ -28233,7 +28161,6 @@ "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "*" } @@ -28243,7 +28170,6 @@ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz", "integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -28319,7 +28245,6 @@ "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.85.2.tgz", "integrity": "sha512-wmVKpAlcr+UB0L5SpbrV865EdleUP7I5+X+48e1aRsQK8q+wsTRBXeUwWVip/1l+HZwlZFeO8iOILJ16VRu0Cw==", "license": "MIT", - "peer": true, "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" @@ -28342,15 +28267,13 @@ "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-spring/node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.3.0" }, @@ -28394,7 +28317,6 @@ "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", "license": "MIT", - "peer": true, "peerDependencies": { "react": ">=16.13", "react-dom": ">=16.13" @@ -28576,8 +28498,7 @@ "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", @@ -28766,8 +28687,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/resolve": { "version": "1.22.8", @@ -29315,7 +29235,6 @@ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -30023,7 +29942,6 @@ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -30359,15 +30277,13 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/stacktrace-parser": { "version": "0.1.11", "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", "license": "MIT", - "peer": true, "dependencies": { "type-fest": "^0.7.1" }, @@ -30968,7 +30884,6 @@ "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", "license": "MIT", - "peer": true, "peerDependencies": { "react": ">=17.0" } @@ -31279,6 +31194,7 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -31448,14 +31364,14 @@ "version": "0.171.0", "resolved": "https://registry.npmjs.org/three/-/three-0.171.0.tgz", "integrity": "sha512-Y/lAXPaKZPcEdkKjh0JOAHVv8OOnv/NDJqm0wjfCzyQmfKxV7zvkwsnBgPBKTzJHToSOhRGQAGbPJObT59B/PQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/through2": { "version": "2.0.5", @@ -31614,8 +31530,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -31918,7 +31833,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=8" } @@ -32020,6 +31934,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -32684,6 +32599,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -33577,6 +33493,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -33655,8 +33572,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/vscode-uri": { "version": "3.1.0", @@ -33683,7 +33599,6 @@ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "makeerror": "1.0.12" } @@ -33771,6 +33686,7 @@ "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -33872,8 +33788,7 @@ "version": "3.6.20", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/whatwg-mimetype": { "version": "3.0.0", @@ -34158,6 +34073,7 @@ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -34372,6 +34288,7 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }