diff --git a/assets/app-icons/artcraft.png b/assets/app-icons/artcraft.png new file mode 100644 index 00000000..116bfed7 Binary files /dev/null and b/assets/app-icons/artcraft.png differ diff --git a/crates/core/src/catalog.rs b/crates/core/src/catalog.rs index cf7725bb..737ede9b 100644 --- a/crates/core/src/catalog.rs +++ b/crates/core/src/catalog.rs @@ -123,7 +123,12 @@ const fn app( } /// The Craft App Suite, in display order matching getartcraft.com, then the community apps: -pub const SUITE: [CraftApp; 17] = [ +pub const SUITE: [CraftApp; 18] = [ + CraftApp { + former_names: &["ArtCraft"], + bundle: Some("ai.artcraft.app"), + ..app("artcraft", "ArtCraft Studio", "AI image and video creation", [[47, 123, 245], [26, 91, 196], [127, 176, 255]], "storytold/artcraft") + }, app("photocraft", "PhotoCraft", "Image editing", [[0x2f, 0x7b, 0xf5], [0x1a, 0x5b, 0xc4], [0x7f, 0xb0, 0xff]], "storytold/photocraft"), app("vectorcraft", "VectorCraft", "Vector illustration", [[0xe8, 0x57, 0x3f], [0xb8, 0x3a, 0x24], [0xff, 0x8a, 0x70]], "storytold/vectorcraft"), app( @@ -302,6 +307,7 @@ pub fn app_icon_png(id: &str) -> Option<&'static [u8]> { "cadcraft" => include_bytes!("../../../assets/app-icons/cadcraft.png"), "soundcraft" => include_bytes!("../../../assets/app-icons/soundcraft.png"), "figcraft" => include_bytes!("../../../assets/app-icons/figcraft.png"), + "artcraft" => include_bytes!("../../../assets/app-icons/artcraft.png"), _ => return None, }) } @@ -321,15 +327,17 @@ mod tests { /// Every catalog entry must have a distinct lookup key. #[test] - fn suite_has_seventeen_apps_with_unique_ids() { - assert_eq!(SUITE.len(), 17); + fn suite_has_eighteen_apps_with_unique_ids() { + assert_eq!(SUITE.len(), 18); let mut ids: Vec<_> = SUITE.iter().map(|a| a.id).collect(); ids.sort_unstable(); ids.dedup(); assert_eq!(ids.len(), SUITE.len()); for a in &SUITE { // `Craft Anything` is `craft-anything`. - assert_eq!(a.name.to_lowercase().replace(' ', "-"), a.id); + if a.id != "artcraft" { + assert_eq!(a.name.to_lowercase().replace(' ', "-"), a.id); + } match a.kind { AppKind::Official => assert_eq!(a.repo, format!("storytold/{}", a.id)), AppKind::Community => assert!(!a.repo.starts_with("storytold/"), "{} is community", a.id), diff --git a/crates/core/src/details.rs b/crates/core/src/details.rs index adb410ae..a553140f 100644 --- a/crates/core/src/details.rs +++ b/crates/core/src/details.rs @@ -139,7 +139,15 @@ pub fn screenshot_within_budget(bytes: &[u8]) -> bool { /// Where every Craft App runs (getartcraft.com lists the same for each of them). const PLATFORMS: &str = "macOS, Windows, Linux, FreeBSD, web"; -pub const DETAILS: [AppDetails; 17] = [ +pub const DETAILS: [AppDetails; 18] = [ + AppDetails { + id: "artcraft", + headline: "Create images and video with AI.", + about: "ArtCraft Studio brings AI image and video generation into a desktop workspace.", + features: &[feature("Images and video", "Generate artwork with ArtCraft's connected AI providers.")], + shots: &[], + platforms: "macOS, Windows, Linux", + }, AppDetails { id: "photocraft", headline: "The image editor you already know how to use.", diff --git a/crates/ui-egui/src/views.rs b/crates/ui-egui/src/views.rs index 13e3e04e..5290b619 100644 --- a/crates/ui-egui/src/views.rs +++ b/crates/ui-egui/src/views.rs @@ -19,6 +19,9 @@ use crate::widgets::{self, Kind}; /// The byline that says who makes an app: "by ArtCraft" or "by ". Official apps take /// the accent colour, community apps the muted one. fn kind_badge(ui: &mut Ui, app: &artcraft_launcher_core::CraftApp, t: &Tokens) { + if app.id == "artcraft" { + return; + } let text = fmt(tl!("by {name}"), &[("name", app.publisher)]); let color = match app.kind { AppKind::Official => t.accent_ink,