Commit Graph
431 Commits
Author SHA1 Message Date
yoriswind 211efca709 Keep paused action policy and spelling receivers bound 2026-11-03 19:39:45 -08:00
yoriswind 15cfdc8398 Preserve paused action authorization and history boundaries 2026-11-03 17:54:45 -08:00
yoriswind 1bcf7a3c6a Merge batch 14 main before action-history review fixes 2026-11-03 17:27:25 -08:00
David Mann 545d447c10 Merge PR #10998: Actions: map recorded Scale Effects and Create Layers 2026-11-04 00:51:01 +01:00
David Mann 3c9ae63951 Merge PR #10955: Actions: map recorded Shear curves 2026-11-03 22:55:46 +01:00
yoriswind 3028355333 Merge main and retain action history across Batch and Spelling 2026-11-03 12:42:57 -08:00
yoriswind 27f86af55b Merge main into recorded layer style mappings 2026-11-03 12:12:41 -08:00
yoriswind 7763e2578f Merge main into recorded Shear mapping 2026-11-03 12:11:27 -08:00
David Mann 940f3b4715 Merge PR #10565: Free Transform: lift a pixel's coverage up to all of it, as Move does 2026-11-03 20:47:09 +01:00
David MannandClaude Opus 5.5 72c2585177 Merge origin/main (287352bf92) into integration-batch-13
Conflict in crates/engine/src/jobs.rs: main's save commit point (#5333,
OPEN/COMMITTED/CANCELLED phase) met the batch's cancel state
(CANCEL_REQUESTED/WORK_FINISHED). Both now share one atomic with a
COMMITTED bit; commit vs cancel and the batch's late-cancel guard each
stay one compare-and-swap. A Batch that refuses a repeated or late cancel
reports CancelRefused::Finishing ("finishing") in jobs.cancel's
notCancelled list, still {"cancelled": 0}. control-protocol.md documents both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:35:44 +01:00
yoriswind 0edb29a18c Merge upstream main into Shear action mapping 2026-11-03 09:32:21 -08:00
skylar taylor-barrick b323c1ba27 Merge pull request #10731 from RickiNano/fix/10665-pasteboard-pick-offcanvas
Don't auto-select off-canvas layers from the pasteboard (#10665)
2026-11-03 18:17:45 +01:00
David Mann 3d36330d94 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	docs/scorecard.md
2026-11-03 18:08:29 +01:00
d21c403e5f Type: add OpenType Numerals submenu (figure styles) (#11051)
* Type: add OpenType Numerals submenu (figure styles)

Type › OpenType › Numerals sets lnum/onum/pnum/tnum as a radio style
(Default through Tabular Oldstyle). Checkmarks follow the selection
start; TYPE-218-5 scorecard/gaps updated; parity floor 640.

* refactor(engine): one style-at-character lookup for the OpenType menus

feature_on_at and numerals_style_at each walked the runs the same way;
they now share style_at. NumeralsStyle::from_id and numerals_style_of
read the existing tables instead of repeating them.

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

* fix(text): persist OpenType numeral tags through PSD EngineData

Map lnum/pnum/tnum as EngineData bools and the width×height combos as
FigureStyle (Krita/Illustrator), so Numerals choices survive PSD round-trip.

* docs(gaps): note Numerals PSD figure-tag round-trip

* revert(text): keep PSD EngineData to the keys Photoshop writes

Backs out the PSD half of 18610324 and its gaps note (58813de3); the
engine reformat in that commit stays.

- `LiningFigures`, `ProportionalFigures` and `TabularFigures` are not
  Photoshop EngineData keys (the commit says Photoshop only has
  `OldStyle`), so writing them puts keys of our own into users' PSDs.
- The `/FigureStyle` code table (1-4) has no oracle: no Photoshop-authored
  file in photocraft-corpus, and neither ag-psd nor psd-tools reads it.

AGENTS.md rule 3 asks for public specs or observation. PSD persistence
of the numeral styles can come back with a Photoshop-authored oracle.
The Numerals menu still round-trips through .pcraft.

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

* test(text): lock PSD EngineData to Photoshop OldStyle for numerals

Document AGENTS.md clean-room: only onum survives TySh round-trip; lnum/pnum/tnum stay .pcraft-only until a Photoshop-authored corpus oracle.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:55:35 +01:00
David MannandClaude Opus 5.5 ae0da189a2 Address CodeRabbit review: a cancel after an action's saved snapshot keeps that file
When an action saves and then closes or switches away, Batch writes the
saved snapshot inside the action closure. A cancel landing after that write
made process_one return Cancelled, so the input was reported as skipped
although its output exists. The snapshot write is now final: the cancel
stops only the inputs after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:26:39 +01:00
David Mann 559d3fe0f4 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/engine/src/cmd/file_cmds.rs
#	crates/ui-egui/src/app/files.rs
#	crates/ui-egui/src/dialogs/tiff_options_ui.rs
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/ui-parity.md
2026-11-03 17:11:53 +01:00
Yuki-init e3a84b628d TIFF Options: compression, image pyramid and Save Transparency (#8827) (#10932)
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.
2026-11-03 15:39:15 +01:00
David MannandClaude Opus 5.5 62c89c87e7 Tests: the Opened Files failed-write case blocks the write with a folder
A path under a regular file can't be resolved on Linux (ENOTDIR), so Batch
refused the whole run before writing and the test failed there. A folder
taking the result's name resolves on every platform and fails the write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:30:25 +01:00
Hartmut NörenbergandClaude Opus 5.5 e16450d4e2 fix(plugins): the install error names the format the file actually is (#10362) (#10633)
* fix(plugins): the install error names the format the file actually is (#10362)

Plug-ins > Install Plug-in pointed at an old Photoshop plug-in (.8bt) answered
with "not a valid plug-in module: not a WebAssembly binary (missing \0asm
header)". That was one fixed message for every file that is not wasm: it said
what we wanted and nothing about the file the person is holding.

format_hint in runtime.rs now reads the magic number and the error says what the
file looks like. For a Windows module the message is (one line in the code,
wrapped here):

  not a valid plug-in module: not a WebAssembly binary (missing \0asm header);
  this file looks like a Windows native executable or library (a .dll, or a
  Photoshop plug-in such as .8bf/.8bt) - PhotoCraft plug-ins are WebAssembly
  modules (.wasm)

7F 45 4C 46 says "a Linux native library (ELF)", CE FA ED FE / CF FA ED FE /
CA FE BA BE / BE BA FE CA say "a macOS native binary (Mach-O, thin or fat)",
50 4B 03 04 / 50 4B 05 06 say "a zip archive". Any other header keeps today's
message byte for byte, so nothing keyed on it changes. The signatures are public
format facts, read with starts_with/get so a short or hostile file cannot panic.
The error variant, the enum, the ABI check, check_code_section and the wasmi
setup are untouched, and native-code loading stays out of scope (PR #7907).

Fail-first, before the production change:

  cargo test -p photocraft-plugins --lib --tests
    lib:  test result: ok. 10 passed; 0 failed
    abi:  test result: FAILED. 19 passed; 1 failed; 0 ignored; 0 measured
          assertion line: "windows dll or .8bt: not a valid plug-in module:
          not a WebAssembly binary (missing \0asm header)" (the MZ fixture that
          was already in the file)
  cargo test -p photocraft-engine --lib -- plugin
    test result: FAILED. 7 passed; 1 failed; 0 ignored; 0 measured; 2518 filtered out

After:

  cargo test -p photocraft-plugins
    lib 10 passed; abi 20 passed; doc-tests 0
  cargo test -p photocraft-engine --lib -- plugin
    test result: ok. 8 passed; 0 failed; 2518 filtered out

The new engine test drives the real plugin.install command with a `path` whose
file starts with MZ bytes, so it covers the menu path (registry::load_file) end
to end, not only the library check.

Gates: cargo clippy -p photocraft-plugins --all-targets -- -D warnings and
cargo clippy -p photocraft-engine --all-targets -- -D warnings (clean; the only
stderr noise is the pre-existing "patch `egui_*` was not used in the crate
graph" cargo warnings); cargo xtask layers ("OK: 31 crates, no layering
violations."); cargo xtask wasm ("  ok     24 crates", "  ok     photocraft-
codecs --features heif", no FAIL line); cargo fmt -p photocraft-plugins --
--check and cargo fmt -p photocraft-engine -- --check (silent, exit 0). The
whole engine --lib suite is not run here: it fails on clean main in this
checkout for font/i18n reasons (no CRAFT_FONTS_DIR). No doc quoted the old
message (git grep "WebAssembly binary" finds only this crate), so nothing to
update there.

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

* test(plugins): one install-error test per recognised format, tempfile in the engine test (#10362)

Review feedback on #10633. The single looped test in abi.rs is now one test
per signature (PE/COFF MZ, ELF, Mach-O in all four byte orders, zip), one for
an empty or unrecognised file (including signatures cut short, which keep the
old message byte for byte) and one showing a valid WebAssembly module still
loads. The engine test uses tempfile::tempdir (already a dependency of
photocraft-engine) so the directory is removed on a panic, and asserts the
message does not contain the temp path: the hint is static text, so the
message adds no path.

Gates: cargo test -p photocraft-plugins --lib --tests (10 lib, 26 tests);
cargo test -p photocraft-engine --lib -- plugin (8 passed); clippy -D warnings
on both crates; cargo fmt --all -- --check; cargo xtask layers (OK).

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

* test(engine): keep the non-wasm install-error test off wasm32, as tempfile is (#10362)

tempfile is a non-wasm32 dependency of photocraft-engine, so the test that
uses it is gated like the crate's other file-system tests.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:22:58 +01:00
David MannandClaude Opus 5.5 a0418a2e29 Address CodeRabbit review: Batch failures, late Esc, CLI format, bridge result
- 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>
2026-11-03 14:50:52 +01:00
David Mann 7329c700bb Merge remote-tracking branch 'origin/main' into integration-batch-13
# Conflicts:
#	apps/photocraft-cli/src/args.rs
#	crates/engine/src/jobs.rs
#	crates/engine/src/jobs/tests.rs
2026-11-03 13:52:30 +01:00
FredApps 3695186329 Consolidate RAM detection in raster::memory::system; scratch-aware PSD/TIFF admission
One RAM detector: the cgroup v1/v2, macOS reclaimable and Windows commit providers move from
photocraft-io into photocraft_raster::memory::system (L0), which the residency budget (#5330)
and photocraft_io::memory both read. Unreadable container limits still fail closed, swap is
never counted as RAM. sysinfo is unified on =0.40.1 (raster only).

While the scratch disk is spilling (on, not paused), import admission credits the tile pixels
of an estimate against spill::scratch_capacity() (free space above the reserve, within the
Scratch Size Limit); decode buffers, conversion copies and merged planes stay RAM-only. Spilling
off/paused and the web keep the RAM-only policy unchanged. PsdMemoryEstimate gains tile_bytes;
the flat TIFF streaming check credits its tile surface the same way; engine approveMemory's
fast pre-check uses the credited hard limit and the import re-checks exactly.
2026-11-03 09:48:22 +01:00
yoriswind c0bd466d5f fix(actions): retain explicit history deletions and user purge boundaries 2026-11-03 00:16:38 -08:00
yoriswind 9f8b880e2b fix(atn): retain nested layer-effects Make intent 2026-11-03 00:10:26 -08:00
yoriswind 446e1a4589 Merge commit '655295b2a2c400bded0640f5d97367000a057b2e' into codex/10723-nested-lefx 2026-11-03 00:01:32 -08:00
yoriswind 70aea3465d merge: integrate exact main scratch storage checkpoint
Merge 655295b2a2. Resolve generated scorecard conflict with exact upstream text only; actual source-bound regeneration and combined validation remain pending.
2026-11-02 23:30:10 -08:00
Khawar Zamman Wani 259b5590ae Merge origin/main (655295b2a2) into PR #10897 branch
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
2026-11-03 12:54:59 +05:30
Khawar Zamman Wani da41a65811 docs: document PSD memory admission and open queue functions
(cherry picked from commit 07c6a3f87cff39dde8ec2b334f4e9b388667bfc8)
2026-11-03 12:51:53 +05:30
yoriswind 4d188a9106 fix(actions): preserve cleared source context and Undo Clear 2026-11-02 23:07:41 -08:00
yoriswind 35dbf2a1bc Actions: keep miskeyed effects references intact 2026-11-02 23:01:31 -08:00
yoriswind f650359b77 Merge current main and refresh combined preference scorecard 2026-11-02 22:13:07 -08:00
FredApps 8ce599da3a Merge remote-tracking branch 'upstream/main' into pr-5330 2026-11-03 07:12:38 +01:00
yoriswind 3271b4acf0 Actions: map Scale Effects and Create Layers recordings 2026-11-02 22:03:52 -08:00
yoriswind 15977fee7f fix(actions): retain explicit history controls during grouping 2026-11-02 21:31:33 -08:00
Khawar Zamman Wani d8e0dde8b5 fix: pass footage import options to the single-file fallback
`load_frames`'s single-file path called `file_cmds::import`, which uses default
import options, so the footage command's lowered PSD/layered-TIFF memory limit
never reached a single-page layered TIFF or PSD. Multi-page TIFF pages already
passed their `opts`.

Give the engine import one shared `import_result_with` path and an
`import_with_options` entry that keep the allocation checkpoint, native
`.pcraft` and format dispatch, typed memory refusals and the preview-only
refusal; the existing `import` delegates with default options, so every other
caller behaves exactly as before. The single-file fallback now passes `opts`.

Add a command-level regression: a single-page layered TIFF under a thread-scoped
`psd_max_pixel_bytes` of 1 must reach `file.import.videoFramesToLayers` as a
typed `EngineError::PsdHardBudget` with its name, required and limit fields
intact and the document unchanged, then import as one frame when the limit is
not lowered.
2026-11-03 10:41:03 +05:30
yoriswind 699683d8eb test(actions): cover grouped and per-step history 2026-11-02 20:08:20 -08:00
Khawar Zamman Wani 06d00e06a9 fix: keep multipage TIFF frames inside the footage budget and refusals typed
The multi-page TIFF branch of `load_frames` pushed each flattened page
straight into `frames`, bypassing the `keep` closure, so `MAX_FOOTAGE_BYTES`
was never checked for those pages: many large pages could retain more than
the cap without an error. Route every flattened page through `keep`, with the
same file-context error mapping as the single-file and GIF/WebP paths, and
keep typed page admission refusals typed.

Add command-level regressions. A per-thread test scope lowers the footage and
PSD limits, so neither test allocates gibibytes: one proves an over-budget
multi-page TIFF is refused with nothing imported, and one proves a page with
Photoshop layer data reaches `file.import.videoFramesToLayers` as a typed
`EngineError::PsdHardBudget` with its name, required and limit fields intact
and the document unchanged.
2026-11-03 09:33:29 +05:30
yoriswind 1f55de75c5 test(atn): cover nonfinite vertical Shear coordinates 2026-11-02 19:25:52 -08:00
yoriswind d29cbec07e fix: consolidate action history after command transactions
Keep command-local history, purge and rollback intact, then consolidate affected documents at action completion or dialog suspension. Preserve semantic no-op identity and redo, visibility semantics and row identifiers. Bind dialog continuation to its exact document/history, inherit the setting in scratch sessions and carry droplet action names. Add the review counterexamples and focused continuation, Batch/droplet and history-option controls. Source is formatted; compilation and runtime qualification remain pending.
2026-11-02 19:02:08 -08:00
Khawar Zamman Wani dcd21f81b4 fix: keep multipage TIFF page memory refusals typed
The multipage TIFF import loop mapped every page-import error to EngineError::Other, so a PsdApprovalRequired or PsdImportBudget from a layered page reached the UI, control channel and agents as text only, without the required/limit fields. Route those two through io_open_error and keep the page-specific text for every other error.
2026-11-03 08:21:00 +05:30
yoriswind 182da545ee feat: start persisted single-action history grouping
Add the default-on Actions history setting and a grouping scope shared by ordinary engine and shell playback entry points. Define public regressions for whole-play Undo/Redo, the off setting, failure prefixes, nesting, no-op and repeated-play boundaries, and persistence. This is an unvalidated draft increment: dialog continuation, direct compound edits, Batch/droplet boundaries and the complete edge-case qualification remain unfinished.
2026-11-02 18:30:27 -08:00
yoriswind 8e17f90c06 Bound Shear action curves and preserve native precision 2026-11-02 18:26:17 -08:00
David MannandClaude Opus 5.5 3ce04b52f4 Address CodeRabbit review: Batch writes, recording and dialog edge cases
- 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>
2026-11-03 03:02:19 +01:00
yoriswind 1a00bffee8 Run public Shear regression tests in the integration harness 2026-11-02 17:04:30 -08: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
yoriswind c9fc8458b4 Map recorded Shear actions with measured curve settings 2026-11-02 16:40:07 -08:00
David Mann 56870f5687 Merge #10905: the author's latest commits 2026-11-03 01:05:27 +01:00
David Mann 4f6a2c50f0 Merge #4579: the author's latest commits
# Conflicts:
#	docs/gaps.md
#	docs/target-app-parity.md
2026-11-03 01:05:27 +01:00
David MannandClaude Opus 5.5 6a63960ec6 Batch 13: settle the overlaps between the batched PRs
- Check Spelling: with #8726's measured form a `change` made in the dialog records no step
  (the reference application records one `spellCheck` step), so #4514's two tests that
  expected recorded `change` steps are removed; its portable recording stays for all else.
- Clippy `chunks_exact_to_as_chunks` in #10765's tests; a single-element loop in #10755's.
- docs/parity-checklist.md and docs/scorecard.md regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 01:04:44 +01:00
David MannandClaude Opus 5.5 55a8d917de Batch 13: #10755 and #10765 on top of batch 12
- Link Layers keeps main's measured toggle mapping (batch 12, #10628): played twice in the
  reference application it links, then unlinks. #10755's second, explicit `linked: true`
  entry for the same event was unreachable on import and contradicted that measurement, so
  it is dropped; Unlink stays explicit. Its tests now cover Unlink only, and `{}` exports.
- #10765: an ordinary Gradient Map now saves as `gradientMapEvent` (#10702 landed in batch 12).
- #10755: flattened pixels read with `Buffer::into_px()` since `Buffer` implements `Drop`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 00:50:41 +01:00