feat(installer): unify Windows setup and completion screens (INSTALLER-UI)

This commit is contained in:
skylartaylor
2026-10-16 03:46:50 +02:00
parent 05955c08b4
commit d09595b323
10 changed files with 291 additions and 142 deletions
+18 -1
View File
@@ -2,7 +2,7 @@
**Stage: alpha** · next: beta, ~30 points of "ready for real work" and ~350–620 h away
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (open-issue count, crash list and localization share re-audited against `upstream/main` 470ea7a3) · **Target:** Adobe After Effects 2026 (26.5.0.89)
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-14 · **Change:** minor (open-issue count, crash list and localization share re-audited against `upstream/main` 470ea7a3) · **Target:** Adobe After Effects 2026 (26.5.0.89)
EffectCraft aims to do what After Effects does, with the same panels, menus and behaviour, written
from scratch in Rust. This page is the one-page summary; the evidence is in
@@ -157,3 +157,20 @@ Come tell us what matters most to you on [Discord](https://discord.gg/artcraft).
| 2026-10-10 | minor | Stage checked against the new core-workflow gate (all six pass; still alpha) |
| 2026-10-10 | major | Rewritten to the shared progress-docs standard: stage, two numbers, dimensions, features, languages, upcoming, progress log; milestones moved to docs/roadmap.md |
| 2026-10-05 | major | Two numbers (breadth vs real use) and workstreams G1–G9 |
## Packaging update (2026-10-11)
Windows setup now uses the suite's shared `craft-installer` UI, preserving existing folder,
shortcut and launch options and app-owned installation policy. Install, uninstall and repair
have explicit completion screens; cancellation and failure have separate endings. Source/profile
checks, WiX UI library compilation for x64/x86/ARM64, PowerShell parsing and package identity
comparisons pass. Native fixture MSI linking/UI tables pass on all three architectures, and
Windows full UI install/repair/uninstall, x86 install/remove, folder upgrades, desktop choices
and launch checks pass with fixtures. Native evidence and real-release acceptance limits
are linked from
[Windows packaging](packaging/windows/README.md).
Windows Settings removal now opens the shared completion screen. Quiet install/removal
remains unattended; Settings labels preserve compatibility with existing Launchers, and
previous Modify policy is retained. Windows 11 fixture verification covers both installation
scopes and x64/x86 removal, with full MSI validation on x64/x86/ARM64.
+4 -1
View File
@@ -283,7 +283,10 @@ fn render_out_is_relative_to_the_working_directory() {
.unwrap();
assert!(o.status.success(), "{out}: {}", String::from_utf8_lossy(&o.stderr));
let v: Value = serde_json::from_slice(&o.stdout).unwrap();
PathBuf::from(v["rendered"][0]["output"].as_str().unwrap())
let done = PathBuf::from(v["rendered"][0]["output"].as_str().unwrap());
assert!(done.is_absolute(), "the CLI reports the absolute output path");
// Windows canonical paths may add a verbatim prefix; compare the same file.
done.canonicalize().unwrap()
};
// A path that already names the project folder is not doubled.
let done = render(&root, "proj/x.ecproj", "proj/out/d.gif");
+1 -1
View File
@@ -306,7 +306,7 @@ mod tests {
let r = s.execute("footage.relink", json!({"item": a.0, "path": target})).unwrap();
assert_eq!(r["relinked"], json!([a.0, b.0, m.0]), "{r}");
assert_eq!(r["stillMissing"], 1);
assert_eq!(path_of(&s, m), new.join("audio/m.wav").to_string_lossy());
assert_eq!(std::path::Path::new(&path_of(&s, m)), new.join("audio/m.wav"));
assert_eq!(path_of(&s, other), "/elsewhere/c.mov");
// One undo step brings every path back.
s.execute("edit.undo", json!({})).unwrap();
+1 -1
View File
@@ -269,7 +269,7 @@ mod tests {
_ => None,
})
.collect();
assert!(paths.contains(&d.join("Job/shots/a.png").to_string_lossy().to_string()), "{paths:?}");
assert!(paths.iter().any(|path| std::path::Path::new(path) == d.join("Job/shots/a.png")), "{paths:?}");
assert!(!o.is_dirty(), "finding the files is not an edit");
let _ = std::fs::remove_dir_all(d);
}
+1 -1
View File
@@ -48,7 +48,7 @@ fn replacing_a_missing_file_relinks_the_others_that_moved_with_it() {
let r = s.execute("file.replaceFootage", json!({"item": a, "path": new.join("stills/a.png").to_string_lossy()})).unwrap();
assert_eq!(r["relinked"].as_array().map(Vec::len), Some(1), "{r}");
let after = footage_paths(&s);
assert!(after.iter().all(|(_, p, missing)| p.starts_with(&*new.to_string_lossy()) && !missing), "{after:?}");
assert!(after.iter().all(|(_, p, missing)| Path::new(p).starts_with(&new) && !missing), "{after:?}");
// One undo step puts both back.
s.execute("edit.undo", json!({})).unwrap();
assert_eq!(footage_paths(&s).iter().map(|(_, p, _)| p.clone()).collect::<Vec<_>>(), before.iter().map(|(_, p, _)| p.clone()).collect::<Vec<_>>());
+10 -1
View File
@@ -1,6 +1,6 @@
# Roadmap detail
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-10 · **Change:** major (new file: milestones moved from ROADMAP.md, current focus and next steps with Opus 5.5 estimates) · **Target:** Adobe After Effects 2026
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** minor (shared Windows installer UI; existing folder/shortcut/launch options retained; install and uninstall completion) · **Target:** Adobe After Effects 2026
Forward-looking detail behind the [ROADMAP](../ROADMAP.md). The work list itself, one gap at a
time, is [gaps.md](gaps.md); the numbers are in [target-app-parity.md](target-app-parity.md).
@@ -85,3 +85,12 @@ To beta (≈ 75% ready and `.aep` opening): **≈ 350–620 h**. To full parity:
|---|---|---|
| 2026-10-10 | minor | Alpha gate: six core workflows checked; all pass, stage stays alpha |
| 2026-10-10 | major | New file: milestones from ROADMAP.md, current focus, next steps with estimates |
## Packaging update (2026-10-11)
Windows setup now uses the suite's shared `craft-installer` UI, preserving existing folder,
shortcut and launch options and app-owned installation policy. Install, uninstall and repair
have explicit completion screens; cancellation and failure have separate endings. Source/profile
checks, WiX UI library compilation for x64/x86/ARM64, PowerShell parsing and package identity
comparisons pass. Full MSI linking and interactive Windows acceptance remain pending; see
[Windows packaging](../packaging/windows/README.md).
+34
View File
@@ -0,0 +1,34 @@
# Windows installer UI
`installer-ui.wxs` is generated from the shared `craft-installer` crate in
[craft-libs](https://github.com/storytold/craft-libs/tree/main/crates/installer).
`package.ps1` compiles it with this app's package source. The common UI provides progress,
repair/remove, and explicit install/uninstall/repair success, cancellation and failure screens.
Regenerate or check from a sibling craft-libs checkout:
```sh
cargo run --locked -p craft-installer -- --write --app effectcraft ../effectcraft/packaging/windows/installer-ui.wxs
cargo run --locked -p craft-installer -- --check --app effectcraft ../effectcraft/packaging/windows/installer-ui.wxs
```
Edit the shared templates/profile and regenerate; avoid editing this snapshot by hand. The
profile retains this app's existing folder, shortcut and launch options; defaults, persistence,
components, associations and signing stay in the app-owned source. Release builds require no
sibling checkout, published crate or additional runtime dependency. `/qn`, `/qb` and `/passive`
remain unattended and do not launch the app.
Source/profile checks and native fixture MSI linking/UI table checks passed for x64, x86 and
ARM64. All 14 profiles passed full UI install, missing-file repair and cached uninstall, and
x86 silent install/removal. Folder upgrades, desktop choices and all six launch profiles also
passed their Windows fixture checks. See the
[Windows verification record](../../../craft-libs/docs/windows-installer-verification.md) for
interactive results, scope validation and remaining acceptance checks with real release payloads.
The shared crate includes reusable linking, full UI, option/upgrade/launch and x86 test runners.
Windows Settings now uses a separate MSI-owned entry whose uninstall command selects full UI,
including the completion screen. Its quiet command and direct `/qn`, `/qb`, `/passive` commands
remain unattended. The main feature references `CraftInstallerSettings`; the shared snapshot
owns this registration and hides the native MSI entry while retaining MSI repair/upgrade APIs.
The Settings display name appends `(Craft Apps)` for compatibility with older Launchers; the
hidden native MSI name stays unchanged. Existing ARP Modify policy is retained explicitly.
+5 -2
View File
@@ -23,7 +23,6 @@
DowngradeErrorMessage="A newer version of EffectCraft is already installed." />
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<!-- The setup wizard (installer-ui.wxs): pick the folder on a fresh install. -->
<UIRef Id="EffectcraftUI" />
<Icon Id="EffectcraftIcon.ico" SourceFile="$(var.IconPath)" />
<Property Id="ARPPRODUCTICON" Value="EffectcraftIcon.ico" />
@@ -54,11 +53,12 @@
DESKTOPSHORTCUT=0 silently); an upgrade keeps what the installed copy chose, and adds none
to an install from before this option. -->
<Property Id="DESKTOPSHORTCUT" Value="1" Secure="yes" />
<Property Id="CRAFTDESKTOPSHORTCUTSELECTED" Secure="yes" />
<Property Id="PREVIOUSDESKTOPSHORTCUT" Secure="yes">
<RegistrySearch Id="PreviousDesktopShortcut" Root="HKLM" Type="raw" Key="Software\EffectCraft" Name="DesktopShortcut" />
</Property>
<SetProperty Id="DESKTOPSHORTCUT" Value="[PREVIOUSDESKTOPSHORTCUT]" After="SetINSTALLFOLDERFromExe" Sequence="both"
Condition="WIX_UPGRADE_DETECTED" />
Condition="WIX_UPGRADE_DETECTED AND NOT CRAFTDESKTOPSHORTCUTSELECTED" />
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="EffectCraft" />
@@ -68,7 +68,10 @@
<Feature Id="Main" Title="EffectCraft" Level="1">
<ComponentGroupRef Id="EffectcraftFiles" />
<ComponentGroupRef Id="CraftInstallerSettings" />
</Feature>
<!-- Shared full UI; app installation policy remains below. -->
<UIRef Id="CraftInstallerUI" />
</Package>
<Fragment>
+216 -133
View File
@@ -1,49 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
EffectCraft installer wizard: original dialogs built from Windows Installer's own controls and
events (no WixUI library), included by effectcraft.wxs with <UIRef Id="EffectcraftUI" />.
fresh install: DestDlg (folder, Change... opens BrowseDlg, desktop shortcut) -> ReadyDlg -> ProgressDlg -> ExitDlg
upgrade: ReadyDlg (the installed copy's folder, not editable) -> ProgressDlg -> ExitDlg
already there: MaintDlg (Repair or Remove) -> ProgressDlg -> ExitDlg
Silent installs (/qn) skip all of this; pass INSTALLFOLDER="..." to choose the folder there.
Folder controls edit INSTALLFOLDER indirectly through _BrowseProperty; SetTargetPath checks the
path and applies it, and Windows Installer reports a bad path itself.
-->
<!-- Generated by craft-installer for effectcraft. Edit the template in craft-libs, then regenerate. -->
<!-- Adapted from PdfCraft installer-ui.wxs (a6bc63a), GridCraft gridcraft.wxs (5b15827),
and EffectCraft installer-ui.wxs (1b4d47f).
Copyright (c) 2026 ArtCraft Team and the PdfCraft contributors.
Copyright (c) 2026 ArtCraft Team and the GridCraft contributors.
Copyright (c) 2026 ArtCraft Team and the EffectCraft contributors.
Licensed under MIT OR Apache-2.0; see the repository's LICENSE-MIT and LICENSE-APACHE. -->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<UI Id="EffectcraftUI">
<TextStyle Id="EcText" FaceName="Segoe UI" Size="9" />
<TextStyle Id="EcTitle" FaceName="Segoe UI" Size="12" Bold="yes" />
<Property Id="DefaultUIFont" Value="EcText" />
<Property Id="ErrorDialog" Value="ErrorDlg" />
<Property Id="_BrowseProperty" Value="INSTALLFOLDER" />
<!-- Windows Installer populates files-in-use lists at runtime. Keep their table even
though these dialogs have no static ListItem rows (the GridCraft UI does the same). -->
<EnsureTable Id="ListBox" />
<!-- Reuse the existing package icon as a fallback; MSI supplies its standard message icon. -->
<Binary Id="CraftInstallerErrorIcon" SourceFile="$(var.IconPath)" />
<SetProperty Id="CraftDesktopShortcutChoice" Value="1" Before="InstallWelcome" Sequence="ui"
Condition="UILevel = 5 AND NOT Installed AND NOT REMOVE AND NOT REINSTALL AND DESKTOPSHORTCUT = &quot;1&quot;" />
<!-- Choose the destination folder (fresh installs only). -->
<Dialog Id="DestDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}Choose where to install [ProductName]" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="30" NoPrefix="yes" Text="[ProductName] will be installed in this folder. Type a different folder, or click Change to browse for one." />
<Control Id="FolderLabel" Type="Text" X="20" Y="85" Width="330" Height="12" Text="&amp;Destination folder:" />
<Control Id="Folder" Type="PathEdit" X="20" Y="100" Width="260" Height="18" Property="_BrowseProperty" Indirect="yes" />
<Control Id="ChangeFolder" Type="PushButton" X="290" Y="99" Width="60" Height="20" Text="&amp;Change...">
<Publish Event="SpawnDialog" Value="BrowseDlg" />
<UI Id="CraftInstallerUI">
<TextStyle Id="CraftInstallerNormal" FaceName="Segoe UI" Size="9" />
<TextStyle Id="CraftInstallerTitle" FaceName="Segoe UI" Size="12" Bold="yes" />
<Property Id="DefaultUIFont" Value="CraftInstallerNormal" />
<Property Id="ErrorDialog" Value="CraftInstallerErrorDlg" />
<Dialog Id="CraftInstallerErrorDlg" Width="320" Height="120" Title="[ProductName] Setup" ErrorDialog="yes" NoMinimize="yes">
<Control Id="ErrorText" Type="Text" X="60" Y="15" Width="245" Height="65" TabSkip="no" />
<Control Id="ErrorIcon" Type="Icon" X="15" Y="15" Width="32" Height="32" FixedSize="yes" IconSize="32" TabSkip="yes" Text="CraftInstallerErrorIcon" />
<Control Id="A" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="Abort" TabSkip="yes">
<Publish Event="EndDialog" Value="ErrorAbort" />
</Control>
<Control Id="DesktopShortcut" Type="CheckBox" X="20" Y="132" Width="330" Height="17" Property="DESKTOPSHORTCUT" CheckBoxValue="1" Text="Create a &amp;desktop shortcut" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="&amp;Back" Disabled="yes" />
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="&amp;Next">
<Publish Event="SetTargetPath" Value="[_BrowseProperty]" Order="1" />
<Publish Event="NewDialog" Value="ReadyDlg" Order="2" />
<Control Id="C" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="Cancel" TabSkip="yes">
<Publish Event="EndDialog" Value="ErrorCancel" />
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="CancelDlg" />
<Control Id="I" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="Ignore" TabSkip="yes">
<Publish Event="EndDialog" Value="ErrorIgnore" />
</Control>
<Control Id="N" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="No" TabSkip="yes">
<Publish Event="EndDialog" Value="ErrorNo" />
</Control>
<Control Id="O" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="OK" TabSkip="yes">
<Publish Event="EndDialog" Value="ErrorOk" />
</Control>
<Control Id="R" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="Retry" TabSkip="yes">
<Publish Event="EndDialog" Value="ErrorRetry" />
</Control>
<Control Id="Y" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="Yes" TabSkip="yes">
<Publish Event="EndDialog" Value="ErrorYes" />
</Control>
</Dialog>
<Dialog Id="FilesInUse" Width="320" Height="190" Title="[ProductName] Setup" NoMinimize="yes">
<Control Id="Description" Type="Text" X="15" Y="15" Width="290" Height="35"
Text="Close the applications listed below, then choose Retry. Ignore may require a restart." />
<Control Id="Files" Type="ListBox" X="15" Y="55" Width="290" Height="85" Property="FileInUseProcess" />
<Control Id="Retry" Type="PushButton" X="95" Y="155" Width="65" Height="20" Default="yes" Text="Retry">
<Publish Event="EndDialog" Value="Retry" />
</Control>
<Control Id="Ignore" Type="PushButton" X="167" Y="155" Width="65" Height="20" Text="Ignore">
<Publish Event="EndDialog" Value="Ignore" />
</Control>
<Control Id="Cancel" Type="PushButton" X="240" Y="155" Width="65" Height="20" Cancel="yes" Text="Cancel">
<Publish Event="EndDialog" Value="Exit" />
</Control>
</Dialog>
<!-- Folder browser: drive/folder list, up one level, new folder. -->
<Dialog Id="BrowseDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}Browse for a folder" />
<Property Id="_BrowseProperty" Value="INSTALLFOLDER" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
<Dialog Id="InstallFolder" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Choose the destination folder" />
<Control Id="Description" Type="Text" X="20" Y="55" Width="330" Height="40" Text="[ProductName] will be installed in this folder. Click Install to continue." />
<Control Id="Install" Type="PushButton" X="195" Y="235" Width="75" Height="23" Default="yes" ElevationShield="yes" Text="&amp;Install">
<Publish Event="SetTargetPath" Value="INSTALLFOLDER" Order="1" />
<Publish Event="EndDialog" Value="Return" Order="99" Condition="1" />
</Control>
<Control Id="FolderLabel" Type="Text" X="20" Y="105" Width="330" Height="15" Text="&amp;Destination folder:" />
<Control Id="Folder" Type="PathEdit" X="20" Y="125" Width="250" Height="20" Property="_BrowseProperty" Indirect="yes" DisableCondition="WIX_UPGRADE_DETECTED" />
<Control Id="ChangeFolder" Type="PushButton" X="280" Y="124" Width="70" Height="23" Text="&amp;Browse..." DisableCondition="WIX_UPGRADE_DETECTED">
<Publish Event="SpawnDialog" Value="CraftBrowseDlg" />
</Control>
<Control Id="UpgradeNote" Type="Text" X="20" Y="160" Width="330" Height="40" Hidden="yes" ShowCondition="WIX_UPGRADE_DETECTED" Text="This update uses your existing installation folder." />
<Control Id="Cancel" Type="PushButton" X="280" Y="235" Width="70" Height="23" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="ConfirmCancel" />
</Control>
</Dialog>
<Dialog Id="CraftBrowseDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\CraftInstallerTitle}Browse for a folder" />
<Control Id="LookInLabel" Type="Text" X="20" Y="47" Width="44" Height="12" Text="&amp;Look in:" />
<Control Id="LookIn" Type="DirectoryCombo" X="66" Y="45" Width="220" Height="80" Property="_BrowseProperty" Indirect="yes" Fixed="yes" Remote="yes">
<Subscribe Event="IgnoreChange" Attribute="IgnoreChange" />
@@ -59,7 +99,8 @@
<Control Id="Path" Type="PathEdit" X="20" Y="210" Width="330" Height="18" Property="_BrowseProperty" Indirect="yes" />
<Control Id="OK" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="OK">
<Publish Event="SetTargetPath" Value="[_BrowseProperty]" Order="1" />
<Publish Event="EndDialog" Value="Return" Order="2" />
<Publish Event="EndDialog" Value="Return" Order="99" Condition="1" />
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
<Publish Event="Reset" Value="0" Order="1" />
@@ -67,136 +108,178 @@
</Control>
</Dialog>
<!-- Last stop before anything changes: shows the folder that will be used. -->
<Dialog Id="ReadyDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}Ready to install [ProductName]" />
<Control Id="InstallText" Type="Text" X="20" Y="45" Width="330" Height="24" NoPrefix="yes" Text="[ProductName] [ProductVersion] will be installed in:" HideCondition="WIX_UPGRADE_DETECTED" />
<Control Id="UpgradeText" Type="Text" X="20" Y="45" Width="330" Height="24" NoPrefix="yes" Text="[ProductName] is already installed. Version [ProductVersion] will replace it in the same folder:" Hidden="yes" ShowCondition="WIX_UPGRADE_DETECTED" />
<Control Id="Destination" Type="Text" X="20" Y="72" Width="330" Height="36" NoPrefix="yes" Text="[INSTALLFOLDER]" />
<Control Id="Prompt" Type="Text" X="20" Y="116" Width="330" Height="24" NoPrefix="yes" Text="Click Install to continue." />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="&amp;Back" DisableCondition="WIX_UPGRADE_DETECTED">
<Publish Event="NewDialog" Value="DestDlg" />
</Control>
<Control Id="Install" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" ElevationShield="yes" Text="&amp;Install">
<Dialog Id="CraftInvalidFolder" Width="300" Height="120" Title="[ProductName] Setup">
<Control Id="Description" Type="Text" X="20" Y="20" Width="260" Height="50" Text="This destination folder cannot be used. Choose a valid local folder and try again." />
<Control Id="OK" Type="PushButton" X="210" Y="85" Width="70" Height="23" Default="yes" Cancel="yes" Text="OK">
<Publish Event="EndDialog" Value="Return" />
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="CancelDlg" />
</Control>
</Dialog>
<!-- Opening the MSI when this version is already installed. -->
<Dialog Id="MaintDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}[ProductName] is already installed" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="24" NoPrefix="yes" Text="Repair puts back missing or damaged files in its folder. Remove uninstalls it; your projects are not touched." />
<Control Id="Repair" Type="PushButton" X="20" Y="80" Width="80" Height="20" Default="yes" ElevationShield="yes" Text="&amp;Repair">
<!-- "a": put back every file, whatever its version or date. With "e"/"o" a changed
unversioned file (effectcraft-cli.exe has no version resource) counts as a user edit
and is kept, and "c" only checks files that carry an MSI checksum. -->
<Publish Event="ReinstallMode" Value="amus" Order="1" />
<Publish Event="Reinstall" Value="ALL" Order="2" />
<Dialog Id="InstallWelcome" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Install [ProductName]" />
<Control Id="Description" Type="Text" X="20" Y="55" Width="330" Height="40"
Text="Setup will install [ProductName] for all users and add it to the Start menu. Click Next to choose its folder." />
<Control Id="DesktopShortcut" Type="CheckBox" X="20" Y="125" Width="330" Height="20" Property="CraftDesktopShortcutChoice" CheckBoxValue="1" Text="Create a &amp;desktop shortcut" />
<Control Id="Install" Type="PushButton" X="195" Y="235" Width="75" Height="23" Default="yes" ElevationShield="no" Text="&amp;Next">
<Publish Property="DESKTOPSHORTCUT" Value="1" Order="1" Condition="CraftDesktopShortcutChoice = &quot;1&quot;" />
<Publish Property="DESKTOPSHORTCUT" Value="0" Order="2" Condition="NOT CraftDesktopShortcutChoice" />
<Publish Property="CRAFTDESKTOPSHORTCUTSELECTED" Value="1" Order="3" />
<Publish Event="EndDialog" Value="Return" Order="99" />
</Control>
<Control Id="Cancel" Type="PushButton" X="280" Y="235" Width="70" Height="23" Cancel="yes" Text="Cancel">
<Publish Event="EndDialog" Value="Exit" />
</Control>
</Dialog>
<Dialog Id="InstallMaintenance" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Maintain [ProductName]" />
<Control Id="Description" Type="Text" X="20" Y="55" Width="330" Height="70"
Text="[ProductName] is already installed. Choose Repair to restore missing files and shortcuts, or Remove to uninstall it." />
<Control Id="Repair" Type="PushButton" X="110" Y="235" Width="75" Height="23" Default="yes" ElevationShield="yes" Text="&amp;Repair">
<!-- Feature events do not retain REINSTALL/REMOVE in the client UI session. -->
<Publish Property="CraftInstallerOperation" Value="repair" Order="1" />
<Publish Event="ReinstallMode" Value="amus" Order="2" />
<Publish Event="Reinstall" Value="ALL" Order="3" />
<Publish Event="EndDialog" Value="Return" Order="4" />
</Control>
<Control Id="Remove" Type="PushButton" X="195" Y="235" Width="75" Height="23" ElevationShield="yes" Text="R&amp;emove">
<Publish Property="CraftInstallerOperation" Value="remove" Order="1" />
<Publish Event="Remove" Value="ALL" Order="2" />
<Publish Event="EndDialog" Value="Return" Order="3" />
</Control>
<Control Id="Remove" Type="PushButton" X="20" Y="108" Width="80" Height="20" ElevationShield="yes" Text="Re&amp;move">
<Publish Event="Remove" Value="ALL" Order="1" />
<Publish Event="EndDialog" Value="Return" Order="2" />
</Control>
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="CancelDlg" />
<Control Id="Cancel" Type="PushButton" X="280" Y="235" Width="70" Height="23" Cancel="yes" Text="Cancel">
<Publish Event="EndDialog" Value="Exit" />
</Control>
</Dialog>
<Dialog Id="ProgressDlg" Width="370" Height="270" Title="[ProductName] Setup" Modeless="yes">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}Working on [ProductName]" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="24" NoPrefix="yes" Text="Please wait. This can take a minute." />
<Control Id="Action" Type="Text" X="20" Y="80" Width="330" Height="12" NoPrefix="yes">
<Subscribe Event="ActionText" Attribute="Text" />
</Control>
<Control Id="Progress" Type="ProgressBar" X="20" Y="96" Width="330" Height="12" ProgressBlocks="yes" Text="Progress">
<Dialog Id="InstallProgress" Width="370" Height="270" Title="[ProductName] Setup" Modeless="yes">
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Setting up [ProductName]" />
<!-- No ActionText table is authored: subscribing would blank this message (#305). -->
<Control Id="Status" Type="Text" X="20" Y="60" Width="330" Height="40" Text="Please wait while setup completes." />
<Control Id="Progress" Type="ProgressBar" X="20" Y="110" Width="330" Height="15" ProgressBlocks="yes">
<Subscribe Event="SetProgress" Attribute="Progress" />
</Control>
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="CancelDlg" />
<Control Id="Cancel" Type="PushButton" X="280" Y="235" Width="70" Height="23" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="ConfirmCancel" />
</Control>
</Dialog>
<Dialog Id="CancelDlg" Width="260" Height="85" Title="[ProductName] Setup" NoMinimize="yes">
<Control Id="Text" Type="Text" X="10" Y="15" Width="240" Height="30" NoPrefix="yes" Text="Cancel [ProductName] setup? Nothing will be changed." />
<Control Id="Yes" Type="PushButton" X="132" Y="57" Width="56" Height="17" Text="&amp;Yes">
<Dialog Id="ConfirmCancel" Width="300" Height="110" Title="Cancel setup">
<Control Id="Description" Type="Text" X="20" Y="20" Width="260" Height="35" Text="Cancel [ProductName] setup?" />
<Control Id="Yes" Type="PushButton" X="130" Y="70" Width="70" Height="23" Text="&amp;Yes">
<Publish Event="EndDialog" Value="Exit" />
</Control>
<Control Id="No" Type="PushButton" X="194" Y="57" Width="56" Height="17" Default="yes" Cancel="yes" Text="&amp;No">
<Control Id="No" Type="PushButton" X="210" Y="70" Width="70" Height="23" Default="yes" Cancel="yes" Text="&amp;No">
<Publish Event="EndDialog" Value="Return" />
</Control>
</Dialog>
<!-- Windows Installer's messages (a bad folder, low disk space, ...) and files in use. -->
<Dialog Id="ErrorDlg" Width="270" Height="105" Title="[ProductName] Setup" ErrorDialog="yes">
<Control Id="ErrorText" Type="Text" X="10" Y="10" Width="250" Height="60" NoPrefix="yes" TabSkip="no" Text="Error" />
<Control Id="Y" Type="PushButton" X="100" Y="80" Width="56" Height="17" TabSkip="yes" Text="&amp;Yes"><Publish Event="EndDialog" Value="ErrorYes" /></Control>
<Control Id="A" Type="PushButton" X="100" Y="80" Width="56" Height="17" TabSkip="yes" Text="Abort"><Publish Event="EndDialog" Value="ErrorAbort" /></Control>
<Control Id="C" Type="PushButton" X="100" Y="80" Width="56" Height="17" TabSkip="yes" Text="Cancel"><Publish Event="EndDialog" Value="ErrorCancel" /></Control>
<Control Id="I" Type="PushButton" X="100" Y="80" Width="56" Height="17" TabSkip="yes" Text="&amp;Ignore"><Publish Event="EndDialog" Value="ErrorIgnore" /></Control>
<Control Id="N" Type="PushButton" X="100" Y="80" Width="56" Height="17" TabSkip="yes" Text="&amp;No"><Publish Event="EndDialog" Value="ErrorNo" /></Control>
<Control Id="O" Type="PushButton" X="100" Y="80" Width="56" Height="17" TabSkip="yes" Text="OK"><Publish Event="EndDialog" Value="ErrorOk" /></Control>
<Control Id="R" Type="PushButton" X="100" Y="80" Width="56" Height="17" TabSkip="yes" Text="&amp;Retry"><Publish Event="EndDialog" Value="ErrorRetry" /></Control>
<Dialog Id="InstallComplete" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Setup completed successfully" HideCondition="REMOVE = &quot;ALL&quot; OR CraftInstallerOperation = &quot;remove&quot;" />
<Control Id="RemovedTitle" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Uninstall completed successfully" Hidden="yes" ShowCondition="REMOVE = &quot;ALL&quot; OR CraftInstallerOperation = &quot;remove&quot;" />
<Control Id="Description" Type="Text" X="20" Y="55" Width="330" Height="70"
Text="[ProductName] is installed. Open it from the Start menu. Click Finish to close setup." HideCondition="REMOVE = &quot;ALL&quot; OR REINSTALL OR CraftInstallerOperation OR ACTION = &quot;ADMIN&quot;" />
<Control Id="Removed" Type="Text" X="20" Y="55" Width="330" Height="70" Hidden="yes"
Text="[ProductName] was uninstalled. Your documents and settings were kept. Click Finish to close setup."
ShowCondition="REMOVE = &quot;ALL&quot; OR CraftInstallerOperation = &quot;remove&quot;" />
<Control Id="Repaired" Type="Text" X="20" Y="55" Width="330" Height="70" Hidden="yes"
Text="[ProductName] was repaired. Open it from the Start menu. Click Finish to close setup."
ShowCondition="(REINSTALL OR CraftInstallerOperation = &quot;repair&quot;) AND NOT REMOVE AND CraftInstallerOperation &lt;&gt; &quot;remove&quot; AND ACTION &lt;&gt; &quot;ADMIN&quot;" />
<Control Id="AdminComplete" Type="Text" X="20" Y="55" Width="330" Height="70" Hidden="yes"
Text="The administrative image was created. Click Finish to close setup." ShowCondition="ACTION = &quot;ADMIN&quot;" />
<Control Id="Finish" Type="PushButton" X="280" Y="235" Width="70" Height="23" Default="yes" Cancel="yes" Text="&amp;Finish">
<Publish Event="EndDialog" Value="Return" Order="999" />
</Control>
</Dialog>
<Dialog Id="FilesInUse" Width="370" Height="270" Title="[ProductName] Setup" KeepModeless="yes">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}Close these programs" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="24" NoPrefix="yes" Text="These programs are using files setup needs to update. Close them, then click Retry." />
<Control Id="List" Type="ListBox" X="20" Y="75" Width="330" Height="140" Property="FileInUseProcess" Sunken="yes" TabSkip="yes" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Retry" Type="PushButton" X="168" Y="243" Width="56" Height="17" Default="yes" Text="&amp;Retry"><Publish Event="EndDialog" Value="Retry" /></Control>
<Control Id="Ignore" Type="PushButton" X="236" Y="243" Width="56" Height="17" Text="&amp;Ignore"><Publish Event="EndDialog" Value="Ignore" /></Control>
<Control Id="Exit" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="E&amp;xit"><Publish Event="EndDialog" Value="Exit" /></Control>
</Dialog>
<!-- The three endings: finished, cancelled, failed. -->
<Dialog Id="ExitDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}[ProductName] setup is complete" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="36" NoPrefix="yes" Text="Click Finish to close setup." />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Finish" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="&amp;Finish">
<Dialog Id="InstallFailed" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Setup failed" />
<Control Id="Description" Type="Text" X="20" Y="55" Width="330" Height="70"
Text="[ProductName] setup could not be completed. Resolve the reported error and run setup again." />
<Control Id="Finish" Type="PushButton" X="280" Y="235" Width="70" Height="23" Default="yes" Cancel="yes" Text="&amp;Finish">
<Publish Event="EndDialog" Value="Return" />
</Control>
</Dialog>
<Dialog Id="UserExitDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}Setup was cancelled" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="36" NoPrefix="yes" Text="[ProductName] was not changed. Run setup again to install it." />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Finish" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="&amp;Finish">
<Publish Event="EndDialog" Value="Exit" />
<Dialog Id="InstallCancelled" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Setup cancelled" />
<Control Id="Description" Type="Text" X="20" Y="55" Width="330" Height="70"
Text="[ProductName] setup was cancelled. Run setup again when you are ready." />
<Control Id="Finish" Type="PushButton" X="280" Y="235" Width="70" Height="23" Default="yes" Cancel="yes" Text="&amp;Finish">
<Publish Event="EndDialog" Value="Return" />
</Control>
</Dialog>
<Dialog Id="FatalErrorDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="20" Transparent="yes" NoPrefix="yes" Text="{\EcTitle}Setup did not finish" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="48" NoPrefix="yes" Text="Something went wrong, and setup put back what was there before. Run setup again, or report it at https://github.com/storytold/effectcraft/issues." />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Finish" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="&amp;Finish">
<!-- Shown by Windows Installer (Restart Manager) when an upgrade or repair must replace files
that a running app holds open. OK closes those programs (MSIRMSHUTDOWN=2) and
continues; Cancel ends setup. -->
<Dialog Id="MsiRMFilesInUse" Width="370" Height="200" Title="[ProductName] Setup" KeepModeless="yes">
<Control Id="Title" Type="Text" X="20" Y="15" Width="330" Height="25" Text="{\CraftInstallerTitle}Files in use" />
<Control Id="Description" Type="Text" X="20" Y="45" Width="330" Height="30"
Text="These programs are using files that setup needs to update. Click OK to close them and continue, or Cancel to stop setup." />
<Control Id="List" Type="ListBox" X="20" Y="80" Width="330" Height="55" Property="FileInUseProcess" Sunken="yes" TabSkip="yes" />
<Control Id="OK" Type="PushButton" X="195" Y="165" Width="75" Height="23" Default="yes" Text="OK">
<Publish Property="MSIRMSHUTDOWN" Value="2" Order="1" />
<Publish Event="EndDialog" Value="Return" Order="2" />
</Control>
<Control Id="Cancel" Type="PushButton" X="280" Y="165" Width="70" Height="23" Cancel="yes" Text="Cancel">
<Publish Event="EndDialog" Value="Exit" />
</Control>
</Dialog>
<InstallUISequence>
<Show Dialog="DestDlg" Before="ProgressDlg" Condition="NOT Installed AND NOT WIX_UPGRADE_DETECTED" />
<Show Dialog="ReadyDlg" After="DestDlg" Condition="NOT Installed AND WIX_UPGRADE_DETECTED" />
<Show Dialog="MaintDlg" After="ReadyDlg" Condition="Installed AND NOT REMOVE AND NOT REINSTALL" />
<Show Dialog="ProgressDlg" Before="ExecuteAction" />
<Show Dialog="ExitDlg" OnExit="success" />
<Show Dialog="UserExitDlg" OnExit="cancel" />
<Show Dialog="FatalErrorDlg" OnExit="error" />
<Show Dialog="InstallWelcome" Before="CostInitialize" Condition="UILevel = 5 AND NOT Installed AND NOT REMOVE AND NOT REINSTALL" />
<!-- Reinstall/Remove control events require the selection manager initialized by costing. -->
<Show Dialog="InstallMaintenance" After="CostFinalize" Condition="UILevel = 5 AND Installed AND NOT REMOVE AND NOT REINSTALL" />
<Show Dialog="InstallFolder" After="InstallMaintenance" Condition="UILevel = 5 AND NOT Installed AND NOT REMOVE AND NOT REINSTALL" />
<Show Dialog="InstallProgress" Before="ExecuteAction" Condition="UILevel = 5" />
<Show Dialog="InstallComplete" OnExit="success" Condition="UILevel = 5" />
<Show Dialog="InstallFailed" OnExit="error" Condition="UILevel = 5" />
<Show Dialog="InstallCancelled" OnExit="cancel" Condition="UILevel = 5" />
</InstallUISequence>
<!-- msiexec /a (an administrative image) gets the same progress and endings. -->
<AdminUISequence>
<Show Dialog="ProgressDlg" Before="ExecuteAction" />
<Show Dialog="ExitDlg" OnExit="success" />
<Show Dialog="UserExitDlg" OnExit="cancel" />
<Show Dialog="FatalErrorDlg" OnExit="error" />
<Show Dialog="InstallProgress" Before="ExecuteAction" Condition="UILevel = 5" />
<Show Dialog="InstallComplete" OnExit="success" Condition="UILevel = 5" />
<Show Dialog="InstallFailed" OnExit="error" Condition="UILevel = 5" />
<Show Dialog="InstallCancelled" OnExit="cancel" Condition="UILevel = 5" />
</AdminUISequence>
</UI>
</Fragment>
<Fragment>
<!-- Keep MSI registration for repair, upgrades and managed removal, but let Settings
invoke our explicit UI command instead of Windows Installer's basic-UI ARP path. -->
<Property Id="ARPSYSTEMCOMPONENT" Value="1" />
<?if $(sys.BUILDARCH) = x86 ?>
<?define CraftMsiSystemFolder = SystemFolder ?>
<?else ?>
<?define CraftMsiSystemFolder = System64Folder ?>
<?endif ?>
<ComponentGroup Id="CraftInstallerSettings" Directory="INSTALLFOLDER">
<Component Id="CraftInstallerSettingsEntry" Guid="*">
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\Craft.effectcraft" ForceDeleteOnUninstall="yes">
<!-- Legacy Launchers match the canonical MSI name exactly; keep this alias distinct. -->
<RegistryValue Name="DisplayName" Type="string" Value="[ProductName] (Craft Apps)" KeyPath="yes" />
<RegistryValue Name="DisplayVersion" Type="string" Value="[ProductVersion]" />
<RegistryValue Name="Publisher" Type="string" Value="[Manufacturer]" />
<RegistryValue Name="DisplayIcon" Type="string" Value="&quot;[INSTALLFOLDER]effectcraft.exe&quot;,0" />
<RegistryValue Name="InstallLocation" Type="string" Value="[INSTALLFOLDER]" />
<RegistryValue Name="URLInfoAbout" Type="string" Value="[ARPURLINFOABOUT]" />
<RegistryValue Name="URLUpdateInfo" Type="string" Value="[ARPURLUPDATEINFO]" />
<RegistryValue Name="HelpLink" Type="string" Value="[ARPHELPLINK]" />
<!-- Deliberately omit WindowsInstaller: Settings must honor UninstallString. -->
<RegistryValue Name="UninstallString" Type="string" Value="&quot;[$(var.CraftMsiSystemFolder)]msiexec.exe&quot; /x [ProductCode] /qf /norestart" />
<RegistryValue Name="QuietUninstallString" Type="string" Value="&quot;[$(var.CraftMsiSystemFolder)]msiexec.exe&quot; /x [ProductCode] /qn /norestart" />
<RegistryValue Name="ModifyPath" Type="string" Value="&quot;[$(var.CraftMsiSystemFolder)]msiexec.exe&quot; /i [ProductCode] /qf /norestart" />
</RegistryKey>
</Component>
<!-- Preserve the app-owned ARPNOMODIFY policy instead of inventing a new default. -->
<Component Id="CraftInstallerSettingsNoModify" Guid="*" Condition="ARPNOMODIFY = &quot;1&quot;">
<RegistryValue Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\Craft.effectcraft"
Name="NoModify" Type="integer" Value="1" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
+1 -1
View File
@@ -107,7 +107,7 @@ Assert-InstalledIn $Custom
Assert-DesktopShortcut $true
# Read single values: Get-ItemProperty throws on some machines' malformed Uninstall entries.
$arp = @(Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall' |
ForEach-Object { if ($_.GetValue('DisplayName') -eq 'EffectCraft') { $_.GetValue('DisplayVersion') } })
ForEach-Object { if ($_.GetValue('DisplayName') -eq 'EffectCraft (Craft Apps)' -and $_.GetValue('SystemComponent') -ne 1) { $_.GetValue('DisplayVersion') } })
if ($arp.Count -ne 1 -or $arp[0] -ne '0.0.3') { throw "expected one EffectCraft 0.0.3 in Apps, found: $($arp -join ', ')" }
# Repair the way the wizard's Repair button does: damage the CLI in place (a later write time, as a