Commit Graph
204 Commits
Author SHA1 Message Date
Zinadin Zidan 489d994524 Saving: a leftover temp file is recognised only with a file part a save can write (review)
A save's temporary file keeps at most the first 64 characters of the
document's name, and older versions put one dot in front of them. A name
with a longer part before the process id and counter was still recognised
as a leftover, so the recovery sweep could delete someone else's file a
day later. Only a part a save can write is recognised now, counted in
characters as the save cuts it.
2026-11-07 03:06:17 +06:00
Zinadin Zidan dd639352ed Saving: a leftover temp file is recognised only with a counter a save can write (review)
The recovery sweep deletes files it recognises as a save's temporary file.
A name whose counter is longer than the 64-bit counter a save counts with,
or whose process id or counter starts with a zero, was still recognised,
so the sweep could delete a file no save wrote. Only the numbers a save
writes are recognised now.
2026-11-07 02:01:13 +06:00
Zinadin Zidan fc94ac59e2 Saving: the temporary file is no longer a dot file (#11624)
A save writes a temporary file beside the document and renames it over the
document. Its name started with a dot, and an SMB server that hides dot
files marked it hidden. The mark survived the rename, so the saved document
was hidden in Finder. The name is now <file>.<pid>-<n>.pcsave.tmp. Leftovers
are recognised, and swept from the recovery folder, under both the old and
the new name.
2026-11-07 00:27:13 +06:00
Khawar Zamman Wani e3ecd73569 feat(frame): rework the Frame tool's selection, handles and placement (#8920)
Thanks @wanikhawar — merging the Frame Tool rework.

Summary: Frame Tool selection/handles/placement now distinguish frame vs image (border / click / double-click and Layers thumbnails), with independent transforms, shape variants, Properties/options-bar stroke and fit commands, Convert to Frame, CLI/MCP `layer.frame.*` commands, native `.pcraft` persistence, and PSD export via editable groups + `pcFr` metadata (not Photoshop's native frame type). Tests cover selection, transforms, fitting, locks/hostile geometry, and native/PSD round-trips.
2026-11-05 12:39:02 +01:00
Khawar Zamman WaniandFredApps 316e00a97e Bound Photomerge memory and safely reject out-of-budget jobs (#7851) (#10463)
* Reduce Photomerge input and fill memory

* Bounded working-memory budgeting and safe allocation for Photomerge

* Photomerge memory: estimate warped sizes, no RAM ceiling, shared RAM detection

- Budget is half of physical RAM (at least 1 GiB) with no 8 GiB ceiling.
- The pre-flight estimate uses each image's warped extent and the band
  grid's exact bound (band_bytes_bound), at the levels seam_blend uses.
- Auto-Blend keeps its old batching with no single-band cap.
- multiband returns a Result instead of a blank image on failure.
- Physical-memory detection moves to photocraft_engine::memory; the GPU
  budgets reuse it, so PHOTOCRAFT_RAM_MB means the same in both.
- Document memoryBudget; restore comments; tests for the band cap and the
  budget precedence.

* Photomerge: lower the blend's peak and estimate it by phase

- The band build makes its binary coverage straight from the alpha and
  writes each Laplacian level in place, so a band holds two fewer planes.
- The weight sums are freed before the collapse, which adds each level
  in place and frees it as it goes.
- The estimate is the largest phase (warp, ROIs, bands, collapse, output,
  fill) rather than the sum of all of them, and the bands get what is
  left of the budget after the ROIs and accumulators.

Every output pixel is unchanged: each change drops a plane or does the
same elementwise operation in place.

* Fix Photomerge band budget validation and fallible pyramid allocation

* Keep reference pyramid test helpers infallible

* Photomerge: bound source loading with the memory budget

Resolve the budget before any source is read, and admit each flattened
source before its surface is allocated. The accounting charges the
retained surface, the same-canvas temporary the flatten writes while the
source document is alive, and a file import's encoded bytes, with
saturating math so an overflow cannot wrap under a finite budget. Merge
to HDR Pro passes no budget and keeps its previous behaviour.

Tests cover the exact retained-plus-temporary charge, a refused source,
the within/over-budget command paths, the post-load estimate path, and
the unbudgeted HDR loader.

* Photomerge: bound source reads and imports by the remaining budget

Read each source file with what is left of the budget as its limit, lower the importer's own PSD/TIFF preflight or the flat decoders' pixel allowance to the remainder after the retained sources and encoded bytes, and refuse formats with no pre-decode estimate. Charge the decoded document's actual pixels (every layer) plus the flatten's working and output surfaces before flattening. Merge to HDR Pro keeps its unbudgeted read-and-import path.

* Remove redundant TIFF decode options update

* Clean up Photomerge budget Clippy findings

* Accept early decoder admission in source budget regression

* Photomerge: report unreadable sources as file errors; never reserve past a read limit

- A budgeted Photomerge reported a missing or unreadable source file as
  'not enough memory'. Only a file over the remaining budget, or one memory
  cannot hold, is a memory error now; others are file errors, as without a budget.
- read_all_with reserved a whole read chunk before checking the caller's limit
  for a file that grew after it was statted. It now refuses first and caps the
  reservation at the limit.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 06:23:56 +01:00
FredApps 97ba6d16a9 Merge pull request #11189 from hnoerenberg/fix/windows-replace-recovery-11178
fix(atomic): report where the original survives when the Windows restore fails
2026-11-04 13:42:21 +01:00
Dylan M. Taylor 37a01ca874 format: bound blob inflation by the load's remaining budget; browser budgets (#10984)
LoadFetch::blob_bounded decompressed a new blob up to max_blob_bytes
and only then charged it against max_total_bytes. Successful blobs are
retained, so several distinct, highly compressible blobs each allocated
a full max_blob_bytes before the total was checked. A new blob may now
inflate only to what the total budget has left.

The browser build loaded native documents with the desktop defaults
(1 GiB a blob, 16 GiB in all), far beyond a wasm32 instance's 4 GiB of
linear memory that the editor also uses. On wasm32 the defaults are now
512 MiB a blob and 1 GiB in all.
2026-11-03 20:55:42 -05:00
Hartmut Nörenberg 82ee6ada2e fix(atomic): the vanished-backup error names the path it looked at 2026-11-04 01:13:43 +01:00
Hartmut Nörenberg 30c617cc79 fix(atomic): a vanished backup is not a recovery copy 2026-11-04 00:36:12 +01:00
Hartmut Nörenberg fbe7680328 fix(atomic): report where the original survives when the Windows restore fails 2026-11-03 23:55:50 +01:00
FredApps 05fc07b538 Merge main into fix/save-memory-6607
Resolve against main's save restructure (Services::save_file/SaveCtl, scratch-integrity checks,
ZipWriter::with_entries, exact encoded_len):
- PSD/PSB saves stream the file model through atomic_write_stream_progress; the length used for
  progress and reservations is main's exact encoded_len (the PR's size_bound under-counted lazy
  data); export_with/encode_with share checked_stages, so main's scratch checks (previews
  included) still refuse unreadable scratch.
- Drop ZipWriter::for_entries in favour of main's with_entries.
- TIFF: take_from_psd moves layer data out of the export's own model (never the document);
  body() reserves its exact length, failures are OutOfMemory (not LimitExceeded, which would
  trigger the PSB retry).
- Streaming writer: AlreadyExists from the writer is a failed save, not a name collision.
- Remove dead Vec put_widened_rle; channel_data_len is test-only.
2026-11-03 09:23:16 +01:00
FredApps 8ce599da3a Merge remote-tracking branch 'upstream/main' into pr-5330 2026-11-03 07:12:38 +01:00
dotCooCoo fef15729aa Merge main into feat/pcraft-full-composite
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.
2026-11-02 17:02:14 -08:00
FredApps 44f13e5498 Scope scratch read failures to the document that holds the tile
One unreadable tile used to block editing, saving and exporting every open document
(spill::check_integrity was process-global). Add Tile/Surface/Document::scratch_health
(free while no tile has failed) and Document::for_each_surface (ops' history walk now
delegates to it). edit_with checks the document before and after, jobs the document they
captured, import the result, save/export the document written; check_since is now
generation-only so a failure or recovery during an operation refuses it once and a retry
works. Caches check their own sources and purge once per new failure instead of every
frame. Fault-injection tests use a duplicated handle (the Unix file has no name).
2026-11-02 22:23:42 +01:00
FredApps b0f3c7516b Merge current main into codex/scratch-storage
Resolve conflicts with main's prefs UI split, session/execute.rs move, GPU memory
budget (gpuMemoryMb, 0 = auto, kept from main) and effective_memory_usage_mb history
budget. Port the Memory Usage % and Scratch Disks preferences into prefs_ui, the edit
integrity guard into session/execute.rs, and adapt main's new pixel readers to the
guarded TileBytes API. Cargo.lock regenerated; scorecard regenerated.
2026-11-02 22:16:22 +01:00
.CooCoo 289dd0a874 Format: take the .pcraft envelope from craft-pcraft-format (#10724)
* Format: take the .pcraft envelope from craft-pcraft-format

The manifest envelope, TileRef, Hash, FORMAT_VERSION, EXTENSION, the
entry names, tile_path and blob_path, is_valid_hash, the manifest
nesting and item checks and the migration runner now come from
craft-libs' craft-pcraft-format. photocraft-format re-exports them, so
callers do not change. Manifest is craft_pcraft_format::Manifest<DocM>.

Saved bundles keep the same bytes. A save still checks only the nesting
and the item count of its manifest, not its size, and the manifest
nesting limit is still computed from photocraft_doc::MAX_GROUP_DEPTH.

Tests: a save, load and save again gives the same bytes in every color
mode and depth, with and without previews; MAX_GROUP_DEPTH equals
craft_pcraft_format::MAX_GROUP_DEPTH.

* Format: pin craft-pcraft-format from storytold/craft-libs

* Format: read manifests with craft-pcraft-format's read_manifest_with

Pins craft-pcraft-format to dca870a0, the head of craft-libs #407.
Loading reads the manifest with read_manifest_with, which runs the
limit check, the version check, the migration steps and the envelope
read in one path; the layers are still decoded one at a time after the
rest of the document. A save builds the manifest with Manifest::new and
tile references with TileRef::new, and the bundle's directory names and
the .zst suffix come from the crate's constants. Saved bytes and error
messages are unchanged.

* Format: pin craft-pcraft-format to craft-libs #407's head 04dd1bd

04dd1bd changes only the crate's docs and a test, so photocraft-format
builds the same code.

* Format: pin craft-pcraft-format to craft-libs main at b8f7d93

craft-libs #407 merged as b8f7d93. Its read_manifest also refuses a
thumbnail other than thumb.png and a composite other than
composite/preview.png, the only names photocraft-format writes.
2026-11-02 11:43:46 -05:00
Khawar Zamman Wani 86ddfb72f7 Reuse large buffers so returning freed memory to the OS doesn't slow filters (#10479) (#10514)
* 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: 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 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: 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: 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)

* 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
2026-11-02 09:29:59 +01:00
dotCooCoo 7a2d998ff7 Merge main into feat/pcraft-full-composite 2026-11-01 15:51:56 -08:00
Gabriel Heuser 24be8ea6e2 Add editable arrowheads to Line tool (#6408) (#9259)
* Add configurable arrowheads to line tool

* Add editable arrowheads to Line tool

* Fix arrow scaling validation and PSD import defaults

* Preserve arrow geometry under affine transforms

* Preserve scaled arrowhead dimensions and prevent non-finite geometry

* Localize arrowhead Start and End labels

* Fix arrow transform editability and short line handling

* Fix PSD arrow concavity serialization

* Fix arrow dimension fallback validation

* Refine arrowhead options UI to match Photoshop

* Add opt-in arrowhead defaults to Shape properties

* Fix tool reset syntax after upstream merge

* Validate arrowhead flags and clamp copied defaults

* Preserve editable headless lines under transforms

* Fix arrowhead UI imports and formatting after v0.17 merge

* Format arrowhead properties after v0.18 merge

* Fix Clippy lint errors in arrow geometry and tests

* Fix headless arrow geometry after nonuniform scaling

* Fix proportional arrow scaling in Properties panel

* Fix Rust formatting in Properties arrow scaling tests

* test: validate invalid arrowhead dimensions without mutation

* fix: preserve arrow compatibility with older pcraft versions
2026-11-01 18:46:05 -05:00
dotCooCoo e7829eb449 Merge main into feat/pcraft-full-composite 2026-11-01 15:30:48 -08:00
David MannandClaude Opus 5.5 622707a36f Address CodeRabbit review: four findings on the batch
- Channel targeting routes an allow-list of pixel-editing paint commands, not every
  `paint.*` id: the Mixer Brush's Alt-click load and Symmetry no longer follow a colour
  set or get refused with "Rasterize the layer" on a fill layer. Patch and Content-Aware
  Move stay routed (#9274).
- Native vector save-back checks the preview SVG as strictly as stored previews are
  checked on load and PSD export, so a preview with an unreadable embedded image is
  refused instead of stored and later dropped (#8571).
- .pcraft blobs read their zstd frame with framing headroom, so an incompressible
  preview right at its 16 MiB limit still loads; the limit still bounds the data.
- Levels/Curves recall (Alt+shortcut) decides the combined Lab record from the target
  the dialog captured, as remembering does, so a viewed layer mask keeps its settings
  beside a Lab colour set (#9274).

Each comes with a test that fails without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:41:36 +01:00
dotCooCoo 69912e6035 Format: the full-size composite stays within 2^26 pixels, and running out of memory leaves it out
A canvas over 2^26 pixels (256 MiB of RGBA8) is saved without composite/full.png. The PNG encoder's buffers are reserved fallibly, so an allocation failure reports an encode error, and a ZIP bundle that cannot be reserved with the composite is written without it.
2026-10-30 06:46:33 -07:00
David Mann b2db24400e Merge main at b1cfff9d3 into integration batch 10
Conflicts resolved as unions of both sides: codecs re-exports (PngRows + TiffBands), smart_cmds source imports
(main's source_surface_from_buffer and cache API with #8571's svg_source_image_in/convert_svg_source; main's
duplicate rendering import dropped), io flat.rs test modules (#8536's banded TIFF tests and main's PNG ones),
menus/invoke.rs (#8571's smart SVG intercept and main's layer.selectUnderCursor), state.rs (#8311's
type_box_overflow and main's type_insertion_style), the canvas_geom test keeps the is_finite guard, docs
status lines keep both change lists, i18n catalogs keep both sides' rows (hi.tsv: main's wording where both
added the same string).
2026-10-30 14:28:02 +01:00
dotCooCoo 49d12c57bf Merge main into feat/pcraft-full-composite
# Conflicts:
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
2026-10-30 06:26:41 -07:00
David Mann fea300dd4c Merge origin/main into integration batch 10 2026-10-30 01:47:01 +01:00
FredApps 0cb4ec56d3 Merge current main and complete retained Pen messages in Nordic catalogs 2026-10-30 00:48:24 +01:00
David MannandClaude Opus 5.5 ce33b19090 Tests: a failed autosave outcome test does not wait for a stuck write
Address CodeRabbit review: dropping the Autosaver joins its worker, which waits
for the write in progress, so a timed-out receive could still hold the test
until nextest kills it. The saver is now ManuallyDrop and only dropped on the
passing path. With a worker that sleeps 60 s per write and a 2 s bound, the
test fails after 2.01 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:15:31 +01:00
David MannandClaude Opus 5.5 f39f94aef8 Tests: the autosave outcome test waits up to 5 minutes, not 30 s, for a real write
background_save_outcomes_report_failure_then_recovery failed on main's Windows
integration job: the real (second) autosave write flushes every tile to disk and
took over 30 s on a busy runner (normally 2-3 s; a concurrent autosave test ran
past 60 s too). The bound only exists to turn a worker that never reports into a
failure instead of a hang, so it is now 5 minutes, still under the 10 minutes
after which nextest's ci profile kills a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 20:53:19 +01:00
FredApps 255ecc81d0 Remove 21 requested PR contributions from the 108-PR integration
Retain 87 PRs. Reconcile overlapping integration fixes and update testing scope.
Removed: #7153 #7742 #5493 #9282 #3455 #9172 #3139 #3967 #4783 #5453 #8347 #1991 #3345 #4042 #4305 #4883 #4890 #5090 #6621 #5870 #5939.

Combined CI pending for this source; historical portable artifact is not applicable.
2026-10-29 18:49:17 +01:00
David Mann afacb057f7 Merge origin/main into pr-8571-fixup
Conflicts: svg.rs (main's bufread inflate and shared render helper, kept
with the PR's bounded raster path), psd_import.rs (the PR's vector
preview moved into main's smart_content), menus/invoke.rs (the SVG
intercept moved into main's split invoke_unguarded), rendering.rs and
the ui-egui import lists (main's explicit imports), Cargo.lock, docs
status lines (unions).
2026-10-29 16:38:24 +01:00
FredApps 6d1f4a47fd Merge approved PhotoCraft PR #9401: fix(format): a save on macOS keeps the file's creation time (#9388)
Preserve both independent additions and moved regression modules without changing production merge semantics.
2026-10-29 04:41:19 +01:00
FredApps cf786a3851 Merge approved PhotoCraft PR #8897: fix(io): read linked smart objects for PSD export as regular files only
Preserve both independent additions and moved regression modules without changing production merge semantics.
2026-10-29 04:41:10 +01:00
FredApps 63fbd96e2b Merge approved PhotoCraft PR #5870: fix(format): reject duplicate native layer identities 2026-10-29 03:29:18 +01:00
dotCooCoo ddd11aaa9a Format: a full-size composite that fails is left out, and pcraftFullComposite takes only booleans
- The PNG encoder gets a fallibly reserved copy of the composite's pixels instead of a
  clone, and a full-size composite that cannot be copied or encoded is left out of the
  ZIP or directory bundle and out of its manifest; the save goes on without it.
- The ZIP size limit for keeping the full-size composite counts the central directory
  and end record as well as the entries.
- `pcraftFullComposite` that is not true, false or null is a bad parameter of
  `file.saveACopy`, the other engine saves and the automation `doc.save`.
- MCP `doc_save` in bridge mode refuses `pcraftFullComposite: false` as well as `true`.
  The app's `app.save` does not take the option.
2026-10-27 22:02:30 -07:00
dotCooCoo b527b5062a Save the flattened document at full size into .pcraft files as an option
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.
2026-10-27 17:51:43 -07:00
Dylan M. Taylor 169bbdfa22 Refactor format for strict Clippy checks 2026-10-27 23:47:06 +00:00
David MannandClaude Opus 5.5 5ccb6fb723 Merge main
Conflicts with main's batch #9880:
- psd file.rs / layer.rs: #5287 (PSB by encoded size, RLE tables widened on
  promotion) against #6583's lazy reader. Main's new widening and length code
  read `.data`, which #6583 leaves empty while the bytes stay in the source;
  it now goes through `encoded()` / `encoded_len()` (also the auto-merged
  lines in `rle_widening`, `encoded_len` and `LayerRecord::write`), and the
  lazy reader sets `write_as_psb: false`. New test
  `a_lazy_psd_promotes_to_the_same_psb_as_an_eager_one`: a lazily read PSD
  writes the same PSB bytes as an eager one; it fails with a `.data` read.
- direct_select.rs / vector_ui.rs: #9321's Type Path and #6353's hidden
  Work Path both kept.
- io tests main.rs: both modules. scorecard.md regenerated (12 of 181);
  ui-parity.md and gaps.md follow it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 19:49:42 +01:00
David Mann 8bc71f924b Merge main
# Conflicts:
#	crates/format/src/store.rs
2026-10-27 19:10:28 +01:00
aiongg ae661394b2 Merge #9036: Autosave: replace a recovery sidecar in one step, so a listing never misses or doubles it (#9034) 2026-10-27 17:44:12 +00:00
aiongg 6e9fb30dbc Merge #6723: Color Lookup: PSD save keeps a .3dl/.look LUT file as loaded (#6544) 2026-10-27 17:44:05 +00:00
aiongg 01594f1f25 Merge #5890: PSD: Frame layers keep their stroke (#4381) 2026-10-27 17:44:01 +00:00
NetterSilverandClaude Opus 5.5 86245d0719 Encode .pcraft previews while the bundle is prepared and compressed
A ZIP save encoded the thumbnail and the 1024 px composite preview as PNG
on the calling thread before any tile was compressed. The composite alone
is a ~200 ms single-threaded deflate on a 36 MP document, about 40 % of a
full save. The previews are now encoded on their own threads while the
document is collected and its new objects are compressed on all cores;
the bundle is assembled afterwards in the same order, so the bytes are
unchanged. Directory saves encode them up front as before. wasm stays
sequential.

bench_export, 36 MP, 3 layers, 16 interleaved rounds of 7 reps (median
of medians / min), Ryzen 16 threads, Linux:
  .pcraft            517.9 / 471.1 ms -> 319.3 / 287.8 ms  (-38 %)
  .pcraft with cache 360.1 / 334.1 ms -> 277.5 / 268.3 ms  (-23 %)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:42:16 +01:00
David Mann 2363c475c0 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/compose/src/lib.rs
#	crates/gpu/src/plan.rs
2026-10-27 14:17:25 +01:00
David MannandClaude Opus 5.5 c4b224b074 Merge main into fix/9034-autosave-discard
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:08:44 +01:00
David Mann cd485b6c2c Merge remote-tracking branch 'origin/main' into pr-7689-fixup 2026-10-27 01:54:27 +01:00
Don OmondiandClaude Opus 5.5 4c76339c36 Address CodeRabbit review: macOS app bundles, label, digest cache, SVG warp bounds
- External vector editing on macOS: discover VectorCraft.app in /Applications and
  ~/Applications, offer Illustrator when an Adobe Illustrator*.app is found, and accept a
  chosen .app under Other application. CFBundleExecutable is read from a bounded Info.plist
  (XML or binary, streamed with a nesting cap) and must resolve inside Contents/MacOS;
  chosen bundles open with /usr/bin/open -a. vectorcraft-cli is looked for in the same
  bundle, then in known installs. Logic is compiled and unit-tested on every Unix.
- Windows "not found" errors name the dialog's "Other application" option.
- Native preview hash verification is remembered per source allocation in a 32-entry
  Weak-keyed LRU (no strong references, no reusable stale addresses, lock not held
  while hashing), so refreshes and warp frames hash a shared source once.
- source_box uses the new io::svg::bounded_canvas_bounds for SVG without Stack Mode, so
  warp setup gets a frame for drawings whose editable layer caches exceed the budget.
  Stack Mode and Edit Contents stay strict. Exact 24,000,000-pixel boundary tested.
- Document every non-test function this PR adds; describe macOS behaviour in
  docs/embedded-vector-editing.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:02:40 +03:00
Don Omondi c129dd50e0 Merge main and preserve original-format embedded vector save-back 2026-10-27 00:45:48 +03:00
David Mann 7e4ceba5df Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/cms/src/lutfile.rs
2026-10-26 14:26:50 +01:00
Dylan M. Taylor 3877bb1dbe chore: resolve SonarCloud maintainability issues (#9044)
* chore: resolve SonarCloud maintainability issues

* fix: format imports and fix effect_entry argument type

* fix: restore test closure and wildcard import that broke compilation

* refactor: split composite_to_surface to reduce cognitive complexity
2026-10-25 19:38:31 -04:00
NetterSilverandClaude Opus 5.5 92a1559138 Refuse .pcraft manifests with too many JSON values before parsing (#8893)
Fixes #8594

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:19:41 -04:00