collective

Browse / github.com/storytold/craft-launcher / patch

feat(windows): add "Add desktop shortcut" to the app ⋯ menu

feat(windows): add "Add desktop shortcut" to the app ⋯ menu ## What Adds **Add desktop shortcut** to the ⋯ menu for installed apps on Windows. ## Why Some people want a desktop icon for a Craft app. This lets them create one from the launcher when they want it. ## How - On Windows, each installed app’s ⋯ menu gets a new item: **Add desktop shortcut** - Clicking it puts a shortcut on the Desktop that opens that app - The option only shows on Windows, and only for apps that are already installed - Other platforms (macOS, Linux, web) are unchanged <img width="1392" height="940" alt="Screenshot 2026-10-26 013826" src="https://github.com/user

Status
open
Score
0.00
Adopters
0 other operators
Author
yugisch (imported from pull request #897, open)
Base
f0cb3b0439d15cae02d806e5ddc0132fd487c89c
Size
+103 −4 in 15 files
Review
pending
Shared
2026-10-28
Id
7bc9b2f4984beff63d99cdfc

How it went

No other operator has reported using it yet.

Diff

raw · apps/artcraft-launcher-web/src/web.rs crates/core/src/protocol.rs crates/native/src/backend.rs crates/native/src/process.rs crates/ui-egui/src/i18n/cs.tsv crates/ui-egui/src/i18n/de.tsv crates/ui-egui/src/i18n/es.tsv crates/ui-egui/src/i18n/fr.tsv crates/ui-egui/src/i18n/hu.tsv crates/ui-egui/src/i18n/ja.tsv crates/ui-egui/src/i18n/pl.tsv crates/ui-egui/src/i18n/pt-BR.tsv crates/ui-egui/src/i18n/ru.tsv crates/ui-egui/src/i18n/zh-CN.tsv crates/ui-egui/src/views.rs

diff --git a/apps/artcraft-launcher-web/src/web.rs b/apps/artcraft-launcher-web/src/web.rs--- a/apps/artcraft-launcher-web/src/web.rs+++ b/apps/artcraft-launcher-web/src/web.rs@@ -320,7 +320,12 @@ impl Backend for WebBackend {     }      fn capabilities(&self) -> Capabilities {-        Capabilities { manage_apps: false, reveal_label: "", self_update: false }+        Capabilities {+            manage_apps: false,+            reveal_label: "",+            self_update: false,+            desktop_shortcuts: false,+        }     }      fn platform(&self) -> Option<Platform> {diff --git a/crates/core/src/protocol.rs b/crates/core/src/protocol.rs--- a/crates/core/src/protocol.rs+++ b/crates/core/src/protocol.rs@@ -39,6 +39,10 @@ pub enum Command {     Reveal {         app: String,     },+    /// Create a desktop shortcut for an installed app (Windows).+    CreateDesktopShortcut {+        app: String,+    },     /// Pause a running download, keeping what's downloaded so far (`Install` resumes it).     Pause {         app: String,@@ -250,6 +254,8 @@ pub struct Capabilities {     /// instead. A backend may only know this once it has found a newer launcher, so the UI reads     /// it again on [`Event::LauncherUpdate`].     pub self_update: bool,+    /// Offer "Add desktop shortcut" for installed apps (Windows desktop only).+    pub desktop_shortcuts: bool, }  /// The UI's view of a backend.diff --git a/crates/native/src/backend.rs b/crates/native/src/backend.rs--- a/crates/native/src/backend.rs+++ b/crates/native/src/backend.rs@@ -1082,6 +1082,22 @@ impl Backend for NativeBackend {                     }                 }             }+            Command::CreateDesktopShortcut { app } => {+                let Some(def) = CraftApp::by_id(&app) else { return };+                let result = match self.installed(&app) {+                    Some(inst) => {+                        let path = std::path::Path::new(&inst.location);+                        crate::process::create_desktop_shortcut(def.name, path)+                    }+                    None => Err(format!("{} isn't installed", def.name)),+                };+                if let Err(e) = result {+                    self.shared.emit(Event::JobFinished {+                        app,+                        result: Err(format!("Couldn't create the desktop shortcut: {e}")),+                    });+                }+            }             Command::UpdateLauncher => self.update_launcher(),             Command::FetchScreenshots { app } => self.fetch_screenshots(&app),             Command::Configure(options) => {@@ -1113,7 +1129,12 @@ impl Backend for NativeBackend {             Some(Os::Windows) => "Show in Explorer",             _ => "Show in Files",         };-        Capabilities { manage_apps: true, reveal_label, self_update: self.shared.self_install.get().is_some_and(Option::is_some) }+        Capabilities {+            manage_apps: true,+            reveal_label,+            self_update: self.shared.self_install.get().is_some_and(Option::is_some),+            desktop_shortcuts: matches!(self.platform.map(|p| p.os), Some(Os::Windows)),+        }     }      fn platform(&self) -> Option<Platform> {diff --git a/crates/native/src/process.rs b/crates/native/src/process.rs--- a/crates/native/src/process.rs+++ b/crates/native/src/process.rs@@ -433,6 +433,49 @@ fn explorer_select_arg(path: &Path) -> Option<std::ffi::OsString> {     Some(arg) } +/// Creates a `.lnk` on the user's Desktop that opens `target`.+///+/// Windows only. `name` is the shortcut's file name without `.lnk` (usually the app's display name).+#[cfg(windows)]+pub fn create_desktop_shortcut(name: &str, target: &Path) -> Result<(), String> {+    if !target.exists() {+        return Err(format!("{} isn't where it was installed", target.display()));+    }+    let desktop = dirs::desktop_dir().ok_or_else(|| "couldn't find the Desktop folder".to_string())?;+    let lnk = desktop.join(format!("{name}.lnk"));+    let target = dunce::canonicalize(target).unwrap_or_else(|_| target.to_path_buf());+    let work_dir = target.parent().map(Path::to_path_buf).unwrap_or_else(|| PathBuf::from("."));++    let target_s = target.to_str().ok_or_else(|| "path isn't valid Unicode".to_string())?;+    let lnk_s = lnk.to_str().ok_or_else(|| "path isn't valid Unicode".to_string())?;+    let dir_s = work_dir.to_str().ok_or_else(|| "path isn't valid Unicode".to_string())?;++    // Single-quote escape for PowerShell string literals.+    let esc = |s: &str| s.replace('\'', "''");+    let script = format!(+        "$s = (New-Object -ComObject WScript.Shell).CreateShortcut('{}'); \+         $s.TargetPath = '{}'; \+         $s.WorkingDirectory = '{}'; \+         $s.Save()",+        esc(lnk_s),+        esc(target_s),+        esc(dir_s),+    );++    let mut cmd = command("powershell.exe");+    cmd.args(["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", &script]);+    run_command(cmd, "powershell", Duration::from_secs(20)).map(|_| ())?;+    if !lnk.exists() {+        return Err("the shortcut was not created".into());+    }+    Ok(())+}++#[cfg(not(windows))]+pub fn create_desktop_shortcut(_name: &str, _target: &Path) -> Result<(), String> {+    Err("desktop shortcuts are only supported on Windows".into())+}+ /// Whether a process started from inside `path` (an exe, an AppImage, or a `.app`) is running. pub fn is_running(path: &Path) -> bool {     if cfg!(windows) {diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv--- a/crates/ui-egui/src/i18n/cs.tsv+++ b/crates/ui-egui/src/i18n/cs.tsv@@ -58,6 +58,7 @@ 	Show in Explorer	Zobrazit v Průzkumníku 	Show in Files	Zobrazit v Souborech 	Reinstall	Znovu nainstalovat+	Add desktop shortcut	Přidat zástupce na plochu 	Uninstall…	Odinstalovat… 	All releases	Všechna vydání 	ArtCraft Launcher {version} is available.	Je k dispozici ArtCraft Launcher {version}.@@ -244,6 +245,7 @@ column	Latest	Nejnovější column	Released	Vydáno filter	Installed	Nainstalováno 	App details	Podrobnosti o aplikaci+	More actions for {name}	Další akce pro {name} 	Close (Esc)	Zavřít (Esc) 	Features	Funkce 	Latest release	Nejnovější verzediff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv--- a/crates/ui-egui/src/i18n/de.tsv+++ b/crates/ui-egui/src/i18n/de.tsv@@ -71,6 +71,7 @@ 	Show in Explorer	Im Explorer zeigen 	Show in Files	Im Dateimanager zeigen 	Reinstall	Neu installieren+	Add desktop shortcut	Desktop-Verknüpfung hinzufügen 	Uninstall…	Deinstallieren… 	All releases	Alle Versionen 	ArtCraft Launcher {version} is available.	ArtCraft Launcher {version} ist verfügbar.@@ -340,6 +341,7 @@ filter	Installed	Installiert 	ArtCraft on GitHub	ArtCraft auf GitHub 	Join the ArtCraft Discord	Dem ArtCraft-Discord beitreten 	App details	App-Details+	More actions for {name}	Weitere Aktionen für {name} 	Close (Esc)	Schließen (Esc) 	Features	Funktionen 	Latest release	Neueste Versiondiff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv--- a/crates/ui-egui/src/i18n/es.tsv+++ b/crates/ui-egui/src/i18n/es.tsv@@ -71,6 +71,7 @@ 	Show in Explorer	Mostrar en el Explorador 	Show in Files	Mostrar en Archivos 	Reinstall	Reinstalar+	Add desktop shortcut	Añadir acceso directo al escritorio 	Uninstall…	Desinstalar… 	All releases	Todas las versiones 	ArtCraft Launcher {version} is available.	ArtCraft Launcher {version} está disponible.@@ -257,6 +258,7 @@ column	Latest	Última column	Released	Publicada filter	Installed	Instaladas 	App details	Detalles de la app+	More actions for {name}	Más acciones para {name} 	Close (Esc)	Cerrar (Esc) 	Features	Funciones 	Latest release	Última versióndiff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv--- a/crates/ui-egui/src/i18n/fr.tsv+++ b/crates/ui-egui/src/i18n/fr.tsv@@ -69,6 +69,7 @@ 	Show in Explorer	Afficher dans l'Explorateur 	Show in Files	Afficher dans Fichiers 	Reinstall	Réinstaller+	Add desktop shortcut	Ajouter un raccourci bureau 	Uninstall…	Désinstaller… 	All releases	Toutes les versions 	ArtCraft Launcher {version} is available.	ArtCraft Launcher {version} est disponible.@@ -338,6 +339,7 @@ filter	Installed	Installées 	ArtCraft on GitHub	ArtCraft sur GitHub 	Join the ArtCraft Discord	Rejoindre le Discord d'ArtCraft 	App details	Détails de l'application+	More actions for {name}	Autres actions pour {name} 	Close (Esc)	Fermer (Esc) 	Features	Fonctionnalités 	Latest release	Dernière versiondiff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv--- a/crates/ui-egui/src/i18n/hu.tsv+++ b/crates/ui-egui/src/i18n/hu.tsv@@ -71,6 +71,7 @@ 	Show in Explorer	Megjelenítés a Fájlkezelőben 	Show in Files	Megjelenítés a fájlkezelőben 	Reinstall	Újratelepítés+	Add desktop shortcut	Asztali parancsikon hozzáadása 	Uninstall…	Eltávolítás… 	All releases	Minden kiadás 	ArtCraft Launcher {version} is available.	Elérhető az ArtCraft Launcher új verziója: {version}.@@ -268,6 +269,7 @@ column	Released	Dátum # Contextual: the filter tab names a group (the status pill says "Installed") filter	Installed	Telepítve 	App details	Alkalmazás részletei+	More actions for {name}	További műveletek: {name} 	Close (Esc)	Bezárás (Esc) 	Features	Funkciók 	Latest release	Legújabb kiadásdiff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv--- a/crates/ui-egui/src/i18n/ja.tsv+++ b/crates/ui-egui/src/i18n/ja.tsv@@ -73,6 +73,7 @@ 	Show in Explorer	エクスプローラーで表示 	Show in Files	ファイルマネージャーで表示 	Reinstall	再インストール+	Add desktop shortcut	デスクトップにショートカットを追加 	Uninstall…	アンインストール… 	All releases	すべてのリリース 	ArtCraft Launcher {version} is available.	ArtCraft Launcher {version} が利用可能です。@@ -270,6 +271,7 @@ column	Released	公開日 # Contextual: the filter tab names a group (the status pill says "Installed") filter	Installed	インストール済み 	App details	アプリの詳細+	More actions for {name}	{name} のその他の操作 	Close (Esc)	閉じる (Esc) 	Features	機能 	Latest release	最新リリースdiff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv--- a/crates/ui-egui/src/i18n/pl.tsv+++ b/crates/ui-egui/src/i18n/pl.tsv@@ -72,6 +72,7 @@ 	Show in Explorer	Pokaż w Eksploratorze plików 	Show in Files	Pokaż w menedżerze plików 	Reinstall	Przeinstaluj+	Add desktop shortcut	Dodaj skrót na pulpicie 	Uninstall…	Odinstaluj… 	All releases	Wszystkie wydania 	ArtCraft Launcher {version} is available.	Dostępna jest nowa wersja ArtCraft Launcher ({version}).@@ -269,6 +270,7 @@ column	Released	Data wydania # Contextual: the filter tab names a group (the status pill says "Installed") filter	Installed	Zainstalowane 	App details	Szczegóły aplikacji+	More actions for {name}	Więcej działań: {name} 	Close (Esc)	Zamknij (Esc) 	Features	Funkcje 	Latest release	Najnowsze wydaniediff --git a/crates/ui-egui/src/i18n/pt-BR.tsv b/crates/ui-egui/src/i18n/pt-BR.tsv--- a/crates/ui-egui/src/i18n/pt-BR.tsv+++ b/crates/ui-egui/src/i18n/pt-BR.tsv@@ -59,6 +59,7 @@ 	Show in Explorer	Mostrar no Explorer 	Show in Files	Mostrar no gerenciador de arquivos 	Reinstall	Reinstalar+	Add desktop shortcut	Adicionar atalho na área de trabalho 	Uninstall…	Desinstalar… 	All releases	Todos os lançamentos 	ArtCraft Launcher {version} is available.	ArtCraft Launcher {version} está disponível.@@ -328,6 +329,7 @@ filter	Installed	Instalados 	ArtCraft on GitHub	ArtCraft no GitHub 	Join the ArtCraft Discord	Entrar no Discord da ArtCraft 	App details	Detalhes do aplicativo+	More actions for {name}	Mais ações para {name} 	Close (Esc)	Fechar (Esc) 	Features	Recursos 	Latest release	Último lançamentodiff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv--- a/crates/ui-egui/src/i18n/ru.tsv+++ b/crates/ui-egui/src/i18n/ru.tsv@@ -69,6 +69,7 @@ 	Show in Explorer	Показать в Проводнике 	Show in Files	Показать в файловом менеджере 	Reinstall	Переустановить+	Add desktop shortcut	Добавить ярлык на рабочий стол 	Uninstall…	Удалить… 	All releases	Все релизы 	ArtCraft Launcher {version} is available.	Доступен ArtCraft Launcher {version}.@@ -336,6 +337,7 @@ filter	Installed	Установлены 	ArtCraft on GitHub	ArtCraft на GitHub 	Join the ArtCraft Discord	Присоединиться к Discord ArtCraft 	App details	О приложении+	More actions for {name}	Другие действия для {name} 	Close (Esc)	Закрыть (Esc) 	Features	Возможности 	Latest release	Последний релизdiff --git a/crates/ui-egui/src/i18n/zh-CN.tsv b/crates/ui-egui/src/i18n/zh-CN.tsv--- a/crates/ui-egui/src/i18n/zh-CN.tsv+++ b/crates/ui-egui/src/i18n/zh-CN.tsv@@ -53,6 +53,7 @@ 	Discard download	丢弃下载 	Release notes	更新说明 	Reinstall	重新安装+	Add desktop shortcut	添加桌面快捷方式 	Uninstall…	卸载… 	All releases	所有版本 	ArtCraft Launcher {version} is available.	ArtCraft Launcher {version} 已可更新。@@ -295,6 +296,7 @@ figcraft	Interface design and prototyping	界面设计与原型制作 	Flatpak bundle	Flatpak 包 	Tarball (.tar.gz)	压缩包 (.tar.gz) 	App details	应用详情+	More actions for {name}	{name} 的更多操作 	Close (Esc)	关闭(Esc) 	Features	功能特性 	Latest release	最新发布diff --git a/crates/ui-egui/src/views.rs b/crates/ui-egui/src/views.rs--- a/crates/ui-egui/src/views.rs+++ b/crates/ui-egui/src/views.rs@@ -991,8 +991,8 @@ impl LauncherUi {         });     } -    /// The menu items for an installed app: open, reveal in the file manager, reinstall, and-    /// uninstall (which asks first).+    /// The menu items for an installed app: open, reveal in the file manager, desktop shortcut,+    /// reinstall, and uninstall (which asks first).     fn menu_installed(&mut self, ui: &mut Ui, status: &AppStatus, id: &str, reveal: &'static str) {         if ui.button(tl!("Open")).clicked() {             self.launch_app(id);@@ -1002,6 +1002,10 @@ impl LauncherUi {             self.send(Command::Reveal { app: id.to_string() });             ui.close();         }+        if self.caps.desktop_shortcuts && ui.button(tl!("Add desktop shortcut")).clicked() {+            self.send(Command::CreateDesktopShortcut { app: id.to_string() });+            ui.close();+        }         if *status == AppStatus::UpToDate && ui.button(tl!("Reinstall")).clicked() {             self.install_app(id);             ui.close();