Commit Graph
3701 Commits
Author SHA1 Message Date
Martin BallandClaude Opus 5.5 dd39514ef0 Merge origin/main into craft-i18n-cutover
Keeps the deletion of the in-repo catalogs; #12357's Layer Via Copy/Cut
rows are in craft-i18n.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 22:28:32 +00:00
David Mann dff5d29584 Merge remote-tracking branch 'origin/main' into fix/12397-wand-seed-tests-antialias 2026-11-08 21:03:43 +01:00
David MannandClaude Opus 5.5 bfe5d2a671 Magic Wand seed-outside tests: keep Anti-alias on, expect the measured fringe (#12397)
Instead of turning Anti-alias off, the tests keep the default setup and
expect the reference application's measured results (#11776, #11779):
the selection ends at 381, Subtract at 219, Intersect runs 219-381.
Suggested by hectorviov on #12399.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 21:03:37 +01:00
David MannandClaude Opus 5.5 4e43c23e43 Magic Wand seed-outside tests: Anti-alias off, so they test the modes with hard edges (#12397)
#12146 gives near-matching neighbours a soft fringe when Anti-alias is on.
The #11848 tests left Anti-alias at its default and expected exact column
bounds, so main's engine tests failed (381 instead of 380).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:31:42 +01:00
David Mann a17acfa7a7 Merge pull request #12049 from DuBearte/fix/11897-clear-on-background
Brush: Clear on the Background paints as Normal and the Mode reads Normal
2026-11-08 20:16:11 +01:00
David Mann 3ef886c2e0 Merge pull request #12382 from TrevorBrown-dev/fix/layer-via-copy-capital-12357
Menus: Layer Via Copy and Layer Via Cut with a capital V, as in the reference (#12357)
2026-11-08 20:10:52 +01:00
David Mann 8acd45bfa9 Merge pull request #12298 from TrevorBrown-dev/fix/patch-destination-selection-12119
Patch: in Destination mode the selection moves with the patch (#12119)
2026-11-08 19:56:56 +01:00
David Mann 83e2172336 Merge pull request #11848 from DuBearte/fix/11779-wand-seed-outside
fix(engine): Magic Wand Subtract and Intersect grow from the seed through the selection (#11779)
2026-11-08 19:33:29 +01:00
David Mann fa41efebc5 Merge pull request #9348 from VoiceLessQ/pattern-preview-wrap-tools
paint: 12 more painting tools wrap across the canvas edges in Pattern Preview (#8883)
2026-11-08 19:22:34 +01:00
Trevor BrownandClaude Sonnet 5.5 76ad52bf47 Patch Destination: the rectangle check is capped and scans in bands (#12119)
Cause: is_hard_rect read selection coverage over the selection's own content bounds in one buffer. After a transform those bounds can reach far past the canvas, while the patch-size guard only measures the on-canvas part, so a huge mostly-off-canvas selection could stall or exhaust memory.

Fix: is_hard_rect now calls is_hard_rect_within with MAX_PIXELS (the patch's own cap); bounds over the cap are not a hard rectangle and the selection stays in place. Otherwise coverage is scanned in 64-row bands, stopping at the first pixel below 1.0.

Verified: new test covers a limit below and at the pixel count and a hole in the third band; patch (25) and retouch (126) lib tests pass, clippy -D warnings and fmt clean.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 wrote at medium
2026-11-08 12:38:04 -05:00
b5501b9bd1 PR #12049: keep a held ~ erasing on the Background, keep Clear on a locked one
The engine turned every "mode": "clear" stroke on the Background into
Normal, so a held ~ painted the foreground there instead of erasing to
the background colour as on main. The options bar already turns a chosen
Clear off at the press, so the engine conversion was not needed: drop it
and keep the one check in clear_off_on_background.

A Background with locked pixels refuses the stroke, so a chosen Clear now
stays chosen there. The "Tool state machine" doc line is back on
tool_event.

Co-Authored-By: DuBearte <DuBearte@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 18:20:55 +01:00
David Mann bb7a1289b0 Merge remote-tracking branch 'origin/main' into pr-12049 2026-11-08 18:05:36 +01:00
Martin BallandClaude Opus 5.5 9dd39df78d feat(i18n): look up translations through the craft-i18n crate
The i18n module becomes a thin Lang wrapper over craft_i18n::I18n and the
catalogs embedded from craft-i18n. Plurals use trn_plain and PhotoCraft
keeps its own fmt; @id rows come only from PhotoCraft's own rows that keep
the label's shape. Detection, PHOTOCRAFT_LOCALE, tag bounds and the 35
offered languages are unchanged.

Serbian: sr, sr-RS, sr-Cyrl-RS and sr-ME reach sr-latn, the only Serbian
catalog, and a saved interface.language "sr" migrates to "sr-latn".

The menu completeness test becomes a report that never fails, and
`cargo xtask i18n-coverage` is replaced by `cargo xtask i18n-report`, which
reads CRAFT_I18N_DIR through the crate. Docs describe the new system.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 16:16:46 +00:00
David Mann 532421aa2a Merge pull request #12212 from TrevorBrown-dev/feat/open-as-smart-object-11678
File: Open as Smart Object... opens a file as a new document holding one embedded smart object (#11678)
2026-11-08 16:59:25 +01:00
David Mann 8c94ba8a15 Merge pull request #12203 from hectorviov/fix/count-history-names
Count tool: History names Add/Drag/Delete Count Object, Add Count Group and one name per group setting (#11763)
2026-11-08 16:58:51 +01:00
David Mann 89660c82fe Merge pull request #12231 from hectorviov/fix/note-stored-values
Notes: new notes store the reference application's colour, popup rectangle and local-time date
2026-11-08 16:57:08 +01:00
David Mann b0953546ca Merge pull request #12148 from hnoerenberg/fix/straighten-layer-states-12033
fix(engine): Straighten Layer greys out and refuses the states the reference does
2026-11-08 16:56:18 +01:00
David Mann 45c76d5c5c Merge pull request #12144 from hnoerenberg/fix/indexed-type-selection-12041
fix(engine): the Type tool leaves a selection in Indexed Color
2026-11-08 16:56:00 +01:00
David Mann 32ce08364b Merge pull request #9991 from ljkx/feat/raw-xmp-crop
Camera Raw sidecars: apply the crop, stop reporting RawFileName (#9985)
2026-11-08 16:55:43 +01:00
Trevor BrownandClaude Sonnet 5.5 66d5ebed89 Menus: Layer Via Copy and Layer Via Cut with a capital V, as in the reference (#12357)
Cause: the menu bar, the command labels and the selection tools' canvas menu spelled the two commands "Layer via Copy" and "Layer via Cut", while the reference application (and PhotoCraft's own History names and ATN event table) use a capital V.

Fix: renamed the labels in crates/ui-egui/src/menu_catalog.rs, the two spec! labels in crates/engine/src/cmd/edit_cmds.rs, and the rows in crates/ui-egui/src/canvas/canvas_tool_menu.rs with its test assertions. In the 33 locale files crates/ui-egui/src/i18n/*.tsv that carry the keys, only the key column of those two rows changed (66 rows), so every translation is kept and no language falls back to English. New tests are in crates/ui-egui/src/layer_via_label_tests.rs, registered with one mod line in lib.rs like the other *_tests.rs files.

Verified: the 5 new tests pass; canvas_tool_menu 51 passed; engine edit_cmds 112 passed; i18n 66 passed and 8 live_tests failed with the known local font panic in epaint fonts.rs (font-dependent, green on CI); cargo xtask parity left docs/parity-checklist.md unchanged; clippy -D warnings and fmt are clean. TSV check against HEAD: 34 files, same row count in each, exactly 66 rows differ (2 per file in 33 files, Hindi has none), only the key column of those rows differs, the translation column is byte-identical, no old key remains, and no CR bytes in any touched file.

Not changed: the translations' text, the History names and the ATN event table (already the capital V), docs/ui-parity.md and the other parity docs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 wrote at medium
2026-11-08 10:14:55 -05:00
Trevor BrownandClaude Opus 5.5 1483d5193d Merge upstream/main into fix/patch-destination-selection-12119
Conflict in retouch_cmds/patch.rs with #12255 (one History name, "Patch Selection", for every mode): kept the Destination branch that moves a hard rectangular selection with the patch and gave it HISTORY_NAME, and restored Plan's destination flag, which #12255 had removed as unused. patch tests 24 passed (including #12255's history-name test), clippy and fmt clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and resolved
2026-11-08 09:51:27 -05:00
Trevor BrownandClaude Sonnet 5.5 f3de619549 Patch Destination: a hard rectangle partly off the canvas moves with the patch (#12119)
Cause: is_hard_rect required the selection's bounds to equal plan.area, which is clipped to the canvas. A hard rectangle reaching past the canvas (e.g. moved there with select.transformSelection, which keeps the off-canvas pixels) has bounds larger than the area, so the pixels were patched but the selection was left behind.

Fix: treat the selection as a hard rectangle when nothing is selected outside its own bounds and every pixel inside those bounds is fully selected, then translate the whole selection by the drag offset. Feathered, non-rectangular and holed selections still stay in place.

Verified: new test destination_moves_a_hard_rectangle_that_extends_past_the_canvas failed before the change (selection stayed at -20,100..40,140 while the pixels changed) and passes after, including one-undo restore of pixels and selection. cargo test -p photocraft-engine --lib patch (19 passed) and retouch (120 passed), clippy -D warnings and fmt clean.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 wrote at medium
2026-11-08 09:47:11 -05:00
David Mann 848fa306f5 Merge pull request #12255 from SBaksa/patch-history-names
Patch: History names as the tool's (fixes #12117)
2026-11-08 15:25:52 +01:00
David Mann 7acc08cbb9 Merge pull request #12306 from hectorviov/fix/mixer-indexed-bitmap
Mixer Brush: refuse Indexed Color and Bitmap documents with the alert (#11958)
2026-11-08 15:09:44 +01:00
0c6b564536 Magic Wand: skip the selection mask when Contiguous is off; more tests (#11848)
PR #11848 built the full-area selection mask for every Subtract and
Intersect click, though the global match (Contiguous off) ignores it.
Build it only when the fill is contiguous.

Tests for the review: Contiguous off keeps the global match, Subtract and
Intersect without a selection select nothing, the row check fails on a hole
in the selected run, and the fill's half-selected threshold (0.49 vs 0.5)
at the seed and at the fill's edge.

Co-Authored-By: DuBearte <130190089+DuBearte@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:04:43 +01:00
David Mann 7d8c18c34e Merge pull request #12265 from hectorviov/fix/color-replacement-refuses-modes
Color Replacement: refuse RGB/32, Grayscale, Bitmap and Indexed documents
2026-11-08 14:56:43 +01:00
David Mann e5decd0b85 Merge remote-tracking branch 'origin/main' into pr-11848 2026-11-08 14:54:13 +01:00
Trevor BrownandClaude Sonnet 5.5 fec4980c7b Patch Destination: clone only the selection; guard, error path and redo tests (#12119)
The Destination branch of the Patch command no longer clones the whole document: it takes only the selection before the target surface borrows the document, and computes the selection's content bounds once, passing them to `is_hard_rect` and reusing them for the translate. Behaviour is unchanged.

Tests are split and extended: Destination with a feathered, ellipse, lasso and holed-rectangle selection leaves the selection in place while the patch still lands; Destination with no selection fails, adds no history entry and a following undo reverts the previous step, not the setup; redo restores both the patched pixels and the moved selection; Source and Content-Aware are separate tests, the latter asserting a pixel change; the main test also asserts source pixels unchanged, the destination carrying the source texture, the moved selection still hard-edged, and the returned damage and offset. A rectangle larger than the patched area and a partly covering selection without a hole cannot be built, because the patched area is the selection's own content bounds; an inverted selection makes the area the whole canvas, which Patch refuses.

Verified: `cargo test -p photocraft-engine --lib patch` 18 passed; `--lib retouch` 119 passed; clippy `--all-targets -D warnings` clean; `cargo fmt -p photocraft-engine` clean.

Not changed: the coverage pass in `is_hard_rect` (one pass over the patched area per command, the same order of work as the patch itself).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 wrote at medium
2026-11-08 08:44:18 -05:00
David Mann dada110838 Merge pull request #12263 from hectorviov/fix/color-replacement-history-name
Color Replacement: History names each edit "Color Replacement Tool"
2026-11-08 14:35:37 +01:00
SBaksa 28d2d9095d History and plug-in names: trim the fillers and blank padding at the ends too
trim_name drops white space, ZWNJ/ZWJ and the blank characters (Braille blank, Hangul fillers,
invisible operators, tag and other default-ignorable blanks) from both ends. A variation or
Mongolian free variation selector or CGJ that belongs to a kept character stays, and so do Hangul
filler jamo that complete a syllable. History names use the same trim as plug-in names.
2026-11-08 22:40:53 +11:00
David Mann 320d285e1a Merge pull request #9887 from Firstbober/feat/indexed-color-matte
feat: add Indexed Color Matte options
2026-11-08 12:20:27 +01:00
SBaksa c2f85a590f Plug-in names: store the name trimmed, count U+2800 and unassigned ignorables as blank
A manifest name such as "\nTone" was checked trimmed but shown as given, so a line break reached
the menu; padding also got past the 64-character limit. The name is now stored trimmed.
The blank Braille pattern and the unassigned default-ignorable ranges no longer count as visible,
in plug-in names and History names. Applying a patch no longer takes the selection icon, and the
History source column has its doc comment back.
2026-11-08 19:59:43 +11:00
SBaksa be7ebc078a Lasso: refuse a history name that shows nothing or hides formatting
A historyName of zero-width, filler or BOM characters showed an empty History
row, and a bidi control or line separator could make a row read differently
from its name. A history name now has no control or line-break character
anywhere, loses white space and ZWNJ/ZWJ at its ends, and is then held to the
plug-in menu-label rule, now shared as
photocraft_plugins::manifest::printable_name (1-64 characters with a visible
one, no hidden format characters; plug-in names also refuse U+2028/U+2029).
2026-11-08 17:55:02 +11:00
Hector Villarreal 8272e6b9f4 Mixer Brush: refuse Indexed Color and Bitmap documents with the alert (#11958)
A Mixer Brush stroke in an Indexed Color or Bitmap document now fails before
anything is painted, with the reference's alert texts (convert to RGB Color /
Grayscale mode to edit); paint.mixerBrush returns that error and leaves the
document and History unchanged. A press on the canvas shows it as an Error
alert instead of a status-bar message. RGB, CMYK, Lab and Grayscale still
paint.
2026-11-08 00:28:08 -06:00
Trevor BrownandClaude Sonnet 5.5 4f629dc4dd Patch: in Destination mode the selection moves with the patch (#12119)
A plain rectangular selection is translated by the drag offset in the same history state as the pixel change. Source, Content-Aware, feathered and non-rectangular selections keep today's behaviour.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-08 00:57:48 -05:00
Trevor BrownandClaude Sonnet 5.5 7c36a5c9e9 Open as Smart Object: the reply names the document as admitted (#12212 review)
When an earlier document is held for its profile prompt and the next open skips the prompt, admission numbers the second document photo-1 again, but the reply reported the name read before admission (photo-2). The reply now reads the name from the admitted document.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-07 23:48:22 -05:00
Hector VillarrealandClaude Opus 5.5 e49c842653 Color Replacement refusal test compares the whole surface (CodeRabbit review on crates/engine/src/cmd/color_replacement_modes_tests.rs:34)
The no-painting check looked at row 32 only; it now snapshots every pixel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:17:14 -06:00
Trevor BrownandClaude Sonnet 5.5 92ccd8b7d6 Open as Smart Object: a damage-only hold is admitted as a smart object; the name is taken again at admission (#12212 review)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-07 23:04:32 -05:00
SBaksa 60d0895c27 Patch Tool history: refuse a bad name with Polygonal Lasso, give the step its icon
historyName is validated before polygonal picks the step name, and the
History panel shows the selection icon for a Patch Tool step.
2026-11-08 14:32:37 +11:00
Hector Villarreal 60e45bb926 Color Replacement: refuse RGB/32, Grayscale, Bitmap and Indexed documents (#11923)
paint.colorReplacement and its live stroke refuse the modes and depths
the reference application refuses, with its words: 32 bit per channel
RGB, Grayscale and Bitmap (only full color modes), and Indexed Color.
Nothing is painted and no history is added. RGB/8, RGB/16, CMYK and Lab
paint as before; cases that weren't measured keep today's behaviour.
2026-11-07 21:00:56 -06:00
Trevor BrownandClaude Sonnet 5.5 022cf4f125 Open as Smart Object: keep the damage flag, the Smart Object flag and unique names
CodeRabbit round 2 on #12212:
- 4504550937 (b): a PSD that recovered from damaged data is held for File > Open's confirmation (the app passes confirmDamage; headless callers open it as before).
- 4504550942: a document held for its prompt counts when numbering, and the number is re-taken when each document is admitted, so two held files with one stem no longer share a name.
- 4504550943 is not changed here (needs colour transforms at regeneration), as are 4504550937 (a), (c) and 4504550953.
- 4504550946: the successful reply carries smartObject, so a document queued behind another open prompt is admitted with the Open As Smart Object history label.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-07 21:59:52 -05:00
Triwarr 2d16a609ea Merge remote-tracking branch 'origin/main' into pdf
# Conflicts:
#	docs/scorecard.md
2026-11-08 03:48:47 +01:00
Hector Villarreal 6b9628c92f Color Replacement: History names each edit "Color Replacement Tool" (#11933)
A Color Replacement click, stroke or Shift+click segment adds one History
state named "Color Replacement Tool", as in the reference application.
The command's label is the same name, so a recorded action's step reads
the same as the History state.
2026-11-07 20:47:03 -06:00
David Mann 62776f4b58 Merge branch 'main' into feat/export-last-folder-10192 (merge queue) 2026-11-08 03:45:33 +01:00
517d0a9eea Merge origin/main into #9887 (Indexed Color Matte)
Keeps main's Fade preview path in filter_preview.rs and passes the PR's
tool colours to the non-Fade preview; catalogs keep both sides' rows.

Co-Authored-By: Firstbober <firstbober@tutanota.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 03:23:31 +01:00
SBaksa 974c32ddc6 Patch: History names as the tool's (#12117)
select.lasso takes an optional historyName; the Patch tool sends "Patch Tool" so its
lasso is that step (the Lasso tool keeps "Lasso"). paint.patch records "Patch Selection"
in every mode; the content-aware job keeps its progress text.
Tests: patch_history_names_tests (engine) and patch_tool_history_names_tests (ui-egui).
2026-11-08 13:09:17 +11:00
David Mann 9da3cda6a7 Merge origin/main into feat/raw-xmp-crop
# Conflicts:
#	docs/file-format-parity.md
2026-11-08 02:56:35 +01:00
David Mann b30c5c8095 Merge pull request #12142 from hectorviov/fix/star-tool
Toolbar: Star Tool in the shape slot between Polygon and Line (#11377)
2026-11-08 02:56:14 +01:00
David Mann ed893ea812 Merge pull request #12188 from davidmann4/fix/11909-bg-eraser-tolerance-field
Background Eraser: Tolerance takes whole numbers 1-100 with an alert, bar tooltips (#11909)
2026-11-08 02:54:43 +01:00
David Mann 4a7d746c0a Merge pull request #11823 from davidmann4/fix/11812-brush-opacity-minimum
Brush: opacity minimum 1 % and "00" sets 100 % (#11812)
2026-11-08 02:49:28 +01:00