mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-24 10:27:44 -06:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a46e484ad6 |
@@ -381,3 +381,8 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
|
||||
| 2026-10-10 | major | Full re-measure; restructured to the progress-docs standard (stage banner, two numbers, dimensions, languages, upcoming, progress log); parity tables moved to `docs/target-app-parity.md`, milestones to `docs/roadmap.md` |
|
||||
| 2026-10-09 | minor | Recently landed: RTL, Ukrainian and Brazilian Portuguese |
|
||||
| 2026-10-06 | major | First honest estimate: 88% catalog, ~62% real parity, alpha ~85% of the way |
|
||||
|
||||
|
||||
### Windows installer unification — release preparation, 2026-10-12
|
||||
|
||||
The shared craft-installer UI preserves this revision's install options and provides persistent installation, repair and Windows Settings removal completion screens. Silent modes stay unattended. Earlier Windows acceptance is recorded in packaging/windows/README.md; normal CI and production-profile upgrade qualification against pinned current releases are in progress. No release or PR has been published.
|
||||
|
||||
@@ -236,7 +236,7 @@ fn save(s: &mut Session, v: &Value) -> CmdResult {
|
||||
match v.get("format").filter(|f| !f.is_null()) {
|
||||
None => {
|
||||
// Saving over a Word 2003 XML file (one opened as such, say) keeps its format.
|
||||
opts.word2003 = ext == "xml" && is_word_2003_file(&path);
|
||||
opts.word2003 = ext == "xml" && crate::io::is_word_2003_file(&path);
|
||||
}
|
||||
Some(f) if matches!(f.as_str(), Some(FORMAT_WORD_2003 | FORMAT_FLAT_OPC)) && ext == "xml" => {}
|
||||
Some(_) => {
|
||||
@@ -254,27 +254,6 @@ fn save(s: &mut Session, v: &Value) -> CmdResult {
|
||||
Ok(json!({"saved": true, "path": path.to_string_lossy(), "encrypted": encrypted}))
|
||||
}
|
||||
|
||||
/// Does `path` hold a Word 2003 XML Document already (so a plain save keeps that format)?
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn is_word_2003_file(path: &std::path::Path) -> bool {
|
||||
use std::io::Read;
|
||||
if crate::file_access::check_read(&path.to_string_lossy()).is_err() {
|
||||
return false;
|
||||
}
|
||||
let Ok(f) = std::fs::File::open(path) else { return false };
|
||||
let mut head = Vec::new();
|
||||
// The root element and its namespaces come first.
|
||||
if f.take(64 * 1024).read_to_end(&mut head).is_err() {
|
||||
return false;
|
||||
}
|
||||
wordcraft_docx::sniff_xml(&head) == wordcraft_docx::XmlKind::WordMl2003
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn is_word_2003_file(_path: &std::path::Path) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn save_as(s: &mut Session, v: &Value) -> CmdResult {
|
||||
if p::str(v, "path").is_none() {
|
||||
s.ui_requests.push(json!({"open": "saveAs"}));
|
||||
|
||||
@@ -378,6 +378,27 @@ fn read_regular_file(path: &std::path::Path, limit: u64) -> Option<Vec<u8>> {
|
||||
Some(out)
|
||||
}
|
||||
|
||||
/// Does `path` hold a Word 2003 XML Document already (so a plain save keeps that format)?
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn is_word_2003_file(path: &std::path::Path) -> bool {
|
||||
use std::io::Read;
|
||||
if crate::file_access::check_read(&path.to_string_lossy()).is_err() {
|
||||
return false;
|
||||
}
|
||||
let Ok(f) = std::fs::File::open(path) else { return false };
|
||||
let mut head = Vec::new();
|
||||
// The root element and its namespaces come first.
|
||||
if f.take(64 * 1024).read_to_end(&mut head).is_err() {
|
||||
return false;
|
||||
}
|
||||
wordcraft_docx::sniff_xml(&head) == wordcraft_docx::XmlKind::WordMl2003
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn is_word_2003_file(_path: &std::path::Path) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// The bytes of the file at `path`, for commands that take a path (pictures, mail-merge data).
|
||||
/// Every such read goes through here so the automation read root applies ([`crate::file_access`]).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
||||
@@ -1645,6 +1645,9 @@ impl WordApp {
|
||||
}
|
||||
let on_chrome = ctx.layer_id_at(pos).is_none_or(|l| l.order == egui::Order::Background);
|
||||
if on_chrome && ctx.memory(|m| m.focused().is_none()) && !egui::Popup::is_any_open(ctx) {
|
||||
// The next frame handles typing before the canvas requests egui focus.
|
||||
// Restore logical focus now so that first text event is not dropped.
|
||||
self.canvas.focused = true;
|
||||
self.canvas.want_focus = true;
|
||||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
@@ -88,3 +88,8 @@ languages, screen-reader access, ecosystem decisions (VBA, add-ins, cloud), an i
|
||||
| 2026-10-10 | major | Created: milestones moved from ROADMAP.md with remaining hours; Current focus from gaps.md; M15–M16 added |
|
||||
| 2026-10-10 | trivial | M11: ink pens and eraser landed (#307) |
|
||||
| 2026-10-11 | trivial | M11: lasso, Add Pen, Ink to Shape and Ink Replay landed (#395) |
|
||||
|
||||
|
||||
### Windows installer unification — release preparation, 2026-10-12
|
||||
|
||||
The shared craft-installer UI preserves this revision's install options and provides persistent installation, repair and Windows Settings removal completion screens. Silent modes stay unattended. Earlier Windows acceptance is recorded in packaging/windows/README.md; normal CI and production-profile upgrade qualification against pinned current releases are in progress. No release or PR has been published.
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
# UI parity with Microsoft Word
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** trivial (no document behind the Start screen, #1129; previously trivial: equation pictures share a few textures, #1002; previously trivial: selection bar in the left margin, #856; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously major: first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-15 · **Change:** trivial (the first text event after a chrome click is retained, #1093; previously trivial: no document behind the Start screen, #1129; previously trivial: equation pictures share a few textures, #1002; previously trivial: selection bar in the left margin, #856; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously major: first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
|
||||
How WordCraft looks and feels next to Word: ribbon, dialogs, panes, keyboard, mouse and touch.
|
||||
Typing behaviour has its own checklist ([`typing-parity.md`](typing-parity.md)). All assets are our
|
||||
@@ -47,6 +47,7 @@ own (`AGENTS.md`: no Microsoft iconography, ever).
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-13 | trivial | The caret is black or white by contrast with the paper, page colour, dark page, shading or highlight under it (#299) |
|
||||
| 2026-10-15 | trivial | Restore logical page focus when a chrome click is accepted, before the next frame handles typing; the first text event after a gallery or tab click is retained. Existing dialog, popup and text-field exclusions and immediate-typing tests remain in place (#1093) |
|
||||
| 2026-10-13 | trivial | Clicks on ribbon and status-bar chrome that run no command button (Styles and style-set gallery tiles, tab names, empty ribbon space, View check boxes, the status bar's view buttons and language menu) no longer take keyboard focus from the page: the caret stays and typing continues, as in Word; text fields such as the font size box still take focus. Tabs, gallery tiles and the status bar's view buttons report their name and state to screen readers (#1093) |
|
||||
| 2026-10-13 | trivial | Interface size: a chosen size is absolute ("100%" is one screen pixel per point, even where the system reports 200 % under a bare window manager) and the new default, Auto, keeps the display scale and shows the size it draws at; 75–300 %; an old saved size becomes the size it drew at; on the web a chosen size cancels browser zoom, Auto follows it (#822) |
|
||||
| 2026-10-13 | trivial | Copy and Cut of a selection holding an equation put HTML with the equation as MathML on the system clipboard beside the text (desktop, through arboard), as MathML in HTML on the clipboard (Word/LibreOffice paste not yet verified; OMML is #1297) (#816). Only copies containing an equation get HTML. Web: text only |
|
||||
|
||||
@@ -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 wordcraft ../wordcraft/packaging/windows/installer-ui.wxs
|
||||
cargo run --locked -p craft-installer -- --check --app wordcraft ../wordcraft/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.
|
||||
@@ -0,0 +1,242 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generated by craft-installer for wordcraft. 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>
|
||||
<!-- 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)" />
|
||||
|
||||
|
||||
<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="C" Type="PushButton" X="15" Y="90" Width="65" Height="20" Text="Cancel" TabSkip="yes">
|
||||
<Publish Event="EndDialog" Value="ErrorCancel" />
|
||||
</Control>
|
||||
<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>
|
||||
|
||||
<Dialog Id="InstallLicense" Width="370" Height="270" Title="[ProductName] Setup">
|
||||
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}License" />
|
||||
<Control Id="License" Type="ScrollableText" X="20" Y="55" Width="330" Height="130" Sunken="yes" TabSkip="no">
|
||||
<Text SourceFile="$(var.LicenseRtf)" />
|
||||
</Control>
|
||||
<Control Id="Accept" Type="CheckBox" X="20" Y="195" Width="330" Height="20" Property="CraftLicenseAccepted" CheckBoxValue="1" Text="I &accept the license terms" />
|
||||
<Control Id="Install" Type="PushButton" X="195" Y="235" Width="75" Height="23" Default="yes" ElevationShield="yes" Text="&Install" Disabled="yes" EnableCondition="CraftLicenseAccepted = "1"" DisableCondition="NOT CraftLicenseAccepted">
|
||||
<Publish Event="EndDialog" Value="Return" Condition="CraftLicenseAccepted = "1"" />
|
||||
</Control>
|
||||
<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="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 review the license." />
|
||||
|
||||
<Control Id="Install" Type="PushButton" X="195" Y="235" Width="75" Height="23" Default="yes" ElevationShield="no" Text="&Next">
|
||||
|
||||
<Publish Event="NewDialog" Value="InstallLicense" 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="&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&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="Cancel" Type="PushButton" X="280" Y="235" Width="70" Height="23" Cancel="yes" Text="Cancel">
|
||||
<Publish Event="EndDialog" Value="Exit" />
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<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="Cancel" Type="PushButton" X="280" Y="235" Width="70" Height="23" Cancel="yes" Text="Cancel">
|
||||
<Publish Event="SpawnDialog" Value="ConfirmCancel" />
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<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="&Yes">
|
||||
<Publish Event="EndDialog" Value="Exit" />
|
||||
</Control>
|
||||
<Control Id="No" Type="PushButton" X="210" Y="70" Width="70" Height="23" Default="yes" Cancel="yes" Text="&No">
|
||||
<Publish Event="EndDialog" Value="Return" />
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<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 = "ALL" OR CraftInstallerOperation = "remove"" />
|
||||
<Control Id="RemovedTitle" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\CraftInstallerTitle}Uninstall completed successfully" Hidden="yes" ShowCondition="REMOVE = "ALL" OR CraftInstallerOperation = "remove"" />
|
||||
<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 = "ALL" OR REINSTALL OR CraftInstallerOperation OR ACTION = "ADMIN"" />
|
||||
<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 = "ALL" OR CraftInstallerOperation = "remove"" />
|
||||
<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 = "repair") AND NOT REMOVE AND CraftInstallerOperation <> "remove" AND ACTION <> "ADMIN"" />
|
||||
<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 = "ADMIN"" />
|
||||
<Control Id="Finish" Type="PushButton" X="280" Y="235" Width="70" Height="23" Default="yes" Cancel="yes" Text="&Finish">
|
||||
<Publish Event="DoAction" Value="LaunchWordcraft" Order="1" Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = "1" AND NOT Installed AND NOT REMOVE AND NOT REINSTALL AND NOT CraftInstallerOperation AND ACTION <> "ADMIN"" />
|
||||
<Publish Event="EndDialog" Value="Return" Order="999" />
|
||||
</Control>
|
||||
<Control Id="Launch" Type="CheckBox" X="20" Y="155" Width="330" Height="20" Property="WIXUI_EXITDIALOGOPTIONALCHECKBOX" CheckBoxValue="1" Text="[WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT]" HideCondition="Installed OR REMOVE OR REINSTALL OR CraftInstallerOperation OR ACTION = "ADMIN"" />
|
||||
</Dialog>
|
||||
|
||||
<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="&Finish">
|
||||
<Publish Event="EndDialog" Value="Return" />
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<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="&Finish">
|
||||
<Publish Event="EndDialog" Value="Return" />
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<!-- 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="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="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>
|
||||
<AdminUISequence>
|
||||
<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.wordcraft" 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=""[INSTALLFOLDER]wordcraft.exe",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=""[$(var.CraftMsiSystemFolder)]msiexec.exe" /x [ProductCode] /qf /norestart" />
|
||||
<RegistryValue Name="QuietUninstallString" Type="string" Value=""[$(var.CraftMsiSystemFolder)]msiexec.exe" /x [ProductCode] /qn /norestart" />
|
||||
<RegistryValue Name="ModifyPath" Type="string" Value=""[$(var.CraftMsiSystemFolder)]msiexec.exe" /i [ProductCode] /qf /norestart" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
<!-- Preserve the app-owned ARPNOMODIFY policy instead of inventing a new default. -->
|
||||
<Component Id="CraftInstallerSettingsNoModify" Guid="*" Condition="ARPNOMODIFY = "1"">
|
||||
<RegistryValue Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\Craft.wordcraft"
|
||||
Name="NoModify" Type="integer" Value="1" KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
|
||||
</Wix>
|
||||
@@ -12,9 +12,8 @@
|
||||
warning when no signing secrets are set).
|
||||
|
||||
Needs: Rust (MSVC toolchain + the target), the Windows SDK (rc.exe, signtool.exe),
|
||||
and WiX v5 with its UI and Util extensions (installer dialogs, "Launch WordCraft" on finish):
|
||||
and WiX v5 with its Util extension (installer dialogs, "Launch WordCraft" on finish):
|
||||
dotnet tool install --global wix --version 5.0.2
|
||||
wix extension add -g WixToolset.UI.wixext/5.0.2
|
||||
wix extension add -g WixToolset.Util.wixext/5.0.2
|
||||
|
||||
.EXAMPLE
|
||||
@@ -126,8 +125,8 @@ $rtf = '{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fswiss\fcharset0 Tahoma;}}\vi
|
||||
|
||||
$Msi = Join-Path $Dist "wordcraft-$Version-windows-$Arch.msi"
|
||||
Invoke-Native 'wix build' {
|
||||
wix build (Join-Path $PSScriptRoot 'wordcraft.wxs') -arch $Arch `
|
||||
-ext WixToolset.UI.wixext -ext WixToolset.Util.wixext `
|
||||
wix build (Join-Path $PSScriptRoot 'wordcraft.wxs') (Join-Path $PSScriptRoot 'installer-ui.wxs') -arch $Arch `
|
||||
-ext WixToolset.Util.wixext `
|
||||
-d "Version=$MsiVersion" -d "BinDir=$Stage" -d "IconPath=$(Join-Path $Root 'assets\app-icon\wordcraft.ico')" `
|
||||
-d "LicenseRtf=$LicenseRtf" `
|
||||
-o $Msi
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
`wix extension add -g WixToolset.UI.wixext/5.0.2` and the same for WixToolset.Util.wixext).
|
||||
Built by package.ps1:
|
||||
|
||||
wix build wordcraft.wxs -arch x64 -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext
|
||||
wix build wordcraft.wxs installer-ui.wxs -arch x64 -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext
|
||||
-d Version=1.2.3 -d BinDir=... -d IconPath=... -d LicenseRtf=... -o out.msi
|
||||
|
||||
Per-machine install into Program Files with a Start Menu shortcut. The installer shows the
|
||||
@@ -37,22 +37,12 @@
|
||||
<Property Id="ARPURLUPDATEINFO" Value="https://getartcraft.com/apps/wordcraft" />
|
||||
<Property Id="ARPHELPLINK" Value="https://discord.gg/artcraft" />
|
||||
|
||||
<!-- Installer UI (#198): WixUI_Minimal is welcome + license, progress, then a finish page so
|
||||
people can see the install worked. Its dialogs and bitmaps are WiX's own
|
||||
(WixToolset.UI.wixext); package.ps1 builds the license RTF from LICENSE-MIT and
|
||||
LICENSE-APACHE. The finish page offers to launch WordCraft (ticked by default) through
|
||||
WixShellExec (WixToolset.Util.wixext); silent installs (/qn) never show it. -->
|
||||
<WixVariable Id="WixUILicenseRtf" Value="$(var.LicenseRtf)" />
|
||||
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch WordCraft" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
|
||||
<Property Id="WixShellExecTarget" Value="[#WordcraftExe]" />
|
||||
<CustomAction Id="LaunchWordcraft" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixShellExec"
|
||||
Impersonate="yes" Return="ignore" />
|
||||
<UI>
|
||||
<ui:WixUI Id="WixUI_Minimal" />
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchWordcraft"
|
||||
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 AND NOT Installed" />
|
||||
</UI>
|
||||
|
||||
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="WordCraft" />
|
||||
@@ -61,7 +51,12 @@
|
||||
|
||||
<Feature Id="Main" Title="WordCraft" Level="1">
|
||||
<ComponentGroupRef Id="WordcraftFiles" />
|
||||
<ComponentGroupRef Id="CraftInstallerSettings" />
|
||||
</Feature>
|
||||
<!-- Shared full UI; app installation policy remains below. -->
|
||||
<!-- Preserve the previous stock WiX wizard's ARP Modify policy. -->
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
<UIRef Id="CraftInstallerUI" />
|
||||
</Package>
|
||||
|
||||
<Fragment>
|
||||
|
||||
Reference in New Issue
Block a user