Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df9654fed9 | ||
|
|
8b086dd385 | ||
|
|
4d9efb9385 | ||
|
|
c619b291a4 | ||
|
|
9c1633bd9c | ||
|
|
32a6e6470f | ||
|
|
239ae4ada2 | ||
|
|
33d50851b0 |
No files matched your search
@@ -39,6 +39,7 @@ venv/
|
||||
# LLM Testing
|
||||
/external/
|
||||
/prompts/
|
||||
/untracked/
|
||||
/reports/
|
||||
/requests/
|
||||
|
||||
|
||||
+45
-2
@@ -1,9 +1,11 @@
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5::{
|
||||
KinoviSeedance2p5AspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p5Bitrate,
|
||||
KinoviSeedance2p5OutputResolution as KinoviOutputResolution,
|
||||
};
|
||||
|
||||
use crate::api::router_aspect_ratio::RouterAspectRatio;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
@@ -64,8 +66,10 @@ fn do_build_kinovi_seedance_2p5(mut builder: GenerateVideoRequestBuilder) -> Res
|
||||
let total_input_seconds = builder.total_reference_video_input_seconds
|
||||
.map(|seconds| u8::try_from(seconds).unwrap_or(u8::MAX));
|
||||
|
||||
// NB: `builder.bitrate` is intentionally ignored — 2.5 has no bitrate
|
||||
// option (and bitrate never affects cost).
|
||||
let maybe_bitrate = match builder.bitrate.take() {
|
||||
Some(RouterBitrate::High) => Some(KinoviSeedance2p5Bitrate::High),
|
||||
Some(RouterBitrate::Normal) | None => None,
|
||||
};
|
||||
|
||||
let unhandled_request_state = KinoviSeedance2p5RemainingItems {
|
||||
start_frame: builder.start_frame.take(),
|
||||
@@ -81,6 +85,7 @@ fn do_build_kinovi_seedance_2p5(mut builder: GenerateVideoRequestBuilder) -> Res
|
||||
duration_seconds,
|
||||
prompt,
|
||||
total_input_seconds,
|
||||
maybe_bitrate,
|
||||
unhandled_request_state: Some(unhandled_request_state),
|
||||
})
|
||||
}
|
||||
@@ -246,6 +251,7 @@ fn plan_duration(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5::{
|
||||
KinoviSeedance2p5AspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p5OutputResolution as KinoviOutputResolution,
|
||||
@@ -261,13 +267,50 @@ mod tests {
|
||||
use crate::api::router_video_model::RouterVideoModel;
|
||||
use crate::api::video_list_ref::VideoListRef;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p5::cost::KinoviSeedance2p5CostState;
|
||||
use crate::generate::generate_video::providers::kinovi::seedance_2p5::draft::KinoviSeedance2p5DraftState;
|
||||
use crate::generate::generate_video::video_generation_draft::VideoGenerationDraftRequest;
|
||||
use crate::generate::generate_video::video_generation_draft_context::VideoGenerationDraftContext;
|
||||
use crate::generate::generate_video::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn bitrate_survives_finalization_without_changing_cost() {
|
||||
let client = RouterClient::KinoviWeb(RouterKinoviWebClient::new(
|
||||
KinoviWebSession::from_cookies_string(String::new()),
|
||||
));
|
||||
let context = VideoGenerationDraftContext {
|
||||
client: Some(&client),
|
||||
..Default::default()
|
||||
};
|
||||
let baseline = unwrap_draft(build_kinovi_seedance_2p5(base_builder()));
|
||||
let baseline_cost = KinoviSeedance2p5CostState::from_draft(&baseline).estimate_cost();
|
||||
|
||||
for bitrate in [None, Some(RouterBitrate::Normal), Some(RouterBitrate::High)] {
|
||||
let mut draft = unwrap_draft(build_kinovi_seedance_2p5(GenerateVideoRequestBuilder {
|
||||
bitrate,
|
||||
..base_builder()
|
||||
}));
|
||||
let draft_cost = KinoviSeedance2p5CostState::from_draft(&draft).estimate_cost();
|
||||
// No media inputs: finalization needs no network calls or credentials.
|
||||
let finalized = draft.to_request(&context).await.expect("finalize should succeed");
|
||||
|
||||
assert_eq!(
|
||||
matches!(finalized.request.maybe_bitrate, Some(KinoviSeedance2p5Bitrate::High)),
|
||||
matches!(bitrate, Some(RouterBitrate::High)),
|
||||
);
|
||||
let final_cost = KinoviSeedance2p5CostState::from_request(&finalized).estimate_cost();
|
||||
for cost in [draft_cost, final_cost] {
|
||||
assert_eq!(cost.cost_in_credits, baseline_cost.cost_in_credits);
|
||||
assert_eq!(cost.cost_in_usd_cents, baseline_cost.cost_in_usd_cents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod materialized_field_conversions {
|
||||
use super::*;
|
||||
|
||||
|
||||
+1
@@ -91,6 +91,7 @@ impl KinoviSeedance2p5CostState {
|
||||
duration_seconds: self.duration_seconds,
|
||||
total_input_seconds,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
};
|
||||
|
||||
// Enterprise tier: what generations actually cost us (our discounted
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p5::{
|
||||
GenerateSeedance2p5Request, KinoviSeedance2p5AspectRatio, KinoviSeedance2p5Modality,
|
||||
GenerateSeedance2p5Request, KinoviSeedance2p5AspectRatio, KinoviSeedance2p5Bitrate, KinoviSeedance2p5Modality,
|
||||
KinoviSeedance2p5OutputResolution,
|
||||
};
|
||||
|
||||
@@ -32,6 +32,8 @@ pub struct KinoviSeedance2p5DraftState {
|
||||
/// all reference videos (each rounded up to a whole second).
|
||||
pub total_input_seconds: Option<u8>,
|
||||
|
||||
pub maybe_bitrate: Option<KinoviSeedance2p5Bitrate>,
|
||||
|
||||
pub unhandled_request_state: Option<KinoviSeedance2p5RemainingItems>,
|
||||
}
|
||||
|
||||
@@ -111,6 +113,7 @@ impl KinoviSeedance2p5DraftState {
|
||||
duration_seconds: self.duration_seconds,
|
||||
total_input_seconds: self.total_input_seconds,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: self.maybe_bitrate,
|
||||
};
|
||||
|
||||
Ok(KinoviSeedance2p5RequestState { request })
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::pricing::kinovi_pricing_rate::KinoviPricingRate;
|
||||
use crate::pricing::kinovi_pricing_tier::KinoviPricingTier;
|
||||
use crate::requests::kinovi_host::KinoviHost;
|
||||
use crate::requests::workflow_run_task::workflow_run_task::{
|
||||
workflow_run_task, KinoviAspectRatioRaw, KinoviBatchCountRaw, KinoviModelTypeRaw,
|
||||
workflow_run_task, KinoviAspectRatioRaw, KinoviBatchCountRaw, KinoviBitrateRaw, KinoviModelTypeRaw,
|
||||
KinoviOutputResolutionRaw, WorkflowRunTaskArgs, WorkflowRunTaskRequest,
|
||||
};
|
||||
|
||||
@@ -60,6 +60,10 @@ pub struct GenerateSeedance2p5Request {
|
||||
/// Controls `faceBlurMode`: true sends "on"; false or None sends "off"
|
||||
/// (the model always sends the field, like 2.5 Preview).
|
||||
pub use_face_blur_hack: Option<bool>,
|
||||
|
||||
/// Output video bitrate. None keeps the standard bitrate; `High` requests
|
||||
/// a higher bitrate. This does not select a container or lossless codec.
|
||||
pub maybe_bitrate: Option<KinoviSeedance2p5Bitrate>,
|
||||
}
|
||||
|
||||
// ── Modality ──
|
||||
@@ -115,6 +119,11 @@ pub enum KinoviSeedance2p5OutputResolution {
|
||||
TenEightyP,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KinoviSeedance2p5Bitrate {
|
||||
High,
|
||||
}
|
||||
|
||||
// ── Pricing ──
|
||||
//
|
||||
// Seedance 2.5 credit pricing (credits per billed second). Without video
|
||||
@@ -295,7 +304,14 @@ fn to_raw_request(req: GenerateSeedance2p5Request) -> WorkflowRunTaskRequest {
|
||||
reference_audio_urls,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: req.use_face_blur_hack,
|
||||
bitrate: None,
|
||||
bitrate: map_bitrate(req.maybe_bitrate),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_bitrate(maybe_bitrate: Option<KinoviSeedance2p5Bitrate>) -> Option<KinoviBitrateRaw> {
|
||||
match maybe_bitrate {
|
||||
Some(KinoviSeedance2p5Bitrate::High) => Some(KinoviBitrateRaw::High),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,6 +764,7 @@ mod tests {
|
||||
duration_seconds,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,6 +791,7 @@ mod tests {
|
||||
duration_seconds: duration,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,9 +851,11 @@ mod tests {
|
||||
duration_seconds: 8,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
});
|
||||
|
||||
assert!(matches!(raw.model_type, KinoviModelTypeRaw::Seedance2p5));
|
||||
assert!(raw.bitrate.is_none());
|
||||
assert_eq!(raw.start_frame_url.as_deref(), Some("https://example.com/start.png"));
|
||||
assert_eq!(raw.end_frame_url.as_deref(), Some("https://example.com/end.png"));
|
||||
assert!(raw.reference_image_urls.is_none());
|
||||
@@ -857,9 +877,11 @@ mod tests {
|
||||
duration_seconds: 8,
|
||||
total_input_seconds: Some(7),
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: Some(KinoviSeedance2p5Bitrate::High),
|
||||
});
|
||||
|
||||
assert!(matches!(raw.aspect_ratio, KinoviAspectRatioRaw::UltraWide21x9));
|
||||
assert!(matches!(raw.bitrate, Some(KinoviBitrateRaw::High)));
|
||||
assert!(raw.start_frame_url.is_none());
|
||||
assert!(raw.end_frame_url.is_none());
|
||||
assert_eq!(raw.reference_image_urls.as_deref().map(|urls| urls.len()), Some(1));
|
||||
@@ -881,6 +903,7 @@ mod tests {
|
||||
duration_seconds: 4,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
});
|
||||
|
||||
assert!(matches!(raw.output_resolution, Some(KinoviOutputResolutionRaw::TenEightyP)));
|
||||
@@ -900,6 +923,7 @@ mod tests {
|
||||
duration_seconds: 5,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
});
|
||||
|
||||
assert!(matches!(raw.aspect_ratio, KinoviAspectRatioRaw::Landscape16x9));
|
||||
@@ -929,6 +953,7 @@ mod tests {
|
||||
duration_seconds: 5,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("t2v 480p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -960,6 +985,7 @@ mod tests {
|
||||
duration_seconds: 4,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("t2v 1080p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -987,6 +1013,7 @@ mod tests {
|
||||
duration_seconds: 4,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("keyframe 1080p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1014,6 +1041,7 @@ mod tests {
|
||||
duration_seconds: 8,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("keyframe 480p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1046,6 +1074,7 @@ mod tests {
|
||||
duration_seconds: 8,
|
||||
total_input_seconds: None,
|
||||
use_face_blur_hack: None,
|
||||
maybe_bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("reference 480p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
|
||||
+16
@@ -1,5 +1,6 @@
|
||||
use artcraft_api_defs::omni_gen::models::omni_gen_video_models::OmniGenVideoModelDetails;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate;
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
use enums::common::generation::model_creator::ModelCreator;
|
||||
@@ -42,6 +43,11 @@ pub fn seedance_2p5_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::TenEightyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
@@ -84,6 +90,11 @@ pub fn seedance_2p5_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::TenEightyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
@@ -125,6 +136,11 @@ pub fn seedance_2p5_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::SevenTwentyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
|
||||
+52
-40
@@ -15,7 +15,7 @@
|
||||
"gsap": "3.15.0",
|
||||
"lenis": "1.3.23",
|
||||
"lucide-react": "^1.31.0",
|
||||
"next": "16.2.6",
|
||||
"next": "^16.2.12",
|
||||
"react": "19.2.6",
|
||||
"react-dom": "19.2.6",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
@@ -608,15 +608,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz",
|
||||
"integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.12.tgz",
|
||||
"integrity": "sha512-d0Z5Bc13Fa4nR8pFAKx2jay2yhJM16vlfHbTzYnUQAxlNb6B6lmn4hjt69lYNt4kRtyYP6gEM49lPRHNbIyneg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz",
|
||||
"integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.12.tgz",
|
||||
"integrity": "sha512-0W1R0teHWJrqKX0FH20IzzIWAOuGtBxPGuObrxy1lE8hQvCFj49KE8a3WUg0D7sq6rn6zkM4c7YGUnhudBS6oA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -630,9 +630,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz",
|
||||
"integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.12.tgz",
|
||||
"integrity": "sha512-Hy5Ls099+aFUmOLmIgPfLqNi6iCwhL3uQCssz5rWk+5Nkc6TUKCE83DY5BbNylfm3+mfwcSFnLRfrZDJhVxdtw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -646,12 +646,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz",
|
||||
"integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.12.tgz",
|
||||
"integrity": "sha512-+YqU2h1cQkHsGfvjAsrSmst8UIFBibBGm5x3Xgel8NLMiDQtNOM4sM2GOEMvG5YiOBNeN/Ykk8cQC2S0Xrqljg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -662,12 +665,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz",
|
||||
"integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.12.tgz",
|
||||
"integrity": "sha512-0qjhiYBaKAqF63LA1ZWAAnKTzFUguAaZiRa5etMLGGPj/B6uEVjtIZldIzFEp3wHlB0koK6aTzqPtSdplTCjoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -678,12 +684,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz",
|
||||
"integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.12.tgz",
|
||||
"integrity": "sha512-7A3q26W+h7gnA15uqBToNuDqBEFZZcqh0mW2mn4AJh/G5pdg2RVE3n4slzLEliASZFG3NmsbEzng/x2Sh09mBg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -694,12 +703,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz",
|
||||
"integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.12.tgz",
|
||||
"integrity": "sha512-qSjL/uppm+cbh21s72Ss8gkiOhQ4dExWHNGOWy6eZV7STj5WsKehgxT61beSsOj+YYQuTplL376lOCdMQU5T8w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -710,9 +722,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz",
|
||||
"integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.12.tgz",
|
||||
"integrity": "sha512-X6hzsOUJac/e7AWSbn9gQ9nzHld1xWP5iyjHpYWvud8pufB679O1xg4JDyKr8Xd69Jvd+kM2Der6uftiZCmjYA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -726,9 +738,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz",
|
||||
"integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.12.tgz",
|
||||
"integrity": "sha512-F6fakeHuFTLOPt0bslQJdf+xtT+WIP9DVn/m4y1w1mRnVPyh3D/cNvzlRkxM444xfm+IvvYNSOrKiA2CDJ0Uxw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2209,12 +2221,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz",
|
||||
"integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==",
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-16.2.12.tgz",
|
||||
"integrity": "sha512-iD59eYQWmbFcEbX7v/acG5DRym9iw1DdaPoD0WTA920naWsE25wShzJW4+UvAs8MK9EC2kBfIH6vtto1H1PHGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/env": "16.2.6",
|
||||
"@next/env": "16.2.12",
|
||||
"@swc/helpers": "0.5.15",
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
@@ -2228,14 +2240,14 @@
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "16.2.6",
|
||||
"@next/swc-darwin-x64": "16.2.6",
|
||||
"@next/swc-linux-arm64-gnu": "16.2.6",
|
||||
"@next/swc-linux-arm64-musl": "16.2.6",
|
||||
"@next/swc-linux-x64-gnu": "16.2.6",
|
||||
"@next/swc-linux-x64-musl": "16.2.6",
|
||||
"@next/swc-win32-arm64-msvc": "16.2.6",
|
||||
"@next/swc-win32-x64-msvc": "16.2.6",
|
||||
"@next/swc-darwin-arm64": "16.2.12",
|
||||
"@next/swc-darwin-x64": "16.2.12",
|
||||
"@next/swc-linux-arm64-gnu": "16.2.12",
|
||||
"@next/swc-linux-arm64-musl": "16.2.12",
|
||||
"@next/swc-linux-x64-gnu": "16.2.12",
|
||||
"@next/swc-linux-x64-musl": "16.2.12",
|
||||
"@next/swc-win32-arm64-msvc": "16.2.12",
|
||||
"@next/swc-win32-x64-msvc": "16.2.12",
|
||||
"sharp": "^0.34.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"gsap": "3.15.0",
|
||||
"lenis": "1.3.23",
|
||||
"lucide-react": "^1.31.0",
|
||||
"next": "16.2.6",
|
||||
"next": "^16.2.12",
|
||||
"react": "19.2.6",
|
||||
"react-dom": "19.2.6",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
|
||||
@@ -101,11 +101,13 @@ const JSON_LD = {
|
||||
// full word flashes for the frames between paint and hydration, then
|
||||
// snaps into the logo-only formation start.
|
||||
//
|
||||
// Also reloads on back/forward-cache restores: Firefox freezes the JS heap
|
||||
// and discards the WebGL context, so a restored page shows a dead galaxy
|
||||
// canvas and stalled instruments. The listener is attached here (pre-paint)
|
||||
// so it lives inside the frozen page and fires on the restore; the intro is
|
||||
// seen-flagged by then, so the reload comes back settled.
|
||||
// Also reloads on back/forward-cache restores (pageshow with persisted):
|
||||
// a bfcache-restored page thaws with its WebGL context discarded in some
|
||||
// browsers, leaving a dead galaxy canvas — a reload swaps it for a clean
|
||||
// load, and the intro is seen-flagged by then so it comes back settled.
|
||||
// (The Firefox back-navigation hydration failure was a different bug:
|
||||
// Next's router crashed on back_forward loads — "Router action dispatched
|
||||
// before initialization" — fixed by the Next 16.2.12 patch bump.)
|
||||
const THEME_SCRIPT = `(function(){try{var t=localStorage.getItem("artcraft-theme");if(t==="light"||t==="dark"){document.documentElement.setAttribute("data-theme",t);}}catch(e){}try{if(!matchMedia("(prefers-reduced-motion: reduce)").matches){document.documentElement.setAttribute("data-intro","");}}catch(e){}addEventListener("pageshow",function(e){if(e.persisted)location.reload();});})();`;
|
||||
|
||||
export default function RootLayout({
|
||||
|
||||
@@ -362,9 +362,9 @@ export const galaxyPointerTuner = defineTunables(
|
||||
boostScale: {
|
||||
label: "Boost scale ×",
|
||||
min: 1.2,
|
||||
max: 3,
|
||||
max: 4,
|
||||
step: 0.1,
|
||||
default: 2,
|
||||
default: 3,
|
||||
info: "Scale multiplier a click toggles onto the held card. Allowed to overlap neighbors; scales back down on leaving the card, and the next hold needs a fresh click.",
|
||||
},
|
||||
boostTau: {
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
//const WINDOWS_VERSION = "0.7.0"; // 2026-02-23 Seedance
|
||||
//const WINDOWS_VERSION = "0.11.0"; // 2026-03-02 Credits accounting
|
||||
//const WINDOWS_VERSION = "0.14.0"; // 2026-03-10 WorldLabs native, Angles
|
||||
//const WINDOWS_VERSION = "0.20.0"; // 2026-03-24 Reprompt, performance
|
||||
const WINDOWS_VERSION = "0.32.0"; // 2026-04-03 characters + prompt box
|
||||
// Keep previous releases here when adding new downloads, newest first.
|
||||
const DOWNLOAD_HISTORY = {
|
||||
"0.37.0": githubDownloadLinks("0.37.0"),
|
||||
"0.32.0": githubDownloadLinks("0.32.0"),
|
||||
"0.28.0": githubDownloadLinks("0.28.0"),
|
||||
"0.23.0": githubDownloadLinks("0.23.0"), // 2026-04-03 Characters + prompt box
|
||||
"0.20.0": githubDownloadLinks("0.20.0"), // 2026-03-24 Reprompt, performance
|
||||
"0.14.0": githubDownloadLinks("0.14.0"), // 2026-03-10 WorldLabs native, Angles
|
||||
"0.11.0": githubDownloadLinks("0.11.0"), // 2026-03-02 Credits accounting
|
||||
"0.7.0": githubDownloadLinks("0.7.0"), // 2026-02-23 Seedance
|
||||
} as const;
|
||||
|
||||
//const MAC_VERSION = "0.7.0"; // 2026-02-23 Seedance
|
||||
//const MAC_VERSION = "0.11.0"; // 2026-03-02 Credits accounting
|
||||
//const MAC_VERSION = "0.14.0"; // 2026-03-10 WorldLabs native, Angles
|
||||
//const MAC_VERSION = "0.20.0"; // 2026-03-24 Reprompt, performance
|
||||
const MAC_VERSION = "0.32.0"; // 2026-04-03 characters + prompt box
|
||||
// To roll back either platform, select a version from DOWNLOAD_HISTORY.
|
||||
const WINDOWS_VERSION: keyof typeof DOWNLOAD_HISTORY = "0.37.0";
|
||||
const MAC_VERSION: keyof typeof DOWNLOAD_HISTORY = "0.37.0";
|
||||
|
||||
// The published desktop builds are out of date — every download button and
|
||||
// link on the site is hidden while this is false. Flip back on when a current
|
||||
// release ships.
|
||||
export const DOWNLOADS_ENABLED = false;
|
||||
// Set false to temporarily hide desktop downloads throughout the website.
|
||||
export const DOWNLOADS_ENABLED = true;
|
||||
|
||||
export const DOWNLOAD_LINKS = {
|
||||
WINDOWS: `https://github.com/storytold/artcraft/releases/download/artcraft-v${WINDOWS_VERSION}/ArtCraft_${WINDOWS_VERSION}_x64-setup.exe`,
|
||||
MACOS: `https://github.com/storytold/artcraft/releases/download/artcraft-v${MAC_VERSION}/ArtCraft_${MAC_VERSION}_universal.dmg`,
|
||||
WINDOWS: DOWNLOAD_HISTORY[WINDOWS_VERSION].WINDOWS,
|
||||
MACOS: DOWNLOAD_HISTORY[MAC_VERSION].MACOS,
|
||||
} as const;
|
||||
|
||||
function githubDownloadLinks(version: string) {
|
||||
const baseUrl = `https://github.com/storytold/artcraft/releases/download/artcraft-v${version}`;
|
||||
return {
|
||||
WINDOWS: `${baseUrl}/ArtCraft_${version}_x64-setup.exe`,
|
||||
MACOS: `${baseUrl}/ArtCraft_${version}_universal.dmg`,
|
||||
} as const;
|
||||
}
|
||||
Reference in new issue
Block a user