Windows installer: choose the install folder, optional desktop shortcut; 0.1.2 (#26)

The setup wizard gains a folder page between Welcome and Ready: pick where
ArtCraft Launcher goes (default %LOCALAPPDATA%\Programs\ArtCraft Launcher;
still per user, so it notes to pick a folder you can write to), and a
"Create a desktop shortcut" box. The page is the stock WixUI_InstallDir one
plus the box, with the same path checks and Change... browser.

Both choices are recorded under HKCU and reused by the next version, so an
update (self-update runs the MSI with /passive) installs into the same folder
and keeps the shortcut choice. The box starts ticked on a first install; an
update from 0.1.0 or 0.1.1, which never asked, adds no desktop icon. A value
given on the command line (INSTALLDESKTOPSHORTCUT=0/1) wins. The desktop
shortcut is a normal MSI component, removed on uninstall.

self_update.rs now recognises an MSI install by the folder the MSI records
(InstallDir), not only the default one, so a launcher installed elsewhere
still updates itself.

The Windows ARM64 workflow now installs twice on real hardware: with the
defaults (default folder, Start Menu and desktop shortcuts) and into a chosen
folder without the desktop shortcut, checking each uninstall removes it all.

Version 0.1.2.

Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
BFlatandClaude Opus 5.5 authored and GitHub committed 2026-10-10 18:34:06 +07:00
1 parent 019330f99a
commit 2f3b843ef0
7 files changed
+165 -48

No files matched your search

+43 -20
View File
@@ -1,7 +1,8 @@
# Windows on ARM: package the ARM64 release the way release.yml does (cross-compiled on an x64
# runner, unsigned here), then install that per-user MSI on a Windows 11 ARM64 runner, check the
# installed binary is ARM64 and the Start Menu shortcut exists, and uninstall. The launcher is a
# GUI app, so it isn't run here.
# runner, unsigned here), then install that per-user MSI on a Windows 11 ARM64 runner: once with
# the defaults (default folder, Start Menu and desktop shortcuts) and once into a chosen folder
# without the desktop shortcut, checking the binary is ARM64 and that each uninstall removes
# everything. The launcher is a GUI app, so it isn't run here.
name: Windows ARM64
# Nightly on main (dependency and runner-image drift shows up within a day), and on PRs that
@@ -71,21 +72,43 @@ jobs:
run: |
$ErrorActionPreference = 'Stop'
$msi = (Get-ChildItem dist/*.msi | Select-Object -First 1).FullName
$p = Start-Process msiexec.exe -ArgumentList '/i', "`"$msi`"", '/qn', '/l*v', 'install.log' -Wait -PassThru
if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 60; throw "msiexec /i exited $($p.ExitCode)" }
# Per-user install: %LOCALAPPDATA%\Programs\ArtCraft Launcher.
$dir = Join-Path $env:LOCALAPPDATA 'Programs\ArtCraft Launcher'
$path = Join-Path $dir 'artcraft-launcher.exe'
if (-not (Test-Path $path)) { Get-Content install.log -Tail 60; throw "artcraft-launcher.exe not installed in $dir" }
$bytes = [System.IO.File]::ReadAllBytes($path)
$startLnk = Join-Path ([Environment]::GetFolderPath('Programs')) 'ArtCraft Launcher.lnk'
$deskLnk = Join-Path ([Environment]::GetFolderPath('Desktop')) 'ArtCraft Launcher.lnk'
$key = 'HKCU:\Software\Learning Machines LLC\ArtCraft Launcher'
function Install([string[]] $props) {
$p = Start-Process msiexec.exe -ArgumentList (@('/i', "`"$msi`"", '/qn', '/l*v', 'install.log') + $props) -Wait -PassThru
if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 60; throw "msiexec /i exited $($p.ExitCode)" }
}
function Uninstall([string] $exe) {
$p = Start-Process msiexec.exe -ArgumentList '/x', "`"$msi`"", '/qn' -Wait -PassThru
if ($p.ExitCode -ne 0) { throw "msiexec /x exited $($p.ExitCode)" }
foreach ($left in $exe, $startLnk, $deskLnk) { if (Test-Path $left) { throw "uninstall left $left behind" } }
if ((Get-ItemProperty $key -ErrorAction SilentlyContinue).InstallDir) { throw "uninstall left InstallDir in $key" }
Write-Output 'ok uninstalled'
}
function Expect([bool] $ok, [string] $what) { if (-not $ok) { Get-Content install.log -Tail 60; throw $what }; Write-Output "ok $what" }
# 1. A first install with the defaults, as the wizard's Next, Next, Install does:
# %LOCALAPPDATA%\Programs\ArtCraft Launcher, with the Start Menu and desktop shortcuts.
Install @()
$exe = Join-Path $env:LOCALAPPDATA 'Programs\ArtCraft Launcher\artcraft-launcher.exe'
Expect (Test-Path $exe) "installed in the default folder: $exe"
$bytes = [System.IO.File]::ReadAllBytes($exe)
$machine = [BitConverter]::ToUInt16($bytes, [BitConverter]::ToInt32($bytes, 0x3C) + 4)
if ($machine -ne 0xAA64) { throw "artcraft-launcher.exe is for machine 0x$('{0:X}' -f $machine), not ARM64" }
Write-Output "ok $path (ARM64)"
$lnk = Join-Path ([Environment]::GetFolderPath('Programs')) 'ArtCraft Launcher.lnk'
if (-not (Test-Path $lnk)) { throw "Start Menu shortcut missing: $lnk" }
Write-Output "ok $lnk"
$p = Start-Process msiexec.exe -ArgumentList '/x', "`"$msi`"", '/qn' -Wait -PassThru
if ($p.ExitCode -ne 0) { throw "msiexec /x exited $($p.ExitCode)" }
if (Test-Path $path) { throw 'uninstall left artcraft-launcher.exe behind' }
if (Test-Path $lnk) { throw 'uninstall left the Start Menu shortcut behind' }
Write-Output 'ok uninstalled'
Expect ($machine -eq 0xAA64) "the exe is ARM64 (machine 0x$('{0:X}' -f $machine))"
Expect (Test-Path $startLnk) "Start Menu shortcut: $startLnk"
Expect (Test-Path $deskLnk) "desktop shortcut (ticked by default): $deskLnk"
Expect ((Get-ItemProperty $key).DesktopShortcutChoice -eq '1') 'the desktop-shortcut choice is recorded as 1'
Uninstall $exe
# 2. A folder of the user's choosing, without the desktop shortcut.
$dir = Join-Path $env:LOCALAPPDATA 'ArtCraft Launcher Test Folder'
Install @("INSTALLFOLDER=`"$dir\`"", 'INSTALLDESKTOPSHORTCUT=0')
$exe = Join-Path $dir 'artcraft-launcher.exe'
Expect (Test-Path $exe) "installed in the chosen folder: $exe"
Expect ((Get-ItemProperty $key).InstallDir.TrimEnd('\') -eq $dir) 'the chosen folder is recorded for updates and self-update'
Expect (Test-Path $startLnk) "Start Menu shortcut: $startLnk"
Expect (-not (Test-Path $deskLnk)) 'no desktop shortcut when it is not wanted'
Expect ((Get-ItemProperty $key).DesktopShortcutChoice -eq '0') 'the desktop-shortcut choice is recorded as 0'
Uninstall $exe
Expect (-not (Test-Path $dir)) 'uninstall removed the chosen folder'
+9 -5
View File
@@ -116,11 +116,15 @@ Release assets (keep these names exact; the launcher's own self-update and the R
Packaging facts the code relies on:
- The Windows MSI is **per-user** (`%LOCALAPPDATA%\Programs\ArtCraft Launcher`, no UAC prompt).
Double-clicked it opens a WixUI setup wizard (Welcome, Ready, progress, Finish with "Launch"),
whose art `package.ps1` draws at build time; self-update runs it with `/passive`, no wizard.
Its Start Menu shortcut sets AppUserModelID `ai.storyteller.artcraft-launcher`, which toast
notifications need. `portable.txt` beside the exe switches on portable mode
- The Windows MSI is **per-user** (no UAC prompt), by default into
`%LOCALAPPDATA%\Programs\ArtCraft Launcher`. Double-clicked it opens a WixUI setup wizard
(Welcome; a folder page with a "Create a desktop shortcut" box; Ready; progress; Finish with
"Launch"), whose art `package.ps1` draws at build time; self-update runs it with `/passive`, no
wizard. The folder and the shortcut choice are recorded under
`HKCU\Software\Learning Machines LLC\ArtCraft Launcher` (`InstallDir`,
`DesktopShortcutChoice`) and reused by the next version, and `self_update.rs` reads `InstallDir`
to know the running copy came from the MSI. Its shortcuts set AppUserModelID
`ai.storyteller.artcraft-launcher`, which toast notifications need. `portable.txt` beside the exe switches on portable mode
(`ArtCraftLauncherData\` next to the exe).
- The macOS bundle is `ArtCraft Launcher.app` (executable `Contents/MacOS/ArtCraft Launcher`), a
normal Dock app that can also sit in the menu bar: `LSUIElement` must never be set.
Generated
+6 -6
View File
@@ -276,7 +276,7 @@ checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "artcraft-launcher"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"artcraft-launcher-core",
"artcraft-launcher-native",
@@ -296,7 +296,7 @@ dependencies = [
[[package]]
name = "artcraft-launcher-core"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"semver",
"serde",
@@ -305,7 +305,7 @@ dependencies = [
[[package]]
name = "artcraft-launcher-native"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"artcraft-launcher-core",
"dirs 6.0.0",
@@ -324,7 +324,7 @@ dependencies = [
[[package]]
name = "artcraft-launcher-ui-egui"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"artcraft-launcher-core",
"egui",
@@ -337,7 +337,7 @@ dependencies = [
[[package]]
name = "artcraft-launcher-web"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"artcraft-launcher-core",
"artcraft-launcher-ui-egui",
@@ -5749,7 +5749,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "xtask"
version = "0.1.1"
version = "0.1.2"
[[package]]
name = "yoke"
+1 -1
View File
@@ -3,7 +3,7 @@ resolver = "3"
members = ["crates/*", "apps/*", "xtask"]
[workspace.package]
version = "0.1.1"
version = "0.1.2"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.90"
+18 -5
View File
@@ -9,7 +9,7 @@
//! | Installed as | Swap |
//! |---|---|
//! | macOS `.app` | copy the new bundle from the DMG over this one (fine while running), reopen it |
//! | Windows MSI (per-user) | after exit, run the new MSI, then start the exe |
//! | Windows MSI (per-user, any folder) | after exit, run the new MSI (it reinstalls into the same folder), then start the exe |
//! | Windows portable zip | after exit, unzip over this folder (settings in `ArtCraftLauncherData` stay) |
//! | Linux AppImage | rename the new AppImage over this one (fine while running), start it |
//! | Linux `.deb` / `.rpm` | the system package manager, with the password prompt; then restart |
@@ -106,10 +106,23 @@ fn detect(exe: &Path) -> Option<Install> {
if dir.join("portable.txt").is_file() {
return Some(Install::Portable(exe.to_path_buf()));
}
// The MSI installs per user into %LOCALAPPDATA%\Programs\ArtCraft Launcher.
let programs = dirs::data_local_dir()?.join("Programs").join(LAUNCHER.name);
let programs = programs.canonicalize().unwrap_or(programs);
(dir == programs).then(|| Install::Msi(exe.to_path_buf()))
// The MSI installs per user, into the folder chosen in its wizard (which it records in the
// registry) or by default %LOCALAPPDATA%\Programs\ArtCraft Launcher.
let canonical = |p: PathBuf| p.canonicalize().unwrap_or(p);
let recorded = msi_install_dir().map(canonical);
let default = dirs::data_local_dir().map(|d| canonical(d.join("Programs").join(LAUNCHER.name)));
[recorded, default].into_iter().flatten().any(|d| d == dir).then(|| Install::Msi(exe.to_path_buf()))
}
/// The folder the MSI installed into, as it records it (`packaging/windows/artcraft-launcher.wxs`).
#[cfg(windows)]
fn msi_install_dir() -> Option<PathBuf> {
use winreg::RegKey;
use winreg::enums::HKEY_CURRENT_USER;
let key = RegKey::predef(HKEY_CURRENT_USER).open_subkey(r"Software\Learning Machines LLC\ArtCraft Launcher").ok()?;
let dir: String = key.get_value("InstallDir").ok()?;
let dir = dir.trim();
(!dir.is_empty()).then(|| PathBuf::from(dir))
}
#[cfg(target_os = "linux")]
@@ -7,6 +7,11 @@
<WixLocalization Culture="en-US" xmlns="http://wixtoolset.org/schemas/v4/wxl">
<String Id="WelcomeDlgTitle" Value="{\WixUI_Font_Bigger}Welcome to [ProductName] [ArtCraftLauncherVersion]" />
<String Id="WelcomeDlgDescription" Value="ArtCraft Launcher installs, updates and opens the Craft App Suite: PhotoCraft, VectorCraft, FilmCraft and the rest of the open-source ArtCraft apps.&#13;&#10;&#13;&#10;It installs for you only, so no administrator rights are needed. Click Next to continue." />
<!-- The folder page (ArtCraftInstallDirDlg). -->
<String Id="InstallDirDlgTitle" Value="{\WixUI_Font_Title}Choose where to install" />
<String Id="InstallDirDlgDescription" Value="Click Next to install to the default folder, or click Change to pick another." />
<String Id="ArtCraftInstallDirNote" Value="ArtCraft Launcher installs for you only, without administrator rights, so pick a folder you can write to (not Program Files). Updates install into the same folder." />
<String Id="ArtCraftDesktopShortcut" Value="Create a desktop shortcut" />
<String Id="VerifyReadyDlgInstallTitle" Value="{\WixUI_Font_Title}Ready to install [ProductName] [ArtCraftLauncherVersion]" />
<!-- Also shown after a repair or removal from the same wizard, so it doesn't say "installed". -->
<String Id="ExitDialogTitle" Value="{\WixUI_Font_Bigger}Completed the [ProductName] [ArtCraftLauncherVersion] Setup Wizard" />
+83 -11
View File
@@ -8,15 +8,18 @@
-culture en-US -loc artcraft-launcher.en-us.wxl -d Version=1.2.3 -d DisplayVersion=1.2.3-rc.1
-d BinDir=... -d IconPath=... -d BannerBmp=... -d DialogBmp=... -o out.msi
Per-user install (no UAC prompt) into %LOCALAPPDATA%\Programs\ArtCraft Launcher, so the
launcher can update itself without elevation, plus a Start Menu shortcut. The shortcut carries
the AppUserModelID ai.storyteller.artcraft-launcher, which Windows needs to show toast
Per-user install (no UAC prompt), by default into %LOCALAPPDATA%\Programs\ArtCraft Launcher, so
the launcher can update itself without elevation, plus a Start Menu shortcut. The shortcut
carries the AppUserModelID ai.storyteller.artcraft-launcher, which Windows needs to show toast
notifications from an unpackaged app and to group the taskbar button. No file associations.
Double-clicked, it opens a setup wizard that names the version: Welcome, Ready, a progress page
and a Finish page with a ticked "Launch ArtCraft Launcher" box. There is no licence page (the
code is MIT OR Apache-2.0; nothing to accept) and no folder page (self-update needs the per-user
folder). The launcher's own self-update runs it with /passive, which skips the wizard.
Double-clicked, it opens a setup wizard that names the version: Welcome; a folder page to pick
where it goes, with a "Create a desktop shortcut" box; Ready; a progress page; and a Finish page
with a ticked "Launch ArtCraft Launcher" box. There is no licence page (the code is MIT OR
Apache-2.0; nothing to accept). The install folder and the desktop-shortcut choice are recorded
under HKCU and reused by later versions, so an update (the launcher's own self-update runs this
with /passive, which skips the wizard) lands in the same folder and keeps the user's choice; the
launcher reads the recorded folder to know it was installed by this MSI.
Per-user packages can't use a file as a component's KeyPath (ICE38), so each component is
keyed on an HKCU value, and the folders we create are removed on uninstall (ICE64).
@@ -51,19 +54,74 @@
</Directory>
</StandardDirectory>
<StandardDirectory Id="ProgramMenuFolder" />
<StandardDirectory Id="DesktopFolder" />
<!-- An update installs where the user put the previous version (recorded by
ArtCraftLauncherApp below; every version since 0.1.0 records it). -->
<Property Id="INSTALLFOLDER">
<RegistrySearch Id="PreviousInstallFolder" Root="HKCU" Key="Software\Learning Machines LLC\ArtCraft Launcher" Name="InstallDir" Type="raw" />
</Property>
<!-- The desktop shortcut: the folder page's box sets INSTALLDESKTOPSHORTCUT to 1. It starts
ticked on a first install; an update keeps what the user chose last time, and an update
from a version that never asked (0.1.0, 0.1.1) adds none. A value given on the command
line (msiexec ... INSTALLDESKTOPSHORTCUT=0 or 1) wins. -->
<Property Id="PREVIOUSDESKTOPSHORTCUT">
<RegistrySearch Id="PreviousDesktopShortcut" Root="HKCU" Key="Software\Learning Machines LLC\ArtCraft Launcher" Name="DesktopShortcutChoice" Type="raw" />
</Property>
<SetProperty Id="INSTALLDESKTOPSHORTCUT" Value="1" After="AppSearch" Sequence="first"
Condition="NOT INSTALLDESKTOPSHORTCUT AND ((NOT Installed AND NOT WIX_UPGRADE_DETECTED AND NOT PREVIOUSDESKTOPSHORTCUT) OR PREVIOUSDESKTOPSHORTCUT = &quot;1&quot;)" />
<!-- Recorded as 1 or 0 (an unticked box leaves the property empty), for the next update. -->
<SetProperty Id="DESKTOPSHORTCUTCHOICE" Action="RememberNoDesktopShortcut" Value="0" Before="InstallInitialize" Sequence="execute"
Condition="NOT (INSTALLDESKTOPSHORTCUT = &quot;1&quot;)" />
<SetProperty Id="DESKTOPSHORTCUTCHOICE" Action="RememberDesktopShortcut" Value="1" Before="InstallInitialize" Sequence="execute"
Condition="INSTALLDESKTOPSHORTCUT = &quot;1&quot;" />
<Feature Id="Main" Title="ArtCraft Launcher" Level="1">
<ComponentGroupRef Id="ArtCraftLauncherFiles" />
</Feature>
<!-- The setup wizard: WixUI_InstallDir without its licence and folder pages. A later
NewDialog on the same button wins. -->
<!-- The setup wizard: WixUI_InstallDir without its licence page, and with our folder page
(ArtCraftInstallDirDlg: the stock one plus the desktop-shortcut box) in place of the stock
one. A later NewDialog on the same button wins. -->
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER" />
<UI>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="2" Condition="NOT Installed" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="NOT Installed" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="ArtCraftInstallDirDlg" Order="2" Condition="NOT Installed" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="ArtCraftInstallDirDlg" Order="2" Condition="NOT Installed" />
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchArtCraftLauncher"
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 AND NOT Installed" />
<Dialog Id="ArtCraftInstallDirDlg" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
<!-- As WixUI_InstallDir does for its own folder page: take the folder, check it, go on. -->
<Publish Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Event="DoAction" Value="WixUIValidatePath_$(sys.BUILDARCHSHORT)" Order="2" Condition="NOT WIXUI_DONTVALIDATEPATH" />
<Publish Event="SpawnDialog" Value="InvalidDirDlg" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
<Publish Event="NewDialog" Value="VerifyReadyDlg" Order="4" Condition="WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID=&quot;1&quot;" />
</Control>
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)">
<Publish Event="NewDialog" Value="WelcomeDlg" />
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg" />
</Control>
<Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgDescription)" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgTitle)" />
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" />
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="FolderLabel" Type="Text" X="20" Y="60" Width="290" Height="30" NoPrefix="yes" Text="!(loc.InstallDirDlgFolderLabel)" />
<Control Id="Folder" Type="PathEdit" X="20" Y="100" Width="320" Height="18" Property="WIXUI_INSTALLDIR" Indirect="yes" />
<Control Id="ChangeFolder" Type="PushButton" X="20" Y="120" Width="56" Height="17" Text="!(loc.InstallDirDlgChange)">
<Publish Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Event="SpawnDialog" Value="BrowseDlg" Order="2" />
</Control>
<Control Id="PerUserNote" Type="Text" X="20" Y="146" Width="330" Height="24" NoPrefix="yes" Text="!(loc.ArtCraftInstallDirNote)" />
<Control Id="DesktopShortcut" Type="CheckBox" X="20" Y="186" Width="330" Height="17" Property="INSTALLDESKTOPSHORTCUT" CheckBoxValue="1" Text="!(loc.ArtCraftDesktopShortcut)" />
</Dialog>
</UI>
<!-- Our own art, drawn by package.ps1 from the ArtCraft mark and the Craft apps' icons. -->
<WixVariable Id="WixUIBannerBmp" Value="$(var.BannerBmp)" />
@@ -86,7 +144,9 @@
<ComponentGroup Id="ArtCraftLauncherFiles">
<Component Id="ArtCraftLauncherApp" Directory="INSTALLFOLDER" Guid="55DCC431-EBEF-4C11-BABB-6D78CE0ECF4E">
<File Id="ArtCraftLauncherExe" Source="$(var.BinDir)\artcraft-launcher.exe" KeyPath="no" />
<!-- Read back by the next version's installer and by the launcher (self_update.rs). -->
<RegistryValue Root="HKCU" Key="Software\Learning Machines LLC\ArtCraft Launcher" Name="InstallDir" Value="[INSTALLFOLDER]" Type="string" KeyPath="yes" />
<RegistryValue Root="HKCU" Key="Software\Learning Machines LLC\ArtCraft Launcher" Name="DesktopShortcutChoice" Value="[DESKTOPSHORTCUTCHOICE]" Type="string" />
<!-- Win+R "artcraft-launcher". -->
<RegistryValue Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\App Paths\artcraft-launcher.exe" Value="[#ArtCraftLauncherExe]" Type="string" />
<RemoveFolder Id="RemoveInstallFolder" Directory="INSTALLFOLDER" On="uninstall" />
@@ -102,6 +162,18 @@
</Shortcut>
<RegistryValue Root="HKCU" Key="Software\Learning Machines LLC\ArtCraft Launcher" Name="StartMenuShortcut" Value="1" Type="integer" KeyPath="yes" />
</Component>
<!-- Only when the folder page's box is ticked; removed on uninstall like everything else. -->
<Component Id="ArtCraftLauncherDesktopShortcut" Directory="DesktopFolder" Guid="CF06DCF3-BE5A-4A2B-B3BF-F553CC6E5D60"
Condition="INSTALLDESKTOPSHORTCUT = &quot;1&quot;">
<Shortcut Id="DesktopShortcut" Name="ArtCraft Launcher"
Description="Install and update the Craft App Suite"
Target="[#ArtCraftLauncherExe]" WorkingDirectory="INSTALLFOLDER"
Icon="ArtCraftLauncherIcon.exe" IconIndex="0">
<ShortcutProperty Key="System.AppUserModel.ID" Value="ai.storyteller.artcraft-launcher" />
</Shortcut>
<RegistryValue Root="HKCU" Key="Software\Learning Machines LLC\ArtCraft Launcher" Name="DesktopShortcut" Value="1" Type="integer" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>