- Colours: AlphaColor components are straight, not premultiplied; use
them directly instead of dividing by alpha (the opacity test now
checks RGB, not just alpha).
- Text: key families by font content key, not PostScript name, so two
faces with one name cannot share a family. Keep text layers at the
path index where their glyphs were painted instead of appending them
after all paths.
- Correctness: reject bow-tie clip paths, start a new subpath after
close, fall back on unparseable/unsupported glyph mappings, truncated
content, failed font registration, and shape-cache budget exhaustion.
- Cancellation: check between text-cache renders as well as between
objects.
- Include Bezier control points in the cache-size estimate; cache the
content hash per Hayro font identity.
- Docs: ImportOptions/CLI help now describe editable text and the
embedded-font caveat. Tests: text cache is not blank, paint order,
bow-tie clip, line after close, cancellation between objects.
Add an experimental, opt-in PDF path importer on craft-pdf's interpret
Device. Fills and strokes become ShapeLayers in document pixels with
their real colours, widths and dash patterns; text, images, clipping
paths, transparency groups, patterns, soft masks and annotations keep
the raster page with a warning, so a page is never half imported.
- ImportOptions::pdf_editable (off by default) and --pdf-editable.
- pdf-editable Cargo feature in photocraft-io; the CLI enables it.
- Shape caches are rendered within a 256 MiB page budget.
- Cancellation is forwarded through a 5 ms watcher; hostile pages are
caught and fall back instead of panicking.
Tests cover fill/stroke colours and dashes, composite parity with the
raster page, text fallback and cancellation.
Conflicts with main's --no-maximize-compatibility (#8064) in the CLI
export args, output mapping and dev-dependencies, and in the
file-format parity status line and revision history: both sides kept.
Co-Authored-By: ljkx <ljkx@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- --pdf-dpi now rejects zero, negative, NaN and infinite values at parse time (unit-tested) instead of silently falling back to 300 DPI.
- The RGBA8 raster is converted and written in 256-row bands with a cancellation check per band, so the f32 buffer no longer duplicates a whole page; the pixel-memory estimate charges the retained raster instead of that buffer.
- docs/file-format-parity.md: the 2026-11-07 revision row is inside the revision table.
- NOTICE now reproduces the Hayro MIT notice, the Foxit/PDFium BSD-3-Clause text, Adobe's CMaps BSD-3-Clause text and the CC0 statement for the ICC profile; package.ps1 ships NOTICE and ATTRIBUTION.md in both installers.
The PSD writer always writes the real merged image for those modes (Bitmap and
Indexed are saved without layers; Duotone is exempt), so maximizeCompatibility
false does not give them the placeholder. The MCP params, control protocol and
CLI help now say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Photoshop's Maximize Compatibility checkbox, for one save: `maximizeCompatibility`
(true or false) on Save, Save As, Save a Copy, `app.save`, serve `doc.save`, MCP
`doc_save`/`doc_export`, Batch, Image Processor and Layers to Files decides the
merged image over Preferences > File Handling > Maximize PSD and PSB File
Compatibility; unset, the preference decides. Anything but a boolean is refused
before anything is written. The CLI takes `--no-maximize-compatibility`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ImportOptions gains pdf_pages (1-based inclusive), pdf_text and pdf_vectors;
the importer maps PDF text objects to editable text layers and vector paths to
shape layers through the effective DPI.
- photocraft-cli convert gains --pdf-dpi and --pdf-pages.
- Preferences gain pdf_dpi/pdf_pages, wired through jobs::import_options.
- apps/photocraft/build.rs copies PDFIUM_DLL beside the exe; the MSI installs it.
* Add recipe-driven PSD export checks
* Exercise PSD recipe runner with the headless CLI
* Make PSD oracle tests isolate temporary paths
* Keep inspect output unchanged; PSD write runner as an xtask library
- info --detail adds text runs, anti-aliasing and full layer-effect settings;
the control channel and MCP inspect output is unchanged.
- The CLI test uses xtask's new library target instead of #[path] includes.
- The runner records revision and dirty state as unknown outside a git checkout.
* psd-write-oracle: name the layer in each check, document versioning, add recipe tests
* Clean up PSD oracle test fixtures on failure with tempfile::TempDir and add docstrings
- A direct Batch in a script or a CLI command list stops it when it is cancelled,
cannot write its error log, or ran out of script steps; per-file errors alone
still continue (CodeRabbit, outside-diff findings on automate_cmds.rs and
photocraft-cli document.rs).
- Recorded Save steps count against the script's step budget, and a Batch whose
files failed with the budget spent carries scriptLimitError, so Browse reports
the failure instead of ok: true (yoriswind).
- actions.rename rewrites a named Batch, nested Play or Conditional call only if
it resolved to the renamed action before the rename; a Batch with explicit
steps is left alone (yoriswind: two same-named actions in different sets).
- Shape Blur's run admission counts each worker's f64 prefix sums and, for a
custom shape, the kernel mask kept for the run plus its worst-case coverage
runs (yoriswind).
- Every sampler that takes the primary release (Color Picker, Curves/Levels and
Properties eyedroppers, Curves Cmd-click, Color Range) ends a transform handle
drag and a Move drag begun before it, not only the Color Picker (#11210 review).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add headless GPU backend foundation for composition and flat export (issue #8062 PR 1)
* Fix headless GPU readback bounds, API compliance, and error preservation (#8062)
* Fix GPU readback bounds, context thread spawn error, and test parity (#8062)
* Fix BandRenderer Send bound, PNG reborrows, and clippy lints (#8062)
* Fix effect shadow test construction and remove dead code wrappers (#8062)
* Use short reborrows for Headless renderer without lifetime escaping (#8062)
* Fix clippy collapsible_if and ensure multi-band geometry in flat export tests (#8062)
* Fix automation Rect import, add renderer diagnostics, and harden flat export retries (#8062)
* Expose optional GPU rendering in photocraft-cli (#8062)
* Fix GPU feature test APIs and native target gates
* Fix CLI locked metadata, bounded benchmark params, and protocol diagnostics (#8062)
* Verify unsupported GPU effects before testing band seams
* Fix CLI renderer borrows and GPU-enabled test compilation
* Use compositor buffer ownership API after upstream memory accounting change
* Fix GPU CLI lint failures and validate run environment policy
* Update export renderer test helper for owned compositor buffers
* Fix needless benchmark layer name borrow
* Fix headless GPU backend review findings (#10842)
- release a closed document's GPU caches through a BandRenderer hook
- bound the monitored readback wait and report a stalled GPU as device loss
- reject 32-bit float documents before band readback so exports fall back to CPU
- keep previews at 8-bit RGBA and report the palette/Duotone renderer bypass
- retain renderer diagnostics when encoding fails
- strengthen renderer, save-path and GPU diagnostics tests
* Use valid batch arguments and inspect method in GPU CLI tests
* Add GPU close-hook and 8-bit preview regression tests (#10842)
* Fix GPU CLI review findings (#10912)
- guard the benchmark's modulo divisors for one-pixel dimensions
- count a cold export as GPU only when the renderer context was used
- report every batch file's export warnings, deduplicating only GPU diagnostics
- drain renderer diagnostics before propagating convert and run save errors
- cover renderer use in the GPU precedence, bridge and serve tests
* fix(io): correct renderer binding mutability
* test(cli): verify narrow benchmark inputs and GPU diagnostics
* Fix remaining headless GPU review findings (#10842)
- release a document's GPU caches when file.close, file.closeAll or
file.closeOthers removes it through a command
- honor cancellation while band readback waits, polling in 100 ms slices
- fault the device after a readback timeout so later exports skip it
- report single-layer, CMYK ink-mixing, ORA and PDF CPU paths to a
requested renderer
- keep fallback warnings in their export instead of leaking them into the
next one
- requeue renderer diagnostics when a renderer-aware save's final write
fails
- write MCP/serve diagnostics without panicking on a broken stderr
- document the actual colour-space contract and drop F32 from the GPU
depth list
* Fix remaining headless GPU review findings (#10842)
- release a document's GPU caches when file.close, file.closeAll or
file.closeOthers removes it through a command
- honor cancellation while band readback waits, polling in 100 ms slices
- fault the device after a readback timeout so later exports skip it
- report single-layer, CMYK ink-mixing, ORA and PDF CPU paths to a
requested renderer
- keep fallback warnings in their export instead of leaking them into the
next one
- requeue renderer diagnostics when a renderer-aware save's final write
fails
- write MCP/serve diagnostics without panicking on a broken stderr
- document the actual colour-space contract and drop F32 from the GPU
depth list
* Fix remaining GPU CLI review findings (#10912)
- reject non-UTF-8 PHOTOCRAFT_GPU values instead of silently using the CPU
- release a batch item's document caches before taking the renderer to the
next item, and report fallbacks once
- count warm benchmark runs that fell back to CPU in the summary
- write serve diagnostics without panicking on a broken stderr
- assert that the batch test's --gpu policy reaches the renderer
* Use question mark for cancellable readback errors
* Use question mark for cancellable readback errors
* Keep GPU export diagnostics with their attempt and stop bands on cancel
Encode native .pcraft bytes before draining renderer diagnostics, and
requeue export warnings when a workspace write fails, so callers still
report the adapter or fallback reason with the error.
Move a failed GPU attempt's queued diagnostics into its CPU retry result
instead of leaving them for a later successful export to report.
Stop GPU band readback at the next band after cancellation, and check the
interrupt before each CpuRenderer sink delivery.
Band readback uploads half-precision textures, so reject U16 alongside
F32: their samples cannot survive the upload. Tighten the 8-bit CPU/GPU
parity checks to 1/255.
* Keep GPU export diagnostics with their attempt and stop bands on cancel
Encode native .pcraft bytes before draining renderer diagnostics, and
requeue export warnings when a workspace write fails, so callers still
report the adapter or fallback reason with the error.
Move a failed GPU attempt's queued diagnostics into its CPU retry result
instead of leaving them for a later successful export to report.
Stop GPU band readback at the next band after cancellation, and check the
interrupt before each CpuRenderer sink delivery.
Band readback uploads half-precision textures, so reject U16 alongside
F32: their samples cannot survive the upload. Tighten the 8-bit CPU/GPU
parity checks to 1/255.
* Report partial warm fallbacks and CPU fallback on stderr
The averaged GPU Warm row now says how many warm runs fell back to the
CPU and were excluded from the average, through the existing helper.
The single-layer convert diagnostic test checks that the CPU fallback is
reported on stderr, in GPU-enabled and CPU-only builds alike.
* Keep headless export warnings when a workspace write fails (#10842)
A capability-scoped save encodes the export, then writes it; when the write
fails, the export warnings (folded together with renderer diagnostics) were
pushed back through `BandRenderer::push_diagnostic`, whose default is a no-op.
A custom renderer that overrides only `drain_diagnostics` therefore lost the
adapter or fallback reason that belonged with the error.
Keep them in `Headless` instead and return them from the next
`drain_diagnostics`, alongside the renderer's own queue. The regression uses a
renderer without a `push_diagnostic` override and checks the warnings survive
the failed write and drain exactly once.
Also name the actual source of the depth limit in the headless GPU matrix:
layer content is uploaded as half-precision textures, not band readback.
* Stop CPU bands and CPU retries on cancellation (#10912)
The CPU retry after a retryable GPU error composes through
`FlatSource::bands`, whose shared `emit` sink did not poll the export
interrupt: a cancel was only observed after the whole composite finished.
Check it once per band, as `CpuRenderer` does, for both RGB and CMYK ink
bands. The flat and streamed-PNG retry branches also check before starting
the full CPU composite again, so a cancelled export returns
`IoError::Cancelled` instead of re-rendering the document.
Tests: band-sink cancellation for RGB and CMYK inks, a renderer call counter
proving a second export reaches the renderer, and a pending `wait_slices`
poll observing cancellation set mid-wait.
* Merge origin/main into GPU foundation (#10842)
* Keep GPU-path diagnostics on every failure path, honour cancel in native copy
- save: export warnings of a failed local write stay in Headless pending diagnostics
- render: preview fallback warnings are returned, drained, and added to the doc.render reply
- io: renderer-neutral fallback wording; native single-layer copy checks cancellation per band
- gpu: gate wait_slices tests off wasm32 (#10842)
* Address CodeRabbit review: keep fallback reasons on failed retries, report native-copy and quick-export CPU use, guard diagnostics drain (#10912)
* Accept render diagnostics from the doc.render reply in the serve GPU test (#10912)
---------
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Resolve apps/photocraft-cli/src/args.rs: keep both ValueError variants (Metadata and main's OnOff).
Resolve docs/file-format-parity.md: keep main's entries and re-add this PR's status entry, table notes and history row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offer TIFF compression, image pyramid and transparency options on Save As and Save a Copy, with matching CLI and automation parameters.
Follow-up: preserve the unsaved state when a copy discards layers over the document's own file, and check the other export formats for the same issue.
Closes#8827.
- A failed input stays in `errors` when `jobs.cancel` or Esc lands while it runs, instead of
showing up as skipped without its reason.
- Esc after the last input is refused: the worker closes the interrupt window once no
checkpoint is left, so `request_batch_interrupt` no longer answers true for a question
that never comes.
- `photocraft-cli batch` without `--format` saves a file PhotoCraft only opens as PSD, the
format `--naming`'s {ext} already plans, instead of asking the encoder for the input's
import-only extension.
- `--recursive` help says the output is flat.
- The app bridge keeps a cancelled Batch's partial result in the error it returns to MCP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review nitpick (#9466): the test only checked that the default export had some ICC profile and that `none` matched it, so a regression writing sRGB instead of the input's Display P3 would still pass. It now compares the default export's profile with the input's bytes.
Review follow-up on #9975. The help text and the CLI book page said the
XMP and free-form text are kept in every flat export and the ICC profile,
EXIF and resolution "either way". Not every format holds them: JPEG and
WebP have no place for free-form text, TIFF writes only its named text
tags, and an Indexed Color document's palette PNG carries no XMP, text,
ICC or EXIF at all, only its resolution. The texts (and the parity-doc
revision row) now say the XMP and text are kept where the format holds
them and that the option leaves everything else as it is without it.
The test fixture gains a free-form text entry and a Display P3 profile
(from photocraft-cms, now a dev-dependency; no profile file committed),
so it checks the text is gone with `none` and kept by a default PNG, and
that the default export keeps a real ICC profile that `none` matches,
instead of comparing None with None.
Since #733 a flat export from the CLI keeps the document's whole XMP
packet and free-form text, as Save As does; only Export As starts at
Metadata: None. Batch exports of Photoshop masters therefore carry the
full DocumentAncestors list (and the text of every type layer), which
can be many times larger than the image, with no way to leave it out.
`--metadata` is a new export option shared by `convert`, `run --out`
and `batch`. `none` sets ExportOptions::xmp to XmpEmbed::None, the same
path Export As's Metadata: None and Quick Export use, so the XMP packet
and free-form text are dropped while the ICC profile, EXIF and
resolution are written as before; PSD, PSB and .pcraft keep the XMP
either way. `all` is the default, so existing scripts write the same
files. Any other value is a usage error (exit 2) naming the flag,
before anything is read or written.
Tested through the binary for PNG, WebP, JPEG and TIFF from all three
subcommands (packet present by default and with `all`, absent with
`none`; pixels, ICC and resolution unchanged), layered saves, and bad
values. Documented in the CLI book page and file-format parity doc.
Keep the PSD memory admission work and upstream's scratch storage
together. Conflicts resolved:
- Cargo.lock: both sysinfo versions are required. 0.40.1 serves
photocraft-io's adaptive import budget; 0.37.2 serves
photocraft-raster's scratch volume and memory supervision. Cargo
metadata resolved the lock with no other changes.
- crates/engine/src/lib.rs: Session keeps pending_preflights and
next_pending_preflight beside scratch_runtime and scratch_epoch.
Session derives Default, so neither set needs a manual initializer.
Upstream: 655295b2a2
- CLI batch: recursive inputs keep the engine's tree order for {serial};
--format picks the encoder whatever extension --naming writes; the
summary prints before --error-log is written.
- Batch: a jobs.cancel during a write keeps that file in `files`;
Opened Files refuses a default-named result that replaces another open
document's file; a recorded Save leaves a document changed when Batch
writes nothing for it.
- Recording: folded Move-tool nudges add whole-pixel steps; a null
Check Spelling `action` is recorded as Check Spelling.
- Linked files: a document without links lists none missing; a look
whose links changed while it ran is dropped.
- Save for Web: a null or empty destination opens the picker; a rename
while the picker is open writes the picked file.
- Actions: a Check Spelling step fails instead of replacing a dialog
opened by hand.
- Docs: desktop Batch cancel flow, Check Spelling step shape, filter row
history count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now takes the manifest envelope from craft-pcraft-format, which has
no full_composite member. The save writes the member after the envelope
when it writes composite/full.png, and read_full_composite reads it back.
* perf: reuse large buffers so a pinned glibc trim threshold costs no speed (#10479)
With MALLOC_TRIM_THRESHOLD_ set (#8743), glibc maps every allocation over
128 KiB fresh, so tiles, compositor bands and filter scratch were page-faulted
in on every call: viewport compose 3.6x slower, Gaussian Blur up to 1.8x,
.pcraft save 2.4x.
- photocraft_raster::scratch: a process-wide, capped pool of large Vec
buffers (u8/u16/f32/[f32; 4]). A miss drops the oldest buffers worth its
size down to a 256 MiB floor, so peak memory stays near glibc's defaults.
The engine releases the pool after any command that closes a document,
Headless::close does too, and an idle thread releases it after 2 s unused.
- Tiles, compose Buffer/masks/bands, algo Image and the blur paths, engine
adjustment/filter scratch, GPU tile upload and io import buffers go through
the pool.
- .pcraft save reuses one zstd FrameCompressor per worker (byte-identical).
- interactive_bench reports minor page faults per run.
36 MP, trim threshold set vs glibc defaults: viewport 5.6 vs 6.1 ms (was
22.7), Blur r4 445 vs 437 ms (was 852), Curves CPU proxy 35.0 vs 35.7 ms
(was 50), flatten 146 vs 147 ms (was 237), save .pcraft 251 vs 230 ms (was
545). RSS after closing a 5400x7200 document: 21 MB with trim set.
* fix(linux): restart once with glibc's trim threshold set so closed documents return memory (#8743)
With glibc's default malloc, the memory of a closed document stays with the
process: freeing a large buffer raises glibc's dynamic mmap threshold, so
later tile buffers land in heap pages glibc keeps. A session that opened and
closed a 5400x7200 document held ~450 MiB afterwards.
photocraft_engine::malloc_tuning::apply(), called first in both binaries'
main, re-executes the binary once with MALLOC_TRIM_THRESHOLD_=131072 on Linux
with glibc: same PID, arguments, argv[0] and environment, in safe Rust
(CommandExt::exec). A malloc variable or glibc.malloc.* tunable the user or a
package already set is left alone, so setting MALLOC_TRIM_THRESHOLD_ opts
out. No-op on macOS, Windows, musl and wasm.
Depends on the scratch-buffer pool (#10479), without which the pinned
threshold slows filters, previews, flatten and .pcraft save by 26-209%.
* fix: corpus tests build again; return remaining pooled buffers (#10479 review)
- Corpus-gated io tests and the Photoshop-oracle test moved `.px` out of a
`Buffer`, which now implements `Drop` (E0509 with `--features corpus`):
use `into_px()`.
- io: masks, the merged image, extra channels, smart-filter masks,
smart-object pixels and patterns give their `interleave` buffers back to
the scratch pool after writing them.
- compose: recycle the mask values of a pass-through group, `effect_shape`
and both `split_parts` callers.
- Styled Free Transform preview copies each band out instead of moving it,
so the band returns to the pool.
- interactive_bench: the fault count covers each whole run, untimed setup
included; say so.
* fix: corpus tests build again; return remaining pooled buffers (#10479 review)
- Corpus-gated io tests and the Photoshop-oracle test moved `.px` out of a
`Buffer`, which now implements `Drop` (E0509 with `--features corpus`):
use `into_px()`.
- io: masks, the merged image, extra channels, smart-filter masks,
smart-object pixels and patterns give their `interleave` buffers back to
the scratch pool after writing them.
- compose: recycle the mask values of a pass-through group, `effect_shape`
and both `split_parts` callers.
- Styled Free Transform preview copies each band out instead of moving it,
so the band returns to the pool.
- interactive_bench: the fault count covers each whole run, untimed setup
included; say so.
* fix: recycle the masks of adjustment-atop and shape layers (#10479 review)
* fix: recycle the masks of adjustment-atop and shape layers (#10479 review)
* fix: recycle effect hiding masks (#10479 review)
* fix: recycle effect hiding masks (#10479 review)
* fix: recycle knockout, CMYK and GPU shape masks; release the pool when Split/Merge Channels close a document (#10479 review)
* fix: recycle knockout, CMYK and GPU shape masks; release the pool when Split/Merge Channels close a document (#10479 review)
* fix: release the pool when a closed document's cancelled workers exit; idle-release switch for tests; don't pool PSD planes (#10479 review)
* fix: release the pool when a closed document's cancelled workers exit; idle-release switch for tests; don't pool PSD planes (#10479 review)
* fix: release the pool for workers cancelled before their document closes; take the pool lock for budget and idle-release changes (#10479 review)
* fix: release the pool for workers cancelled before their document closes; take the pool lock for budget and idle-release changes (#10479 review)
* fix: idle release rechecks activity under the lock; try_take_exact releases the pool and retries before failing (#10479 review)
* fix: idle release rechecks activity under the lock; try_take_exact releases the pool and retries before failing (#10479 review)
* test: cover a successful try_take_exact miss; name the failed-reservation test for what it checks (#10479 review)
* test: cover a successful try_take_exact miss; name the failed-reservation test for what it checks (#10479 review)
* fix: pool the final GPU effect shape buffer and return it after use (#10479 review)
* fix: pool the final GPU effect shape buffer and return it after use (#10479 review)
* docs: describe scratch pool lifecycle regression invariants
* test: verify polling releases cancelled document scratch buffers
* perf: recycle remaining transform selection and PSD scratch buffers
* fix: free PSD gather bands before allocating channel planes
* test: assert exact scratch take reuses pooled storage
The three version 12 fixtures were tracked inside crates/psd/fuzz/corpus,
which crates/psd/fuzz/.gitignore ignores and cargo-fuzz rewrites (cmin) or
deletes; the psd and engine test binaries include_bytes! them, so cleaning
the corpus broke the build. They move to crates/psd/tests/fixtures/atn with
a README (sizes, purpose, sha256; the seeds note folded in) and an
ATTRIBUTION.md row; the fuzzer takes the folder as an extra corpus dir.
photocraft-cli recognised an extensionless Actions file only by version
word 16, so a version 12 set without .atn (classic Mac OS Photoshop 5)
went down the JSON path and failed with a UTF-8 error. It now accepts 12
and 16, the versions photocraft-psd reads. Input: v12-commands.atn copied
to "Legacy Actions" and passed to load_actions_file.
Co-authored-by: yoriswind <164435053+yoriswind@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The doc.save row, docs/development.md, the MCP doc_save description, the
file.saveACopy params text, the --pcraft-full-composite help and the
option's doc comments said the option writes composite/full.png. A save
leaves it out when the canvas is past 2^26 pixels, when its pixels cannot
be allocated or encoded, or when a ZIP bundle would pass its limits. They
now say so and point to the manifest's full_composite member, which names
the entry when it was written.
Main's noop_step now takes the step's params (an Arrange that cannot move the layer is a no-op too); it is public for the CLI's run and batch, which pass them.
A .pcraft bundle holds the flattened document only as thumb.png (at most
256 px) and composite/preview.png (at most 1024 px). A new option also
writes composite/full.png: the flattened document at its canvas size,
an 8-bit RGBA PNG made like the two previews, with the ICC profile of
its pixels embedded. The option is off by default, and a save without
it writes the same bytes as before.
- Preferences > File Handling > Save Full-Size Composite in .pcraft
Files sets it for the app's Save, Save As and Save a Copy, in the
desktop and web builds.
- The engine's file.saveACopy and its batch and export commands take
"pcraftFullComposite" (unset, the preference), the automation
doc.save and MCP doc_save take
"pcraftFullComposite": true, and photocraft-cli convert, run and
batch take --pcraft-full-composite.
The manifest names the image in a new optional member, full_composite;
the format version stays 1, and bundles without it open as before. A
canvas over 2^30 pixels, or one whose pixels cannot be allocated, is
saved without it, and so is a ZIP bundle that it would take past 4 GiB
or 65,535 entries.
Main reorganised the source tree (command modules under engine/src/cmd, tools and dialogs under ui-egui/src, large files split into modules, inline test modules moved to their own files). This branch's changes to the moved and split files are ported to their new locations.
Merged with our fix-up d5139d177 (pr-1199-fixup), which merged main as of 8f09d122d.
Conflicts with main since then and with batch 8:
- photocraft-cli: main split the CLI into clap modules (#7071); the model wiring
(local_headless, configure_cli_models, batch's shared backend, droplet) is re-applied there.
- Cargo.lock regenerated: crates/ml pins ureq =3.4.2, so the app's ureq goes 3.5.0 -> 3.4.2.
- cutout_cmds: main's subject area (#8235) kept for the classical path beside the model path.
- file_cmds batch: main's per-input closure takes `s` later, so the backend and
integrations prefs are read before the closure instead of inside it.
- prefs_ui: model-choice filter with #4573's pref_choice_label; Integrations stays visible
(#4097) with the model choices only where models run; the translation-coverage test main
deleted (c3aa3f9e5) stays deleted; old section_fields test calls take PreferenceFieldOptions.
- Semantic clashes git did not flag: #4097's Integrations test literals in
apps/photocraft take ..Default::default() for the new model fields; main's and #4573's
section_fields test calls pass PreferenceFieldOptions.
- engine lib.rs, prefs.rs, smartselect tests, snapshot example, jobs_ui_tests, people.toml,
docs and i18n catalogs merged as unions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
panels.rs: the PR's snapshot rows next to main's History Brush source
and scroll-to-current; ui-parity.md: main's status line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>