Compare commits

...
273 changed files with 219 additions and 126 deletions
@@ -138,6 +138,7 @@ pub(crate) fn plan_batch_count(
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
1 => Ok(KinoviMidjourneyBatchCount::One),
2 => Ok(KinoviMidjourneyBatchCount::Two),
3 => Ok(KinoviMidjourneyBatchCount::Three),
4 => Ok(KinoviMidjourneyBatchCount::Four),
other => match strategy {
RequestMismatchMitigationStrategy::ErrorOut => {
@@ -146,13 +147,11 @@ pub(crate) fn plan_batch_count(
value: format!("{}", other),
}))
}
RequestMismatchMitigationStrategy::PayMoreUpgrade => {
// 3 → 4, anything > 4 → 4
Ok(if other < 3 { KinoviMidjourneyBatchCount::Two } else { KinoviMidjourneyBatchCount::Four })
}
RequestMismatchMitigationStrategy::PayLessDowngrade => {
// 3 → 2, anything > 4 → 4
Ok(if other < 4 { KinoviMidjourneyBatchCount::Two } else { KinoviMidjourneyBatchCount::Four })
// Kinovi supports batches of 1–4; only counts of 5+ reach here, so both
// mitigation strategies clamp to the maximum supported batch.
RequestMismatchMitigationStrategy::PayMoreUpgrade
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
Ok(KinoviMidjourneyBatchCount::Four)
}
},
}
@@ -379,9 +378,10 @@ mod tests {
}
#[test]
fn one_two_four_map_directly() {
fn one_two_three_four_map_directly() {
assert_eq!(batch_for(Some(1)), KinoviMidjourneyBatchCount::One);
assert_eq!(batch_for(Some(2)), KinoviMidjourneyBatchCount::Two);
assert_eq!(batch_for(Some(3)), KinoviMidjourneyBatchCount::Three);
assert_eq!(batch_for(Some(4)), KinoviMidjourneyBatchCount::Four);
}
@@ -394,43 +394,57 @@ mod tests {
));
}
/// Kinovi now supports batches of 3, so 3 is a first-class value and the
/// mitigation strategy no longer applies to it.
#[test]
fn three_errors_under_strict() {
let builder = GenerateImageRequestBuilder {
image_batch_count: Some(3),
fn three_maps_directly_regardless_of_strategy() {
for strategy in [
RequestMismatchMitigationStrategy::ErrorOut,
RequestMismatchMitigationStrategy::PayMoreUpgrade,
RequestMismatchMitigationStrategy::PayLessDowngrade,
] {
let builder = GenerateImageRequestBuilder {
image_batch_count: Some(3),
request_mismatch_mitigation_strategy: strategy,
..base_builder()
};
assert_eq!(
unwrap_direct_request(build_kinovi_midjourney_7(builder)).request.batch_count,
KinoviMidjourneyBatchCount::Three,
"strategy={:?}", strategy,
);
}
}
/// Counts above 4 are still unsupported: strict errors out, and both
/// pay-more/pay-less strategies clamp to the maximum supported batch (4).
#[test]
fn five_errors_under_strict_and_clamps_to_four_otherwise() {
let strict = GenerateImageRequestBuilder {
image_batch_count: Some(5),
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
..base_builder()
};
assert!(matches!(
build_kinovi_midjourney_7(builder),
build_kinovi_midjourney_7(strict),
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { .. })),
));
}
#[test]
fn three_upgrades_to_four_with_pay_more() {
let builder = GenerateImageRequestBuilder {
image_batch_count: Some(3),
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
..base_builder()
};
assert_eq!(
unwrap_direct_request(build_kinovi_midjourney_7(builder)).request.batch_count,
KinoviMidjourneyBatchCount::Four,
);
}
#[test]
fn three_downgrades_to_two_with_pay_less() {
let builder = GenerateImageRequestBuilder {
image_batch_count: Some(3),
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayLessDowngrade,
..base_builder()
};
assert_eq!(
unwrap_direct_request(build_kinovi_midjourney_7(builder)).request.batch_count,
KinoviMidjourneyBatchCount::Two,
);
for strategy in [
RequestMismatchMitigationStrategy::PayMoreUpgrade,
RequestMismatchMitigationStrategy::PayLessDowngrade,
] {
let builder = GenerateImageRequestBuilder {
image_batch_count: Some(5),
request_mismatch_mitigation_strategy: strategy,
..base_builder()
};
assert_eq!(
unwrap_direct_request(build_kinovi_midjourney_7(builder)).request.batch_count,
KinoviMidjourneyBatchCount::Four,
"strategy={:?}", strategy,
);
}
}
}
@@ -126,6 +126,7 @@ pub(crate) fn plan_batch_count(
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
1 => Ok(KinoviMidjourneyBatchCount::One),
2 => Ok(KinoviMidjourneyBatchCount::Two),
3 => Ok(KinoviMidjourneyBatchCount::Three),
4 => Ok(KinoviMidjourneyBatchCount::Four),
other => match strategy {
RequestMismatchMitigationStrategy::ErrorOut => {
@@ -134,11 +135,11 @@ pub(crate) fn plan_batch_count(
value: format!("{}", other),
}))
}
RequestMismatchMitigationStrategy::PayMoreUpgrade => {
Ok(if other < 3 { KinoviMidjourneyBatchCount::Two } else { KinoviMidjourneyBatchCount::Four })
}
RequestMismatchMitigationStrategy::PayLessDowngrade => {
Ok(if other < 4 { KinoviMidjourneyBatchCount::Two } else { KinoviMidjourneyBatchCount::Four })
// Kinovi supports batches of 1–4; only counts of 5+ reach here, so both
// mitigation strategies clamp to the maximum supported batch.
RequestMismatchMitigationStrategy::PayMoreUpgrade
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
Ok(KinoviMidjourneyBatchCount::Four)
}
},
}
@@ -212,6 +213,16 @@ mod tests {
));
}
#[test]
fn batch_count_three_is_accepted() {
let builder = GenerateImageRequestBuilder { image_batch_count: Some(3), ..base_builder() };
let req = match build_kinovi_midjourney_7_niji(builder).expect("build") {
ImageGenerationDraftOrRequest::Request(ImageGenerationRequest::KinoviMidjourney7Niji(r)) => r,
_ => panic!("expected Request"),
};
assert_eq!(req.request.batch_count, KinoviMidjourneyBatchCount::Three);
}
#[test]
fn batch_count_four_is_accepted() {
let builder = GenerateImageRequestBuilder { image_batch_count: Some(4), ..base_builder() };
@@ -126,6 +126,7 @@ pub(crate) fn plan_batch_count(
0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)),
1 => Ok(KinoviMidjourneyBatchCount::One),
2 => Ok(KinoviMidjourneyBatchCount::Two),
3 => Ok(KinoviMidjourneyBatchCount::Three),
4 => Ok(KinoviMidjourneyBatchCount::Four),
other => match strategy {
RequestMismatchMitigationStrategy::ErrorOut => {
@@ -134,11 +135,11 @@ pub(crate) fn plan_batch_count(
value: format!("{}", other),
}))
}
RequestMismatchMitigationStrategy::PayMoreUpgrade => {
Ok(if other < 3 { KinoviMidjourneyBatchCount::Two } else { KinoviMidjourneyBatchCount::Four })
}
RequestMismatchMitigationStrategy::PayLessDowngrade => {
Ok(if other < 4 { KinoviMidjourneyBatchCount::Two } else { KinoviMidjourneyBatchCount::Four })
// Kinovi supports batches of 1–4; only counts of 5+ reach here, so both
// mitigation strategies clamp to the maximum supported batch.
RequestMismatchMitigationStrategy::PayMoreUpgrade
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
Ok(KinoviMidjourneyBatchCount::Four)
}
},
}
@@ -201,6 +202,16 @@ mod tests {
));
}
#[test]
fn batch_count_three_is_accepted() {
let builder = GenerateImageRequestBuilder { image_batch_count: Some(3), ..base_builder() };
let req = match build_kinovi_midjourney_8(builder).expect("build") {
ImageGenerationDraftOrRequest::Request(ImageGenerationRequest::KinoviMidjourney8(r)) => r,
_ => panic!("expected Request"),
};
assert_eq!(req.request.batch_count, KinoviMidjourneyBatchCount::Three);
}
#[test]
fn batch_count_four_is_accepted() {
let builder = GenerateImageRequestBuilder { image_batch_count: Some(4), ..base_builder() };
@@ -282,6 +282,11 @@ mod tests {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Two).calculate_costs().kinovi_credits, 24);
}
#[test]
fn batch_three_is_thirtysix_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Three).calculate_costs().kinovi_credits, 36);
}
#[test]
fn batch_four_is_fortyeight_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Four).calculate_costs().kinovi_credits, 48);
@@ -308,6 +313,7 @@ mod tests {
for batch in [
KinoviMidjourneyBatchCount::One,
KinoviMidjourneyBatchCount::Two,
KinoviMidjourneyBatchCount::Three,
KinoviMidjourneyBatchCount::Four,
] {
let outer = make_request(batch);
@@ -280,6 +280,11 @@ mod tests {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Two).calculate_costs().kinovi_credits, 24);
}
#[test]
fn batch_three_is_thirtysix_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Three).calculate_costs().kinovi_credits, 36);
}
#[test]
fn batch_four_is_fortyeight_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Four).calculate_costs().kinovi_credits, 48);
@@ -305,6 +310,7 @@ mod tests {
for batch in [
KinoviMidjourneyBatchCount::One,
KinoviMidjourneyBatchCount::Two,
KinoviMidjourneyBatchCount::Three,
KinoviMidjourneyBatchCount::Four,
] {
let outer = make_request(batch);
@@ -277,6 +277,11 @@ mod tests {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Two).calculate_costs().kinovi_credits, 24);
}
#[test]
fn batch_three_is_thirtysix_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Three).calculate_costs().kinovi_credits, 36);
}
#[test]
fn batch_four_is_fortyeight_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Four).calculate_costs().kinovi_credits, 48);
@@ -302,6 +307,7 @@ mod tests {
for batch in [
KinoviMidjourneyBatchCount::One,
KinoviMidjourneyBatchCount::Two,
KinoviMidjourneyBatchCount::Three,
KinoviMidjourneyBatchCount::Four,
] {
let outer = make_request(batch);
@@ -25,7 +25,8 @@ const CREDITS_PER_MIDJOURNEY_TASK: u32 = 12;
/// 500,000 credits / $2,159.09 = 231.58 ≈ 243 credits per dollar (rounded down).
/// Kinovi only currently exposes a single Midjourney resolution preset.
const MIDJOURNEY_RESOLUTION: &str = "1k";
/// Kinovi moved these Midjourney models to `2k`; `1k` is no longer accepted.
const MIDJOURNEY_RESOLUTION: &str = "2k";
const BUSINESS_TYPE: &str = "midjourney-image-generation";
@@ -174,6 +175,7 @@ impl KinoviMidjourneyModel {
pub enum KinoviMidjourneyBatchCount {
One,
Two,
Three,
Four,
}
@@ -182,6 +184,7 @@ impl KinoviMidjourneyBatchCount {
match self {
Self::One => 1,
Self::Two => 2,
Self::Three => 3,
Self::Four => 4,
}
}
@@ -364,6 +367,11 @@ mod tests {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Two).calculate_costs().kinovi_credits, 24);
}
#[test]
fn batch_three_is_thirtysix_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Three).calculate_costs().kinovi_credits, 36);
}
#[test]
fn batch_four_is_fortyeight_credits() {
assert_eq!(make_request(KinoviMidjourneyBatchCount::Four).calculate_costs().kinovi_credits, 48);
@@ -525,11 +533,11 @@ mod tests {
#[test]
fn minimal_v7_matches_captured_sample() {
// Sample #1: `{"prompt":"Anime dinosaur","aspectRatio":"1:1","resolution":"1k","model":"midjourney-v7"}`
let params = api_params(minimal_request(KinoviMidjourneyModel::V7, "Anime dinosaur"));
assert_eq!(params["prompt"], "Anime dinosaur");
// Sample #1: `{"prompt":"A corgi walking in the park","aspectRatio":"1:1","resolution":"2k","model":"midjourney-v7"}`
let params = api_params(minimal_request(KinoviMidjourneyModel::V7, "A corgi walking in the park"));
assert_eq!(params["prompt"], "A corgi walking in the park");
assert_eq!(params["aspectRatio"], "1:1");
assert_eq!(params["resolution"], "1k");
assert_eq!(params["resolution"], "2k");
assert_eq!(params["model"], "midjourney-v7");
let obj = params.as_object().unwrap();
@@ -704,6 +712,12 @@ mod tests {
assert_eq!(api_params(req)["batchCount"], 2);
}
#[test]
fn batch_three_sends_batch_count_3() {
let req = KinoviGenerateImageRequest { batch_count: KinoviMidjourneyBatchCount::Three, ..minimal_request(KinoviMidjourneyModel::V7Niji, "p") };
assert_eq!(api_params(req)["batchCount"], 3);
}
#[test]
fn batch_four_sends_batch_count_4() {
let req = KinoviGenerateImageRequest { batch_count: KinoviMidjourneyBatchCount::Four, ..minimal_request(KinoviMidjourneyModel::V7, "p") };
@@ -719,9 +733,9 @@ mod tests {
}
#[test]
fn resolution_is_1k() {
fn resolution_is_2k() {
let p = api_params(minimal_request(KinoviMidjourneyModel::V7, "p"));
assert_eq!(p["resolution"], "1k");
assert_eq!(p["resolution"], "2k");
}
// ── Captured-sample replay (#9: kitchen sink) ──
@@ -745,7 +759,7 @@ mod tests {
};
let got = api_params(req);
let expected: Value = serde_json::from_str(
r#"{"prompt":"abandoned skyscrapers","aspectRatio":"1:1","resolution":"1k","model":"midjourney-v7","stylize":1000,"chaos":100,"weird":3000,"quality":0.5,"style":"raw","no":"dark, gloomy, night"}"#,
r#"{"prompt":"abandoned skyscrapers","aspectRatio":"1:1","resolution":"2k","model":"midjourney-v7","stylize":1000,"chaos":100,"weird":3000,"quality":0.5,"style":"raw","no":"dark, gloomy, night"}"#,
).unwrap();
assert_eq!(got, expected);
}
@@ -768,7 +782,32 @@ mod tests {
};
let got = api_params(req);
let expected: Value = serde_json::from_str(
r#"{"prompt":"desolate cliff overlooking the ocean","aspectRatio":"9:16","resolution":"1k","model":"midjourney-v8","batchCount":4,"quality":1.0}"#,
r#"{"prompt":"desolate cliff overlooking the ocean","aspectRatio":"9:16","resolution":"2k","model":"midjourney-v8","batchCount":4,"quality":1.0}"#,
).unwrap();
assert_eq!(got, expected);
}
/// Real capture (2026-07-01): V7-niji + batch 3 + all knobs. `seed` is
/// present in the real request but not yet supported by the client, so
/// it is excluded from the expected shape here.
#[test]
fn captured_v7_niji_batch_3_matches_except_seed() {
let req = KinoviGenerateImageRequest {
model: KinoviMidjourneyModel::V7Niji,
prompt: "A shiba walking in the park".to_string(),
aspect_ratio: "16:9".to_string(),
negative_prompt: Some("bad quality, green".to_string()),
stylize: Some(80),
weird: Some(700),
chaos: Some(15),
quality: Some("0.25".to_string()),
raw_mode: false,
batch_count: KinoviMidjourneyBatchCount::Three,
reference_image_urls: None,
};
let got = api_params(req);
let expected: Value = serde_json::from_str(
r#"{"prompt":"A shiba walking in the park","aspectRatio":"16:9","resolution":"2k","model":"midjourney-v7-niji","batchCount":3,"stylize":80,"chaos":15,"weird":700,"quality":0.25,"no":"bad quality, green"}"#,
).unwrap();
assert_eq!(got, expected);
}
+1 -1
View File
@@ -1,4 +1,4 @@
/// The version of the ArtCraft desktop app reported in the UI and via Analytics.
/// This should match the CI builds.
pub const ARTCRAFT_VERSION: &str = "0.33.0";
pub const ARTCRAFT_VERSION: &str = "0.35.0";
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2.0.0-rc",
"productName": "ArtCraft",
"version": "0.33.0",
"version": "0.35.0",
"identifier": "ai.artcraft.app",
"build": {
"frontendDist": "../../../frontend/apps/artcraft/dist",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2.0.0-rc",
"productName": "ArtCraft",
"version": "0.33.0",
"version": "0.35.0",
"identifier": "ai.artcraft.app",
"build": {
"frontendDist": "../../../frontend/apps/artcraft/dist",
@@ -16,20 +16,20 @@
# Paths can be absolute, or relative to this YAML file's directory.
alert_beep_sound: test_beep.wav
alert_done_sound: /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_flower.wav
alert_await_user_input_sound: /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_ghost.wav
alert_done_sound: ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_flower.wav
alert_await_user_input_sound: ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_ghost.wav
extra_alert_beep_sounds: []
extra_alert_done_sounds:
- /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_specialflower.wav
- /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_correct.wav
- /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_character_extrapower.wav
- ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_specialflower.wav
- ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_correct.wav
- ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_character_extrapower.wav
extra_alert_await_sounds:
- /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_wrong.wav
- /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_drybones_crumble.wav
- /Users/bt/dev/storyteller/artcraft/frontend/apps/artcraft/app/public/resources/sound/smrpg_enemy_spikethrow.wav
- ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_wrong.wav
- ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_drybones_crumble.wav
- ../../../../../frontend/apps/artcraft/app/public/resources/sound/smrpg_enemy_spikethrow.wav
loop_alert_timeout_millis: 2000
Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

@@ -62,7 +62,7 @@ export const APP_DESCRIPTORS: AppDescriptor[] = [
id: "2D",
label: "Image Editor",
icon: faPenNib,
imageSrc: "/resources/gifs/2D_CANVAS_DEMO.gif",
imageSrc: "/resources/gifs/2D_CANVAS_DEMO.webp",
description: "Easy edits. Great for graphic design.",
large: true,
},
@@ -70,7 +70,7 @@ export const APP_DESCRIPTORS: AppDescriptor[] = [
id: "3D",
label: "3D Stage",
icon: faCube,
imageSrc: "/resources/gifs/3D_CANVAS_DEMO.gif",
imageSrc: "/resources/gifs/3D_CANVAS_DEMO.webp",
description: "Precision control. Great for AI film.",
large: true,
},
@@ -5,7 +5,7 @@
rgb(var(--st-photo-tint-rgb) / var(--st-photo-tint-alpha)),
rgb(var(--st-photo-tint-rgb) / var(--st-photo-tint-alpha))
),
url("/resources/images/digital-mountains.png");
url("/resources/images/digital-mountains.webp");
background-repeat: no-repeat;
background-size: cover;
position: fixed;
@@ -6,7 +6,7 @@ export const ImageTo3DObject = () => {
title="Generate 3D Object"
subtitle="Transform your image into a 3D object with textures."
variant="object"
backgroundImage="/resources/images/floating-cubes.png"
backgroundImage="/resources/images/floating-cubes.webp"
/>
);
};
@@ -6,7 +6,7 @@ export const ImageTo3DWorld = () => {
title="Image to 3D World"
subtitle="Generate 3D worlds from an image with World Labs"
variant="world"
backgroundImage="/resources/images/room-of-items.png"
backgroundImage="/resources/images/room-of-items.webp"
/>
);
};
@@ -5,7 +5,7 @@
rgb(var(--st-photo-tint-rgb) / var(--st-photo-tint-alpha)),
rgb(var(--st-photo-tint-rgb) / var(--st-photo-tint-alpha))
),
url("/resources/images/forest-trees.png");
url("/resources/images/forest-trees.webp");
background-repeat: no-repeat;
background-size: cover;
position: fixed;
@@ -1075,12 +1075,12 @@ class Scene {
if (this.skybox == "Default") {
const texture = loader.load([
"/resources/skybox/day/px.png",
"/resources/skybox/day/nx.png",
"/resources/skybox/day/py.png",
"/resources/skybox/day/ny.png",
"/resources/skybox/day/pz.png",
"/resources/skybox/day/nz.png",
"/resources/skybox/day/px.webp",
"/resources/skybox/day/nx.webp",
"/resources/skybox/day/py.webp",
"/resources/skybox/day/ny.webp",
"/resources/skybox/day/pz.webp",
"/resources/skybox/day/nz.webp",
]);
this.scene.background = texture;
if (this.ambientLight) this.scene.remove(this.ambientLight);
@@ -1088,12 +1088,12 @@ class Scene {
if (this.hemisphereLight) this.scene.add(this.hemisphereLight);
} else if (this.skybox == "m_0") {
const texture = loader.load([
"/resources/skybox/night/Night_Moon_Burst_Cam_2_LeftX.png",
"/resources/skybox/night/Night_Moon_Burst_Cam_3_Right-X.png",
"/resources/skybox/night/Night_Moon_Burst_Cam_4_UpY.png",
"/resources/skybox/night/Night_Moon_Burst_Cam_5_Down-Y.png",
"/resources/skybox/night/Night_Moon_Burst_Cam_0_FrontZ.png",
"/resources/skybox/night/Night_Moon_Burst_Cam_1_Back-Z.png",
"/resources/skybox/night/Night_Moon_Burst_Cam_2_LeftX.webp",
"/resources/skybox/night/Night_Moon_Burst_Cam_3_Right-X.webp",
"/resources/skybox/night/Night_Moon_Burst_Cam_4_UpY.webp",
"/resources/skybox/night/Night_Moon_Burst_Cam_5_Down-Y.webp",
"/resources/skybox/night/Night_Moon_Burst_Cam_0_FrontZ.webp",
"/resources/skybox/night/Night_Moon_Burst_Cam_1_Back-Z.webp",
]);
this.scene.background = texture;
if (this.ambientLight) this.scene.remove(this.ambientLight);
@@ -1112,12 +1112,12 @@ class Scene {
if (this.hemisphereLight) this.scene.remove(this.hemisphereLight);
} else if (this.skybox == "m_3") {
const texture = loader.load([
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_2_LeftX.png",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_3_Right-X.png",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_4_UpY.png",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_5_Down-Y.png",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_0_FrontZ.png",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_1_Back-Z.png",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_2_LeftX.webp",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_3_Right-X.webp",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_4_UpY.webp",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_5_Down-Y.webp",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_0_FrontZ.webp",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_1_Back-Z.webp",
]);
this.scene.background = texture;
if (this.ambientLight) this.scene.remove(this.ambientLight);
@@ -1142,7 +1142,7 @@ class Scene {
// deafult image skybox.
_create_single_skybox() {
const loader = new THREE.TextureLoader();
const texture = loader.load("/resources/skybox/single.jpg", () => {
const texture = loader.load("/resources/skybox/single.webp", () => {
texture.mapping = THREE.EquirectangularReflectionMapping;
texture.colorSpace = THREE.SRGBColorSpace;
this.scene.background = texture;
@@ -64,7 +64,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Jump Forward",
thumbnail: "/resources/characters_gif/Jump Forward.gif",
thumbnail: "/resources/characters_gif/Jump Forward.webp",
media_type: "glb",
},
{
@@ -73,7 +73,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Tonic Seizure",
thumbnail: "/resources/characters_gif/Tonic Seizure.gif",
thumbnail: "/resources/characters_gif/Tonic Seizure.webp",
media_type: "glb",
},
{
@@ -82,7 +82,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Ginga Variation 3",
thumbnail: "/resources/characters_gif/Ginga Variation 3.gif",
thumbnail: "/resources/characters_gif/Ginga Variation 3.webp",
media_type: "glb",
},
{
@@ -91,7 +91,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Texting",
thumbnail: "/resources/characters_gif/Texting.gif",
thumbnail: "/resources/characters_gif/Texting.webp",
media_type: "glb",
},
{
@@ -100,7 +100,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Pushing",
thumbnail: "/resources/characters_gif/Pushing.gif",
thumbnail: "/resources/characters_gif/Pushing.webp",
media_type: "glb",
},
{
@@ -109,7 +109,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Texting While Standing",
thumbnail: "/resources/characters_gif/Texting While Standing.gif",
thumbnail: "/resources/characters_gif/Texting While Standing.webp",
media_type: "glb",
},
{
@@ -118,7 +118,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Getting Bit",
thumbnail: "/resources/characters_gif/Getting Bit.gif",
thumbnail: "/resources/characters_gif/Getting Bit.webp",
media_type: "glb",
},
{
@@ -127,7 +127,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Twist Dance",
thumbnail: "/resources/characters_gif/Twist Dance.gif",
thumbnail: "/resources/characters_gif/Twist Dance.webp",
media_type: "glb",
},
{
@@ -136,7 +136,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Peek Around Corner",
thumbnail: "/resources/characters_gif/Femme Peek Around Corner.gif",
thumbnail: "/resources/characters_gif/Femme Peek Around Corner.webp",
media_type: "glb",
},
{
@@ -145,7 +145,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Sword And Shield Casting",
thumbnail: "/resources/characters_gif/Sword And Shield Casting.gif",
thumbnail: "/resources/characters_gif/Sword And Shield Casting.webp",
media_type: "glb",
},
{
@@ -154,7 +154,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Breakdance Ready",
thumbnail: "/resources/characters_gif/Breakdance Ready.gif",
thumbnail: "/resources/characters_gif/Breakdance Ready.webp",
media_type: "glb",
},
{
@@ -163,7 +163,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Sitting Yell",
thumbnail: "/resources/characters_gif/Sitting Yell.gif",
thumbnail: "/resources/characters_gif/Sitting Yell.webp",
media_type: "glb",
},
{
@@ -172,7 +172,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Uppercut",
thumbnail: "/resources/characters_gif/Uppercut.gif",
thumbnail: "/resources/characters_gif/Uppercut.webp",
media_type: "glb",
},
{
@@ -181,7 +181,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Breakdance Uprock Var 1",
thumbnail: "/resources/characters_gif/Breakdance Uprock Var 1.gif",
thumbnail: "/resources/characters_gif/Breakdance Uprock Var 1.webp",
media_type: "glb",
},
{
@@ -190,7 +190,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Standard Walk",
thumbnail: "/resources/characters_gif/Standard Walk.gif",
thumbnail: "/resources/characters_gif/Standard Walk.webp",
media_type: "glb",
},
{
@@ -199,7 +199,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Sword And Shield Jump",
thumbnail: "/resources/characters_gif/Sword And Shield Jump.gif",
thumbnail: "/resources/characters_gif/Sword And Shield Jump.webp",
media_type: "glb",
},
{
@@ -208,7 +208,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Baseball Bunt",
thumbnail: "/resources/characters_gif/Baseball Bunt.gif",
thumbnail: "/resources/characters_gif/Baseball Bunt.webp",
media_type: "glb",
},
@@ -218,7 +218,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Sneaking Forward",
thumbnail: "/resources/characters_gif/Sneaking Forward.gif",
thumbnail: "/resources/characters_gif/Sneaking Forward.webp",
media_type: "glb",
},
{
@@ -227,7 +227,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Right Strafe Walking",
thumbnail: "/resources/characters_gif/Right Strafe Walking.gif",
thumbnail: "/resources/characters_gif/Right Strafe Walking.webp",
media_type: "glb",
},
{
@@ -236,7 +236,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Laying Breathless",
thumbnail: "/resources/characters_gif/Laying Breathless.gif",
thumbnail: "/resources/characters_gif/Laying Breathless.webp",
media_type: "glb",
},
{
@@ -245,7 +245,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Orc Walk",
thumbnail: "/resources/characters_gif/Orc Walk.gif",
thumbnail: "/resources/characters_gif/Orc Walk.webp",
media_type: "glb",
},
{
@@ -254,7 +254,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Run Look Back",
thumbnail: "/resources/characters_gif/Run Look Back.gif",
thumbnail: "/resources/characters_gif/Run Look Back.webp",
media_type: "glb",
},
{
@@ -263,7 +263,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Lengthy Head Nod",
thumbnail: "/resources/characters_gif/Lengthy Head Nod.gif",
thumbnail: "/resources/characters_gif/Lengthy Head Nod.webp",
media_type: "glb",
},
{
@@ -272,7 +272,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Right Hook",
thumbnail: "/resources/characters_gif/Right Hook.gif",
thumbnail: "/resources/characters_gif/Right Hook.webp",
media_type: "glb",
},
{
@@ -281,7 +281,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Great Sword Impact",
thumbnail: "/resources/characters_gif/Great Sword Impact.gif",
thumbnail: "/resources/characters_gif/Great Sword Impact.webp",
media_type: "glb",
},
{
@@ -290,7 +290,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Zombie Biting",
thumbnail: "/resources/characters_gif/Zombie Biting.gif",
thumbnail: "/resources/characters_gif/Zombie Biting.webp",
media_type: "glb",
},
{
@@ -299,7 +299,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Leaning",
thumbnail: "/resources/characters_gif/Leaning.gif",
thumbnail: "/resources/characters_gif/Leaning.webp",
media_type: "glb",
},
{
@@ -308,7 +308,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Corkscrew Evade",
thumbnail: "/resources/characters_gif/Corkscrew Evade.gif",
thumbnail: "/resources/characters_gif/Corkscrew Evade.webp",
media_type: "glb",
},
{
@@ -317,7 +317,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Catwalk Walk Forward Crossed",
thumbnail: "/resources/characters_gif/Catwalk Walk Forward Crossed.gif",
thumbnail: "/resources/characters_gif/Catwalk Walk Forward Crossed.webp",
media_type: "glb",
},
{
@@ -326,7 +326,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Look Around",
thumbnail: "/resources/characters_gif/Look Around.gif",
thumbnail: "/resources/characters_gif/Look Around.webp",
media_type: "glb",
},
{
@@ -335,7 +335,7 @@ export const demoAnimationItems: MediaItem[] = [
type: AssetType.ANIMATION,
length: 100,
name: "Convulsing",
thumbnail: "/resources/characters_gif/Convulsing.gif",
thumbnail: "/resources/characters_gif/Convulsing.webp",
media_type: "glb",
},
];
@@ -14,14 +14,14 @@ export const demoSkyboxItems: MediaItem[] = [
media_id: "SKY::Default",
type: AssetType.SKYBOX,
name: "Day Skybox",
thumbnail: "/resources/skybox/day/px.png",
thumbnail: "/resources/skybox/day/px.webp",
},
{
version: 1,
media_id: "SKY::m_0",
type: AssetType.SKYBOX,
name: "Night Skybox",
thumbnail: "/resources/skybox/night/Night_Moon_Burst_Cam_2_LeftX.png",
thumbnail: "/resources/skybox/night/Night_Moon_Burst_Cam_2_LeftX.webp",
},
{
version: 1,
@@ -29,7 +29,7 @@ export const demoSkyboxItems: MediaItem[] = [
type: AssetType.SKYBOX,
name: "Gray Skybox",
thumbnail:
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_2_LeftX.png",
"/resources/skybox/gray/Sky_AllSky_Overcast4_Low_Cam_2_LeftX.webp",
},
{
version: 1,

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 232 KiB

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Before

Width:  |  Height:  |  Size: 575 KiB

After

Width:  |  Height:  |  Size: 575 KiB

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 336 KiB

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Some files were not shown because too many files have changed in this diff Show More