Commit Graph
77 Commits
Author SHA1 Message Date
FredApps cd9ce83ae7 Merge approved PhotoCraft PR #3554: fix(packaging): launch bundled CLI directly from AppImage
Resolve moved test modules and independent catalog/workflow additions without changing behavior.
2026-10-29 03:31:02 +01:00
FredApps 8abafb2980 Merge approved PhotoCraft PR #7743: packaging: the portable zip holds the files, not a folder with the files in it 2026-10-29 03:29:33 +01:00
FredApps cd3a054cf0 Merge approved PhotoCraft PR #6383: Windows releases ship a symbols zip: the release build's PDBs for profilers (#688) 2026-10-29 03:29:22 +01:00
FredApps b426bd284b Merge approved PhotoCraft PR #4742: fix(macos): bundle photocraft-cli and resolve it from droplet scripts 2026-10-29 03:28:35 +01:00
FredApps e7e6ff1bf2 Merge approved PhotoCraft PR #4547: fix(icons): PhotoCraft icon corner radius matches other Craft apps (#4478) 2026-10-29 03:28:34 +01:00
Dylan M. Taylor 8988161e90 Honor CARGO_BUILD_TARGET in FreeBSD packaging for aarch64 release builds (#9989)
* Honor CARGO_BUILD_TARGET in FreeBSD packaging for aarch64 release builds

package.sh took the arch from uname -m. The release VM is an x86_64 guest that
cross-compiles aarch64 with CARGO_BUILD_TARGET set, so an aarch64 build was
named freebsd-x86_64 and read binaries from the host target dir.

Read the arch from CARGO_BUILD_TARGET when it is set, and read binaries from
target/<triple>/release. Skip the man-page example and the --version run when
cross-building, since the aarch64 binaries cannot run on the x86_64 guest.
Unset CARGO_BUILD_TARGET keeps the old behavior.

* Reject unsupported CARGO_BUILD_TARGET and resolve host-tuple in FreeBSD packaging

Accept only x86_64-unknown-freebsd and aarch64-unknown-freebsd. Any other
target exits 2 before any build, so a non-FreeBSD target cannot be packaged
as an x86_64 FreeBSD archive.

Resolve cargo's host-tuple alias to the real host triple before choosing the
binary dir, so the path matches cargo's output.

* Skip the photocraft-cli --version run when CARGO_BUILD_TARGET is set

A target build's binary may not run on the host (aarch64 on the x86_64 VM),
and under set -e the smoke run would fail the release after the archive is
written. The native path (CARGO_BUILD_TARGET unset) still runs it.
2026-10-28 15:48:48 -04:00
Dylan M. Taylor f1866aed06 Grant network access so update checks can reach GitHub (#9982)
* Grant network access so update checks can reach GitHub

* Fix Flatpak network comment: --share=network is for --control, not update checks
2026-10-28 13:19:42 -04:00
Jeremy 40310f10ea test(installer): cover the compiled folder searches, resolver chains and wizard folder replay
The policy check now reads the compiled AppSearch and RegLocator rows for the
remembered-folder properties (registry root, key, value name and type for both
scopes) and runs the DIR, FOLDER, PATH and legacy exe actions in sequence order
against seeded values, so their precedence and the preset-folder guard cannot
regress. It also replays the wizard folder actions in sequence order (a
remembered folder beats the new-install default, a typed folder beats both) and
requires the machine restore action between the scope page and costing.
2026-10-27 21:13:29 +00:00
Jeremy f82028c00d fix(installer): check the exact architecture-specific machine folder
The pre-signing check matched any folder named Program Files, so it rejected
a valid MSI when Windows resolves Program Files elsewhere, accepted the 64-bit
root for an x86 package and accepted a different app name. Build the expected
folder from the package architecture's resolved root and the policy name.
2026-10-27 19:13:14 +00:00
Jeremy 8f3265c747 fix(installer): resolve the wizard scope and folder correctly
Real msiexec runs (per-user and all-users, silent and wizard) found two defects
that the metadata fixtures cannot see:

- "Only for me" published ALLUSERS=2, so FindRelatedProducts treated the run as
  per-machine and skipped the existing per-user copy. An interactive upgrade
  left two registered products and uninstall left files behind. The page now
  leaves ALLUSERS unset for a per-user choice.
- Windows Installer resolves Program Files once, for the context it started in,
  so choosing "Everyone" in the wizard installed into the user profile with a
  machine-wide registration. The wizard now sets the new-install folder itself
  after the choice (Program Files, Program Files (x86) for 32-bit, or the
  per-user Programs folder). Silent installs are unchanged.

check-install-policy.ps1 now fails if either regresses.
2026-10-27 15:32:31 +00:00
Jeremy bf4e8214fc fix(installer): correct the wizard and shortcut description in the release docs; harden install-policy checks 2026-10-27 12:01:33 +00:00
Jeremy f35b356f87 fix(installer): address scope and ARM64 review feedback 2026-10-27 12:01:33 +00:00
Jeremy c19d201748 feat(installer): default to a per-user ArtCraft folder 2026-10-27 12:01:33 +00:00
Jeremy 9956352be0 packaging(windows): dual-scope MSI, per-user by default, under the ArtCraft suite folder
Microsoft "Single Package Authoring": Package/Scope="perUserOrMachine"
writes ALLUSERS=2 and MSIINSTALLPERUSER=1, so the package installs
per-user by default (no UAC prompt) into %LOCALAPPDATA%\Programs\ArtCraftPhotoCraft (Windows Installer redirects the ProgramFiles folder in a
per-user install), and per-machine into Program Files\ArtCraft\PhotoCraft
through the setup wizard's "Install for all users" box or msiexec
ALLUSERS=1. Registry follows the context automatically: HKMU writes HKCU
per-user and HKLM per-machine, HKCR classes land in HKCU\Software\Classes
per-user, and components are keyed on registry values (ICE38).

Upgrades update the copy registered in the same context: the remembered
InstallDir is read from HKLM and HKCU separately, and installing in the
other context while a copy already exists is stopped with a message naming
the fix rather than creating a silent duplicate. The existing UpgradeCode,
ProductCode behaviour and per-machine upgrades are unchanged.

The Start Menu shortcut becomes non-advertised in its own component (an
advertised shortcut must target its component keypath, which is now a
registry value), as the ArtCraft Launcher's MSI already does.

Built and checked with WiX 5.0.2: full ICE validation and ICE50 pass, and
the built package's Property table, launch conditions, directory chain and
registry roots were read back from the MSI.
2026-10-27 12:01:33 +00:00
Dylan M. Taylor d3b0a76c3f lint: drop blank lines after doc comments and attributes; tidy IME else, closure, shell tests (#9576)
- Remove blank lines between doc comments or #[test] attributes and the item (S8855, S8856)
- type_tool: drop the redundant else after continue in the IME event arm (S8862)
- ui_state: pass OsStr::to_os_string instead of an equivalent closure (S1612)
- macos packaging: [[ ]] for the attempt test (S7688); bind validate_staple's argument to a local (S7679)
2026-10-25 22:08:50 -04:00
aiongg b078e0e8a6 Panels: review follow-ups to the shared docking and egui 0.37 merges (#9575) 2026-10-25 22:03:26 -04:00
5c9192bbe2 Migrate PhotoCraft to egui 0.37 (#9363)
* egui037: name each control for what it sets, and order the eager fonts for the UI

CodeRabbit review follow-ups on the migration: the Artboard X/Y fields, the CAF
dropdowns, the channel Solidity field, the GPU backend/adapter dropdowns, the
corner radius fields and the canvas extension colour all had an accessible name
that named another control (or was shared by four). The eager craft-fonts
registration now puts the UI Japanese face first, as the lazy loader does, so
Japanese UI is not drawn in the manifest's Mincho face and shared Han characters
do not take Japanese forms.

Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* egui037: rustfmt the eager craft-font order

---------

Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-25 15:04:35 -04:00
Erica Franco 938542c78b Security: pin and verify the Windows signing client (#2936) (#3938)
sign.ps1 downloaded Microsoft.Trusted.Signing.Client from the unversioned
NuGet URL and loaded its dlib into signtool, next to the Azure signing
secrets, with no version or integrity check.

Download a pinned version (1.0.95, which the unversioned URL serves today)
and refuse it unless its SHA-256 matches, so upgrading the client is a
deliberate change in the repository. The release docs say where the pin
lives.
2026-10-25 12:32:10 -04:00
aiongg 1a93824602 Packaging: --skip-build without the man-and-completions example leaves the CLI docs out
Prebuilt binaries packaged with --skip-build failed when the example
wasn't built with them. The package now leaves photocraft-cli's man pages
and completions out with a warning, as a cross build without an emulator
does.
2026-10-24 18:02:12 +00:00
aiongg 8950052c57 Packaging: build the man-and-completions example in its own cargo run
Built in the same run as the release binaries, the example's
dev-dependencies would have their features unified into them.
2026-10-24 17:27:54 +00:00
IvanandClaude Opus 5.5 cb730955f6 feat(freebsd): ship photocraft-cli's man pages and completions (#7071)
The FreeBSD tarball carries them in the ports layout: man/man1,
share/bash-completion/completions, share/zsh/site-functions and
share/fish/vendor_completions.d.

The man-and-completions example is built in the same cargo build as
the binaries (--bins --example), and package.sh runs it from the
target dir. With --skip-build it must already be built, or the script
says so and fails. A dry run builds nothing, so it leaves them out.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:27 +03:00
IvanandClaude Opus 5.5 92863642a7 feat(linux): ship photocraft-cli's man pages and completions (#7071)
The deb, rpm and tarball install the man pages in /usr/share/man/man1
and the bash, zsh and fish completions in bash-completion's
completions, /usr/share/zsh/site-functions and fish's
vendor_completions.d. Fedora's and openSUSE's zsh read site-functions;
Debian's reads /usr/share/zsh/vendor-completions instead (its zsh is
configured with --enable-additional-fpath), so the deb also carries a
symlink there.

The man-and-completions example is built in the same cargo build as
the binaries (--bins --example, same profile, features and target),
and package.sh runs it from the target dir. A cross build runs it
through $EMULATOR, as the --version smoke test does, and without one
packages no pages or completions, with a warning. With --skip-build the
example must already be built, or the script says so and fails. The
pages aren't gzipped, like the other packaged docs.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:27 +03:00
Brandon ThomasandClaude Opus 5.5 4991f6377e packaging(macos): retry stapling when Apple's ticket lookup drops (#8995)
* packaging(macos): retry stapling when Apple's ticket lookup drops

The 0.15.0 release's macOS job failed after both notarizations were
accepted: stapling the DMG lost its connection to Apple's CloudKit
ticket service (error 68). Retry stapling up to five times with a
growing pause before failing the job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* packaging(macos): retry stapler validation too

The re-run of the 0.15.0 macOS job stapled the DMG, then lost the
CloudKit connection in `stapler validate` (error 68 again). Retry
staple + validate as a pair in package.sh, and retry the validation
verify.sh does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:11:00 -04:00
Dylan M. Taylor 75f5a9c126 packaging: use [[ for bash conditional tests (SonarCloud S7688) (#8914)
* packaging: use [[ for bash conditional tests (SonarCloud S7688)

SonarCloud flags every bare [ conditional in bash scripts under packaging/ as a reliability smell (rule S7688). Convert the flagged [ ... ] tests to [[ ... ]]: safer word-splitting and no glob surprises, same semantics for these string and file tests.

* packaging/web: parse size overrides as base 10 under [[
2026-10-23 15:03:12 -04:00
Higgs32584andClaude Opus 5.5 a1b5ecd16c Merge upstream main into fix/688-windows-symbols
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:18:24 -04:00
Hamza fd6fc92015 packaging: the portable zip holds the files, not a folder with the files in it
`Compress-Archive -Path $Portable` archives the staging *folder*, so the zip
contained

  photocraft-<version>-windows-<arch>-portable\photocraft.exe

rather than `photocraft.exe` at its root -- which is what this script's own
synopsis promises, what portable mode needs (portable.txt sits beside the exe,
and extracting to a folder of the user's choosing now nests one more), and what
the launcher looks for when it installs a portable build
(storytold/craft-launcher#627). `<folder>\*` archives the contents, which is
what VectorCraft's windows7.ps1 already does for its portable zip.

Nothing read the layout back, which is how it drifted, so the three files are
checked in the archive after it is written.

Fixes #7699.
2026-10-19 16:56:22 +05:00
Korg1ch cb1b479452 fix(web): start the app when the server already inflated its gzip part (#7639) 2026-10-19 12:37:54 +03:00
Michael Higgins f45d15d211 Merge branch 'main' into fix/688-windows-symbols 2026-10-18 10:15:51 -04:00
haranishi dd21e325b5 fix(appimage): expose the bundled CLI and MCP entry point 2026-10-18 10:55:00 +09:00
Diego Chagas 235e7578f3 Print through CUPS over IPP and the print portal, so File › Print works in the Flatpak (#2728) (#3017)
Main prints through craft-print, which runs `lp`; a Flatpak has no `lp`,
so File › Print fails there. This keeps craft-print as the route wherever
`lp` is on the path (and on Windows) and adds two more behind it:

- `photocraft-ipp`, a pure-Rust IPP client (CUPS-Get-Printers/Classes/
  Default, Print-Job over HTTP/1.1) that talks to CUPS over its socket or
  TCP; the Flatpak manifests grant --socket=cups for it. A `printServer`
  named in the request always takes this route. `file.print.printers`
  lists the queues.
- The XDG Print portal as the app's spooler (`Session::print_spooler`),
  for a sandbox without the CUPS socket or a system without a printer:
  the system print dialog, seeded with copies, paper and orientation.
  PHOTOCRAFT_PRINT_PORTAL=1 prefers it, =0 removes it.

A route that exists but fails (unknown printer, refused job) is an error;
only a missing route moves on. Results gain `backend` (lp, windows, ipp,
portal). docs/printing.md describes the routes.
2026-10-17 15:55:54 -06:00
Korg1ch 4c881326ea fix(windows): keep crash-recovery autosaves out of the roaming profile (#5648) (#6412)
* fix(windows): keep crash-recovery autosaves out of the roaming profile (#5648)

* test(windows): cover the local recovery folder and moving old autosaves (#5648)
2026-10-16 20:19:06 -06:00
wwew 93f6376c5e docs: write product name as PhotoCraft in package descriptions and comments (#5615) 2026-10-16 16:59:41 -06:00
Korg1ch 3c75db7a80 fix(linux): avoid duplicate AppImage desktop launcher when managed by AM / AppMan (#6392) (#6394)
* fix(linux): avoid duplicate AppImage desktop launcher when managed by AM / AppMan (#6392)

* test(linux): cover AM / AppMan launcher detection in AppRun smoke test (#6392)
2026-10-16 16:37:17 -06:00
Higgs32584andClaude Opus 5.5 7cf4f27cf6 Windows releases ship a symbols zip: the release build's PDBs for profilers (#688)
package.ps1 now also writes photocraft-<v>-windows-<arch>-symbols.zip with photocraft.pdb and
photocraft_cli.pdb from the same optimized build as the signed exes, plus symbols.txt on loading
them in WPA, Superluminal or Visual Studio. It's a separate optional asset, so the MSI and
portable zip keep their size; the shared release workflow uploads everything in dist/release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 19:42:38 -04:00
Mara Johnson a08f7a2295 Merge remote-tracking branch 'origin/main' into fix/4478-icon-corner-radius 2026-10-15 14:56:43 -04:00
df055f3dcb CI: fix the tests that keep main red, and ship the wasm as compressed parts (#5926, #5302) (#5941)
* Fix two ui-egui tests broken on main by concurrent landings

- profile_open_tests: #5614 renamed the built-in profile to "(PhotoCraft)" after #5208
  hard-coded "(Photocraft)" in the Use-embedded button label.
- smart_object_curves_preview_keeps_its_size_on_a_reduced_proxy (#5779): the reduced
  square is in the right place, but its corner pixels are 220/255 after the smart
  object's resampling; assert full coverage inside and over half at the corners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Web: ship the wasm as compressed parts behind a boot module (#5302)

The release site held the app's wasm as one file, which sits at the 25 MiB
per-file cap of Cloudflare Pages, so the 24 MiB gate failed unrelated PRs.

packaging/web/package.sh now gzips the wasm and cuts it into parts of at
most 20 MiB under app/, and ships the page of a new crate,
apps/photocraft-web-boot. That module fetches the parts in order, has the
browser inflate them (DecompressionStream) and passes the stream to the
app's own wasm-bindgen loader, so the browser still compiles while it
downloads. A failed download is retried twice, then the page says so.

The check is now that no file in the site is over 24 MiB. trunk build in
apps/photocraft-web and the Docker image still give one .wasm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Web boot: inflate the parts in the module where the browser can't (#5302)

* Tests: the adjustment-layer drag preview test follows #5778 (#5926)

#5778 caps a live adjustment-layer preview by the view's zoom, so at 100 %
the drag shows full resolution even with Low Resolution Previews on. The
test still expected the reduced copy there. It now checks that at 100 % every
column shows by default, and moves the low-res on/off comparison to 50 %,
where the reduced copy is still used: on, the drag shows only the (inverted)
black columns; off, it matches the committed view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:21:30 +02:00
9a905959ed fix(linux): install the AppImage menu entry when AppImageLauncher skips it (#5551)
AppImageLauncher exports DESKTOPINTEGRATION even after
X-AppImage-Integrate=false makes it skip the AppImage. AppRun treated that
variable as "already integrated" and wrote no .desktop file, so the menu
stayed empty. Skip our entry only when another .desktop file's Exec already
points at this AppImage.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-14 15:03:32 -07:00
Nigel Ihza Firdausy 0cdd537480 fix(macos): bundle the CLI and resolve it from generated droplets 2026-10-13 11:01:39 +07:00
20539b5dc3 i18n(sr): add a Serbian (Latin, Ekavian) UI catalog (#4436)
* i18n(sr): add a Serbian (Latin, Ekavian) UI catalog

Adds sr.tsv with 4,022 rows: every key the i18n-coverage tool measures
plus the union of the dynamic labels the other catalogs translate. The
catalog is written in Latin script, Ekavian; every Serbian locale
(sr-RS, sr-Latn-*, sr_RS@latin and Cyrillic sr-Cyrl-*) resolves to it.
Plurals use the one/few/many rule (plural_russian) with three forms per
@plural entry.

Original translations from the plain meaning of the English labels; no
proprietary localisation resources were used (ATTRIBUTION.md and
LICENSE-translations.txt updated). Tests cover locale resolution,
contextual and id overrides, placeholders, and plurals across teens and
compound counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Info.plist: declare the Serbian localization

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dejan Port <snork1982@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 22:50:20 -04:00
0a32b3593d Add Krita (.kra) import (#4388)
Open .kra and .krz with paint layers (8/16-bit RGBA and gray, any offset), nested
and pass-through groups, names, visibility, opacity, locks, blend modes and
transparency masks, plus the image's ICC profile and resolution. Vector, filter,
fill, file and clone layers and the other mask kinds are left out with a warning;
float, CMYK and Lab documents open as Krita's merged image (preview). Import only:
Save suggests .ora. Checked against 35 Krita 5.2.9-authored files.

Co-authored-by: Scott Campbell <251750033+scottcampbelldata@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:41:38 -04:00
f80e57fb8a Fix macOS system dialog localization (#4261)
* Fix macOS system dialog localization

* Info.plist: declare the Hungarian and Turkish localizations too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:16:46 -04:00
Mara Johnson d84ad4c22f fix(packaging): Windows and Linux icons keep the rounded-square corners (#4478) 2026-10-12 19:02:42 -04:00
skylar taylor-barrick 7b0317ff41 Merge PhotoCraft 0.8.2 and stabilize background-job tests 2026-10-12 20:21:10 +02:00
skylar taylor-barrick a301e31864 Merge current main fixes and validate craft-print rollout 2026-10-12 19:39:05 +02:00
Claude 7f9c4394c1 Merge #4253: wasm back under the 24 MiB gate
Combined with #4147's opt-level "z" list. Conflict in Cargo.toml's
wasm-release profile: kept #4147's photocraft-psd = "z" and dropped
#4253's "s" entry for it (one table per package). Measured with trunk
--release: main 184c226a is 25,235,857 bytes (over the 25,165,824 gate);
with this merge, 24,161,781.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:56:54 -04:00
Korg1ch dc35ce7630 fix(linux): avoid duplicate AppImage desktop launcher (#3623) (#3641) 2026-10-12 10:35:47 -04:00
JGFandClaude Opus 5.5 5a0e2e2658 Web: wasm back under the 24 MiB gate with ~1 MiB headroom (25.38 -> 24.17 MB)
The "web build and wasm size gate" job failed on main and every PR: the
release `_bg.wasm` measured 25,379,677 bytes at 8d49ecf5, over the
25,165,824-byte limit in packaging/web/package.sh.

- egui's default fonts (Ubuntu Light, Hack, Noto Emoji, emoji-icon-font)
  were embedded uncompressed (1.4 MB) by egui's `default_fonts` feature.
  The app now builds egui without it; ui-egui's build.rs deflates the
  same files from the `epaint_default_fonts` crate (a build-dependency,
  so no font files are committed) and `egui_fonts.rs` rebuilds egui's
  exact default stack from them on first use. Test builds keep the
  feature as the oracle: the rebuilt definitions equal egui's byte for
  byte.
- Translation catalogs: every catalog repeated the same English sources.
  build.rs now stores the `context<TAB>source` keys once and each catalog
  as references plus translations, losslessly (`text()` still returns
  each .tsv byte for byte; malformed lines are kept as they are).
- wasm-release profile: opt-level "z" for PhotoCraft's own crates.
  Dependencies stay at "s" (at "z" the wasm grew 0.4 MB, measured), the
  file-decoding crates (codecs, psd, raw, heif, vector) stay at "s" and
  the pixel crates at 3, so decoding and filters keep their speed.

Measured with `trunk build --release` (trunk 0.21.14, wasm-opt v123 -Oz):
  upstream main 8d49ecf5      25,379,677 bytes
  this change                 24,167,186 bytes (-1,212,491)
  gate                        25,165,824 bytes (998,638 headroom)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:59:25 -04:00
Brandon ThomasandClaude Opus 5.5 0d6e658e73 PSD: rasterized fill and smart layers stay rasterized after save and reopen (#4034)
* fix(psd): rasterized fill and smart layers stay rasterized after save and reopen

Layer > Rasterize > Fill Content / Smart Object swapped the layer's content
to pixels but kept the fill (SoCo/GdFl/PtFl) or placed-layer (SoLd/PlLd/SoLE)
blocks read from the PSD. Written back, they made the reopened file a fill
layer or smart object again and discarded the pixels. Photoshop writes none
of them for a pixel layer.

- Rasterize drops those blocks from the layer.
- The PSD exporter never writes fill, placed, type (TySh), shape-content
  (vscg/vstk), adjustment or CgEd blocks for a raster layer, whichever
  command made it one; a vector mask (vmsk/vsms with its vogk) stays.

Tests: synthetic fill + smart object rasterize/save/reopen, the exporter
backstop with stale blocks, and Photoshop-authored corpus files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(psd): keep a pixel layer's vstk when it has no vector mask, as Photoshop does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:11:33 -04:00
Atos LinsandBrandon Thomas da3da0942c Linux: StartupWMClass is the window's app id (#3481)
* Linux: StartupWMClass is the window's app id

The desktop entry said StartupWMClass=photocraft, but the window carries
the app id ai.storyteller.photocraft (APP_ID) on Wayland and under X11
alike, so nothing that matches a window to its launcher by that key ever
matched it. VectorCraft, GridCraft and WordCraft already use the app id.

A test reads the entry and compares it with APP_ID.

* Contributors: add atoslins (Atos Lins)

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 03:28:09 -04:00
Dan HiornsandClaude Opus 5.5 20cb95bc27 Windows installer: refuse Windows 7 and 8 with a clear message (#2310) (#3261)
The Rust standard library has needed Windows 10 since 1.78, so on
Windows 7 and 8 PhotoCraft fails at launch with "The procedure entry
point ProcessPrng could not be located in the dynamic link library
bcryptprimitives.dll" (#2310). The MSI installed anyway.

It now has a launch condition: a registry search sets WINDOWS10ORLATER
from HKLM\...\Windows NT\CurrentVersion\CurrentMajorVersionNumber,
which exists only from Windows 10 on (in the 32- and 64-bit views), and
without it the installer stops with "PhotoCraft needs Windows 10 or
later." Repair and uninstall still run (Installed). MSI's VersionNT
isn't used: it has reported both 603 and 1000 on Windows 10.

The README's Windows downloads now say Windows 10 or later is required;
the portable zip can't check, as Windows rejects the exe before any of
its code runs.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:26:36 -04:00