mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
* M14: "Edit with PdfCraft" on the File Explorer menu of PDF files (#682) The Windows MSI adds a classic shell verb under HKLM\Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit that runs "pdfcraft.exe" "%1" with the app's icon. It appears whichever app is the default for .pdf and never changes that default. On Windows 11 it is under "Show more options": a first-level entry needs an IExplorerCommand handler and package identity, which an MSI doesn't have. MultiSelectModel is Document: several selected PDFs (up to 15) start one launch each, and the existing single-instance hand-off opens them as tabs of one window. The verb has its own component, conditioned on a new secure public property INSTALLPDFCONTEXTMENU (default 1, a welcome-dialog checkbox, INSTALLPDFCONTEXTMENU=0 for unattended installs), following INSTALLDESKTOPSHORTCUT. Its menu value is the key path, so uninstall removes every value. The portable zip still registers nothing. test-msi.ps1 checks the compiled Registry, Component, Property and CheckBox rows, and that the .pdf default is never written. Docs: README, docs/releasing.md, parity notes, ROADMAP log. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Leave ROADMAP.md to main (its log moved on); the change is described in the PR --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]>
This commit is contained in:
6 files changed
+79
-14
No files matched your search
@@ -448,14 +448,18 @@ The portable zip runs from any folder, a USB stick included. Its `portable.txt`
|
||||
logs and crash recovery in a `PdfCraftData` folder next to `pdfcraft.exe`, so nothing is written to
|
||||
`%APPDATA%`; delete that file to use the normal per-user folders.
|
||||
|
||||
The MSI installs for all users and requires administrator privileges. For unattended deployment
|
||||
without a desktop shortcut, run from an elevated terminal:
|
||||
The MSI installs for all users and requires administrator privileges. It adds "Edit with PdfCraft"
|
||||
to the right-click menu of PDF files in File Explorer (on Windows 11, under "Show more options"),
|
||||
without changing which app opens PDFs by default. For unattended deployment without a desktop
|
||||
shortcut, run from an elevated terminal:
|
||||
|
||||
```powershell
|
||||
msiexec /i "pdfcraft-<ver>-windows-x64.msi" /qn /norestart INSTALLDESKTOPSHORTCUT=0
|
||||
```
|
||||
|
||||
Use the MSI for your architecture. Per-user installation overrides are not supported.
|
||||
Add `INSTALLPDFCONTEXTMENU=0` to leave out the right-click menu entry. Pass both on upgrades too,
|
||||
since each install applies its own choices. Use the MSI for your architecture. Per-user
|
||||
installation overrides are not supported.
|
||||
|
||||
### macOS
|
||||
|
||||
|
||||
+12
-4
@@ -119,10 +119,18 @@ neither the MSI nor the portable zip needs the Visual C++ redistributable.
|
||||
this.
|
||||
- `installer-ui.wxs` supplies native welcome, maintenance, progress, files-in-use and outcome
|
||||
dialogs. Full UI confirms success with Finish; failures and cancellations have distinct messages.
|
||||
The welcome dialog has a "Create a desktop shortcut" checkbox, ticked by default. `/qn` and `/qb`
|
||||
stay unattended and create the desktop shortcut unless `INSTALLDESKTOPSHORTCUT=0` is passed.
|
||||
- `test-msi.ps1 <file.msi>` checks the compiled shortcut and dialog tables. Packaging runs it in a
|
||||
child process before signing, so the MSI isn't held open when signtool runs. The ARM64 install
|
||||
The welcome dialog has a "Create a desktop shortcut" checkbox and an "Edit with PdfCraft"
|
||||
right-click menu checkbox, both ticked by default. `/qn` and `/qb` stay unattended and create
|
||||
both unless `INSTALLDESKTOPSHORTCUT=0` or `INSTALLPDFCONTEXTMENU=0` is passed.
|
||||
- The PDF right-click entry (#682) is a classic verb under
|
||||
`HKLM\Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit`, so it shows whichever app
|
||||
is the default for `.pdf` and never changes that default. Several selected PDFs (up to 15, the
|
||||
limit for a `Document` verb) start one `pdfcraft.exe "<file>"` each, and the single-instance
|
||||
hand-off opens them as tabs. On Windows 11 it is under "Show more options"; a first-level entry
|
||||
needs an `IExplorerCommand` handler and package identity (a sparse MSIX package), which the MSI
|
||||
doesn't have.
|
||||
- `test-msi.ps1 <file.msi>` checks the compiled shortcut, context-menu and dialog tables. Packaging
|
||||
runs it in a child process before signing, so the MSI isn't held open when signtool runs. The ARM64 install
|
||||
smoke test checks that both all-users shortcuts point at the installed `pdfcraft.exe` and are
|
||||
removed on uninstall, and that `INSTALLDESKTOPSHORTCUT=0` skips the desktop one.
|
||||
- The portable zip holds both executables, the README, the licences, the OFL licence of each
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Original, text-only native MSI dialogs. No third-party UI artwork or custom-action DLLs.
|
||||
Full UI gets welcome (with the desktop-shortcut checkbox)/progress/outcome screens; /qn and /qb stay unattended. -->
|
||||
Full UI gets welcome (with the desktop-shortcut and PDF context-menu checkboxes)/progress/outcome screens; /qn and /qb stay unattended. -->
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
<Fragment>
|
||||
<UI Id="PdfcraftInstallerUI">
|
||||
@@ -14,6 +14,8 @@
|
||||
Text="Setup will install [ProductName] for all users and add it to the Start menu. Click Install to continue." />
|
||||
<Control Id="DesktopShortcut" Type="CheckBox" X="20" Y="105" Width="330" Height="17"
|
||||
Property="INSTALLDESKTOPSHORTCUT" CheckBoxValue="1" Text="Create a &desktop shortcut" />
|
||||
<Control Id="PdfContextMenu" Type="CheckBox" X="20" Y="123" Width="330" Height="17"
|
||||
Property="INSTALLPDFCONTEXTMENU" CheckBoxValue="1" Text="Add "&Edit with PdfCraft" to the right-click menu of PDF files" />
|
||||
<Control Id="Install" Type="PushButton" X="195" Y="145" Width="75" Height="23" Default="yes" ElevationShield="yes" Text="&Install">
|
||||
<Publish Event="EndDialog" Value="Return" />
|
||||
</Control>
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
wix build pdfcraft.wxs installer-ui.wxs -arch x64 -d Version=1.2.3 -d BinDir=... -d ModelsDir=... -d IconPath=... -o out.msi
|
||||
|
||||
Per-machine install into Program Files, a Start Menu shortcut, a desktop shortcut (on by default,
|
||||
optional), an App Paths entry, and PdfCraft registered as a PDF handler (offered under "Open with"
|
||||
and in Default apps, never forced as the default). Files opened with PdfCraft arrive on its
|
||||
command line.
|
||||
optional), an App Paths entry, PdfCraft registered as a PDF handler (offered under "Open with"
|
||||
and in Default apps, never forced as the default), and "Edit with PdfCraft" on PDF files'
|
||||
right-click menu (on by default, optional). Files opened with PdfCraft arrive on its command
|
||||
line.
|
||||
-->
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
<Package
|
||||
@@ -40,6 +41,9 @@
|
||||
INSTALLDESKTOPSHORTCUT=0 on the msiexec command line for unattended installs. Secure, so the
|
||||
choice made in the (unelevated) UI reaches the elevated install. -->
|
||||
<Property Id="INSTALLDESKTOPSHORTCUT" Value="1" Secure="yes" />
|
||||
<!-- "Edit with PdfCraft" on the right-click menu of PDF files in File Explorer (#682): on by
|
||||
default, the same way, and INSTALLPDFCONTEXTMENU=0 for unattended installs without it. -->
|
||||
<Property Id="INSTALLPDFCONTEXTMENU" Value="1" Secure="yes" />
|
||||
<UIRef Id="PdfcraftInstallerUI" />
|
||||
|
||||
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||
@@ -136,6 +140,21 @@
|
||||
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Shortcuts" Name="Desktop" Type="integer" Value="1" KeyPath="yes" />
|
||||
</Component>
|
||||
|
||||
<!-- PDF context menu (#682): "Edit with PdfCraft" opens the file in PdfCraft whichever app is
|
||||
the default for .pdf, which it leaves alone (SystemFileAssociations, like the image
|
||||
menu). Several PDFs selected (up to 15, Windows' limit for this kind of entry) start one
|
||||
launch each, and the single-instance hand-off opens them as tabs of one window. On
|
||||
Windows 11 the entry is under "Show more options": the first-level menu only takes
|
||||
packaged apps' (IExplorerCommand) entries. Its own component with an HKLM key path, so
|
||||
INSTALLPDFCONTEXTMENU=0 leaves it out and uninstall removes it. -->
|
||||
<Component Id="PdfcraftPdfContextMenu" Guid="439F084F-657D-4AC2-A4CC-F099C518D71F"
|
||||
Condition="INSTALLPDFCONTEXTMENU = 1">
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit" Value="Edit with PdfCraft" Type="string" KeyPath="yes" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit" Name="Icon" Value=""[#PdfcraftExe]",0" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit" Name="MultiSelectModel" Value="Document" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit\command" Value=""[#PdfcraftExe]" "%1"" Type="string" />
|
||||
</Component>
|
||||
|
||||
<Component Id="PdfcraftCli">
|
||||
<File Id="PdfcraftCliExe" Source="$(var.BinDir)\pdfcraft-cli.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
|
||||
@@ -117,6 +117,38 @@ foreach ($ext in @('png', 'jpg', 'jpeg', 'tif', 'tiff', 'gif', 'bmp', 'jp2', 'j2
|
||||
Assert-Equal $selection[0] 'Single' "$ext context menu selection"
|
||||
}
|
||||
|
||||
# PDF context menu (#682): "Edit with PdfCraft" opens the file in PdfCraft. Its own component, gated
|
||||
# by INSTALLPDFCONTEXTMENU (default 1, secure, welcome checkbox), with its menu value as key path so
|
||||
# uninstall removes it. Several selected PDFs each start pdfcraft.exe "%1" (Document), which the
|
||||
# single-instance hand-off opens as tabs. The .pdf default is never set, so the user's default app
|
||||
# stays.
|
||||
$pdfKey = 'Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit'
|
||||
$pdfMenu = Read-Row ('SELECT `Registry`, `Value`, `Component_`, `Root` FROM `Registry` WHERE `Key` = ''' + $pdfKey + ''' AND `Name` IS NULL') 4
|
||||
Assert-Equal $pdfMenu[1] 'Edit with PdfCraft' 'PDF context menu label'
|
||||
Assert-Equal $pdfMenu[2] 'PdfcraftPdfContextMenu' 'PDF context menu component'
|
||||
Assert-Equal $pdfMenu[3] '2' 'PDF context menu HKLM root'
|
||||
foreach ($value in @(@('Icon', '"[#PdfcraftExe]",0'), @('MultiSelectModel', 'Document'))) {
|
||||
$row = Read-Row ('SELECT `Value`, `Component_` FROM `Registry` WHERE `Key` = ''' + $pdfKey + ''' AND `Name` = ''' + $value[0] + '''') 2
|
||||
Assert-Equal $row[0] $value[1] "PDF context menu $($value[0])"
|
||||
Assert-Equal $row[1] 'PdfcraftPdfContextMenu' "PDF context menu $($value[0]) component"
|
||||
}
|
||||
$pdfCommand = Read-Row ('SELECT `Value`, `Component_` FROM `Registry` WHERE `Key` = ''' + $pdfKey + '\command''') 2
|
||||
Assert-Equal $pdfCommand[0] '"[#PdfcraftExe]" "%1"' 'PDF context menu command'
|
||||
Assert-Equal $pdfCommand[1] 'PdfcraftPdfContextMenu' 'PDF context menu command component'
|
||||
$pdfComponent = Read-Row 'SELECT `Condition`, `KeyPath` FROM `Component` WHERE `Component` = ''PdfcraftPdfContextMenu''' 2
|
||||
Assert-Equal $pdfComponent[0] 'INSTALLPDFCONTEXTMENU = 1' 'PDF context menu condition'
|
||||
Assert-Equal $pdfComponent[1] $pdfMenu[0] 'PDF context menu key path'
|
||||
$pdfDefault = Read-Row 'SELECT `Value` FROM `Property` WHERE `Property` = ''INSTALLPDFCONTEXTMENU''' 1
|
||||
Assert-Equal $pdfDefault[0] '1' 'PDF context menu default'
|
||||
if (($secure[0] -split ';') -notcontains 'INSTALLPDFCONTEXTMENU') { throw "INSTALLPDFCONTEXTMENU is not secure: '$($secure[0])'" }
|
||||
$pdfCheckbox = Read-Row 'SELECT `Type`, `Property`, `Text` FROM `Control` WHERE `Dialog_` = ''InstallWelcome'' AND `Control` = ''PdfContextMenu''' 3
|
||||
Assert-Equal $pdfCheckbox[0] 'CheckBox' 'Welcome PDF context-menu control'
|
||||
Assert-Equal $pdfCheckbox[1] 'INSTALLPDFCONTEXTMENU' 'Welcome PDF context-menu property'
|
||||
if ($pdfCheckbox[2] -notmatch 'Edit with PdfCraft') { throw "Welcome PDF context-menu label: '$($pdfCheckbox[2])'" }
|
||||
$pdfChecked = Read-Row 'SELECT `Value` FROM `CheckBox` WHERE `Property` = ''INSTALLPDFCONTEXTMENU''' 1
|
||||
Assert-Equal $pdfChecked[0] '1' 'Welcome PDF context-menu value'
|
||||
Assert-NoRow 'SELECT `Registry` FROM `Registry` WHERE `Key` = ''Software\Classes\.pdf'' AND `Name` IS NULL' '.pdf default app'
|
||||
|
||||
# Negative sequences are Windows Installer's success/user-exit/failure paths. Only full UI
|
||||
# shows these dialogs: an unattended /qn or /qb install must never wait for a Finish click.
|
||||
foreach ($exit in @(@('InstallComplete', '-1'), @('InstallCancelled', '-2'), @('InstallFailed', '-3'))) {
|
||||
@@ -147,4 +179,4 @@ $rm = Read-Row 'SELECT `Dialog` FROM `Dialog` WHERE `Dialog` = ''MsiRMFilesInUse
|
||||
Assert-Equal $rm[0] 'MsiRMFilesInUse' 'Files-in-use dialog'
|
||||
[void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Database)
|
||||
[void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Installer)
|
||||
Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog, OCR models'
|
||||
Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut (default on, checkbox), optional PDF context menu (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog, OCR models'
|
||||
@@ -23,7 +23,7 @@ status = "shipped"
|
||||
commands = ["file.open"]
|
||||
tools = ["doc_open"]
|
||||
evidence = ["crates/automation/tests/automation.rs::open_inspect_render_and_find", "crates/ui-egui/tests/ui.rs::opening_a_pdf_shows_comments_and_bookmarks", "crates/ui-egui/tests/ui.rs::files_and_quit_from_the_operating_system"]
|
||||
notes = "File ▸ Open, the command line (Linux and Windows file associations), and on macOS Finder, Open With and the Dock (Apple events; the bundle declares PDF as a document type)"
|
||||
notes = "File ▸ Open, the command line (Linux and Windows file associations), the Windows MSI's \"Edit with PdfCraft\" entry on PDF files' File Explorer right-click menu (#682; several selected PDFs open as tabs of one window), and on macOS Finder, Open With and the Dock (Apple events; the bundle declares PDF as a document type). Not yet: a first-level Windows 11 menu entry (needs package identity) or Combine from the file manager (#585)"
|
||||
|
||||
[[feature]]
|
||||
id = "core.repair-broken-files"
|
||||
@@ -10035,7 +10035,7 @@ status = "planned"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = []
|
||||
notes = "Windows MSI creates a Start menu shortcut and an optional (default on) desktop shortcut and confirms full-UI completion with Finish (#143); packaging/windows/test-msi.ps1 checks the compiled MSI and windows-arm64.yml checks installation/removal. PDF files use a dedicated embedded document icon when PdfCraft is chosen as the default reader (confirmed in Windows Explorer); first-page thumbnails remain unsupported. Signed installers across platforms remain planned."
|
||||
notes = "Windows MSI creates a Start menu shortcut, an optional (default on) desktop shortcut and an optional (default on, INSTALLPDFCONTEXTMENU) \"Edit with PdfCraft\" File Explorer menu entry for PDF files (#682), and confirms full-UI completion with Finish (#143); packaging/windows/test-msi.ps1 checks the compiled MSI and windows-arm64.yml checks installation/removal. PDF files use a dedicated embedded document icon when PdfCraft is chosen as the default reader (confirmed in Windows Explorer); first-page thumbnails remain unsupported. Signed installers across platforms remain planned."
|
||||
|
||||
[[feature]]
|
||||
id = "misc.web-deploy"
|
||||
|
||||
Reference in new issue
Block a user