Commit Graph
2735 Commits
Author SHA1 Message Date
yoriswind 7b0d68c8e3 Fix Artboard action round trips and singleton links 2026-11-01 17:48:22 -08:00
yoriswind c06d7d1e34 Simplify recorded guide validation 2026-11-01 17:00:10 -08:00
yoriswind ed71465b89 Apply measured Artboard background settings without resizing 2026-11-01 16:58:20 -08:00
yoriswind 863597ba90 Merge remote-tracking branch 'origin/main' into codex/atn-missing-action-events 2026-11-01 16:53:50 -08:00
aiongg fab871dadb Merge pull request #10535 from davidmann4/integration-batch-11
Integration batch 11: 9 ATN PRs on main
2026-11-01 19:11:02 -05: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
yoriswind e6f23fd8e2 feat(engine): map limited Artboard-from-Group ATN forms
Preserve the target-only call and exactly reconstruct empty-guide settings. Apply preset tags without resizing, retain fractional RGB, and refuse unresolved guide state before mutation. Nonempty associations and unproved flag values remain unfinished.
2026-11-01 15:26:29 -08:00
yoriswind 82e613fca8 docs: explain ATN adapters and reference survey scope 2026-11-01 14:34:32 -08:00
yoriswind f39d0b8280 feat(engine): replay the Crop Photos ATN parent once 2026-11-01 14:06:12 -08:00
yoriswind 1130c8a66c feat(engine): map Type to Shape and radius-only Shape Blur ATN 2026-11-01 13:53:09 -08:00
yoriswind 71b98c8134 feat(engine): replay target-only ATN menu events 2026-11-01 13:36:08 -08:00
yoriswind b4c7656c49 feat(engine): replay Smart Object file events from ATN 2026-11-01 13:09:16 -08:00
yoriswind 5881923406 fix(engine): replay recorded link and unlink explicitly 2026-11-01 12:49:47 -08:00
5f63b3fe4c Integration: address CodeRabbit review of batch 11
- #8714: a Camera Raw Filter step imports only with a process version
  the PSD mapping has verified (PrVN/PrVe 5/184549376 or 6/251920384)
  and a CrVe text; other steps stay kept steps that save back unchanged.
- #7642: the patternName fallback matches names only, so a pattern
  whose id equals that text no longer wins. A step that names its
  pattern only by patternName exports that name.
- #8327: the iso-2022-jp label is refused (its names are 7-bit escape
  sequences that would pass through as ASCII).
- #10417: the Levels export gate also refuses endpoints of the wrong
  shape (both ends text, a text gamma) and channels that aren't objects.
- #10459: a set paused at a step dialog keeps the rest of the set by
  action name, so actions.move while the dialog is open neither plays a
  foreign action nor skips one; "Played N steps" covers the whole Play
  (steps before the pause and the dialog's own step); the suggested .atn
  name is cut to 255 bytes at a character boundary; actions.list returns
  every set, empty ones too; set_names dedups with a HashSet.

Co-authored-by: FinnNk <1424666+FinnNk@users.noreply.github.com>
Co-authored-by: Silvertree2010 <133535268+Silvertree2010@users.noreply.github.com>
Co-authored-by: regexer <3689390+regexer@users.noreply.github.com>
Co-authored-by: marshiyar <57055687+marshiyar@users.noreply.github.com>
Co-authored-by: hectorviov <1501713+hectorviov@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 17:51:02 +01:00
David MannandClaude Opus 5.5 4614b3e119 Integration: build two ATN test files with defaults (#8714, #8858, #8327)
#8327 adds `AtnFile::encoding`. Two tests from other PRs in the batch
build an AtnFile literal without it (#8714 fix-up's Camera Raw save-back
test, #8858's Vibrance string-class test), so the engine's lib tests
didn't compile on the batch. They now fill the rest with
`..Default::default()`, as the other literals in the file do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 15:46:37 +01:00
David Mann bb8edaf596 Merge PR #8327: Actions: read .atn step names in the code page Photoshop wrote them in (#6743)
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/cmd/atn_cmds/tests.rs
2026-11-01 15:36:45 +01:00
David Mann 6aba9ddfd8 Merge PR #10459: Actions panel: New Set, and New Action records into the selected set (#7535)
# Conflicts:
#	crates/engine/src/cmd/atn_cmds/tests.rs
2026-11-01 15:13:27 +01:00
1d0bc23c3e Integration: don't count symbols on Mac Roman letters as windows-1252 (#8327)
The previous fix-up counted § ® ¾ ¿ (and ¹ » ¼ ½) as common windows-1252
text, but those bytes are Mac Roman ß Æ æ ø (π ª º Ω). Mac names with no
Unicode copy such as "Weiß", "Gaußscher Weichzeichner", "Størrelse",
"Lærredstørrelse" and "Æble" then tied and read as windows-1252 ("Wei§",
"St¿rrelse"), where the PR read them right.

Keep only the 12 symbols whose byte is not a letter in Mac Roman
(¡ ¢ £ ¥ © « ° ± ² ³ ¶ ·). Windows "Größe »", "50 °" and "« auto »" still
read as windows-1252; a name whose only non-ASCII character is ® ("Logo
®") reads as Mac Roman "Logo Æ" again, as on the PR head (tested as a
known limitation).

Co-authored-by: regexer <3689390+regexer@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 15:00:36 +01:00
aiongg 84732d09bc Merge pull request #9618 from RickiNano/prefs/gpu-adapter-typed-read
Prefs: startup reads Graphics Adapter through the typed Performance section
2026-11-01 08:56:16 -05:00
aiongg dbe24c7e92 Merge pull request #10658 from RickiNano/fix/10640-preset-group-reimport
Ask before re-importing a brush or gradient file over its group (#10640)
2026-11-01 08:55:56 -05:00
af59f65b82 Integration: keep windows-1252 symbols in .atn step names (#8327)
The code-page guess counted only letters and typographic marks as common
text in single-byte pages, so a Windows step name with no Unicode copy and
a Latin-1 symbol read better as Mac Roman, whose letters sit on those
bytes: "Logo ®" showed as "Logo Æ", "Größe »" as "Grˆfle ª" (main showed
them right), and Save Actions wrote that text back.

When the guess lands on Mac Roman, weigh it against windows-1252 once more
with common Latin-1 symbols (© ® ° § « » ± ½ …) counted and the letters
windows-1252 has only at 0x80-0x9F left out. Mac Roman keeps the names
only when it still reads better; a tie goes to windows-1252, confident
with a Latin hint and flagged uncertain otherwise. Mac Roman "Größe",
"Ändern", "Contrôle", "Arrière-plan" and the corpus French sets still read
as Mac Roman.

Co-authored-by: regexer <3689390+regexer@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:43:03 +01:00
David Mann acddb90d47 Merge PR #8736: Refuse exporting an adjustment edit that names its layer to .atn
Conflict: main moved crates/engine/src/atn_cmds.rs into crates/engine/src/cmd/atn_cmds/; the refusal is ported into steps_make.rs set_to's layer.setAdjustment arm unchanged. The tests followed the rename.
2026-11-01 14:41:56 +01:00
David Mann f2fd6efdc0 Merge PR #7642: Prefer an installed pattern ID over a colliding name in ATN import 2026-11-01 14:41:19 +01:00
David Mann 2c0ad3fc34 Merge PR #8714: feat(actions): import recorded Camera Raw Filter steps as filter.cameraRaw 2026-11-01 14:41:16 +01:00
David Mann 1b308ca411 Merge PR #8858: fix(atn): import Photoshop's string class for Vibrance adjustment Sets 2026-11-01 14:41:14 +01:00
David Mann fa69a54f74 Merge PR #10424: ATN export: Stroke steps write the settings they leave at default (#8303) 2026-11-01 14:41:09 +01:00
David Mann 53aa1a4a2c Merge PR #10417: fix(engine): refuse strict .atn export of a partial Levels edit (#8530) 2026-11-01 14:41:06 +01:00
adc4e45fe7 Integration: look up a recorded pattern id by id alone before the name (#7642)
`resolve_or_named` resolved the recorded `Idnt` with `resolve`, which also
matches by name in each collection. So a document pattern named like the
id beat the library pattern with that id, and a foreign id equal to some
pattern's name (`Idnt "Red"`, `Nm "Blue"`) played "Red" and never tried
the recorded name. The id is now looked up by id only (document, then
library), then the name decides. A library pattern found by id whose
pixels can't be read reports that instead of falling back, main's
paintable check still runs, and a pattern installed neither way is named
in the error (`no pattern "My Wood" (id "...")`) instead of only its UUID.

Co-authored-by: Silvertree2010 <133535268+Silvertree2010@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:36:29 +01:00
68b869eac4 Integration: save Vibrance adjustment steps with the string class vibrance (#8858)
Vibrance's adjustment class is now Photoshop's string id `vibrance`, so Set and Make steps
save back with the class Photoshop runs instead of the `Vbrn` code it refuses (#8383).
`Vbrn` moves to LEGACY_CLASSES, so actions saved before still load. A recorded Vibrance
with non-zero temperature or tint stays a kept step and saves back unchanged.

Co-authored-by: jturnbach <70334515+jturnbach@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:34:17 +01:00
4d2fd2214d Integration: Camera Raw Filter steps save back and refuse unknown keys (#8714)
A loaded Camera Raw Filter step became an import-only filter.cameraRaw, so
Save Actions dropped it (skipUnsupported) or failed, where before it was
written back byte for byte. Keys outside the table (e.g. PerV = 20, a
profile name) were ignored, so the step replayed a different look without
being reported.

The step now keeps its recorded descriptor in a hidden param and saves back
as that descriptor while its params are still what it imports as; edited or
PhotoCraft-recorded steps are still refused. A step with any unknown key, or
with no settings, stays a kept Photoshop step, as the PSD mapping does.

Co-authored-by: FinnNk <1424666+FinnNk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:31:13 +01:00
David Mann d86f678325 Merge main into #8327 2026-11-01 14:27:37 +01:00
David Mann 0ee627e2fc Merge main into #8858 2026-11-01 14:22:49 +01:00
David Mann 115d20fd74 Merge main into #7642 2026-11-01 14:18:26 +01:00
aiongg 713197635d Merge origin/main into prefs/gpu-adapter-typed-read 2026-11-01 13:16:28 +00:00
Khawar Zamman Wani 97a83f074f Bound PSD layer and mask import memory before decoding (#10503)
* Bound PSD import pixel storage before decoding

* Include ZIP inflater workspace in PSD preflight

* PSD import: budget unknown-mode layers, stop after a fatal layer error, keep best-effort for non-allocation limits (#8593 review)
2026-11-01 12:33:09 +01:00
RickiNanoandClaude Opus 5.5 586563fc2b Address review: one group-name set for clashes, test the .grd prompt (#10640)
- free_group's brush check builds the set of group names once instead of
  scanning every preset for each numbered candidate.
- reimporting_a_gradient_file_asks_before_touching_its_group covers the
  gradient branch of the prompt: no prompt for a new group, Cancel imports
  nothing, Import as New Group adds "Fire (2)", Replace swaps "Fire" alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:18:24 +01:00
RickiNanoandClaude Opus 5.5 122e55a4a0 Ask before re-importing a brush or gradient file over its group (#10640)
Importing an .abr or .grd whose name matched an existing group silently
deleted that group's user presets (edited or moved-in ones included); the
preset libraries are outside document history, so it could not be undone.

- brush.presets.importAbr / gradient.presets.importGrd: `replace` now
  defaults to false; a taken group name imports beside it as "Kit (2)",
  "Kit (3)"…, and the result's `group` names the one it landed in.
- UI: when the group already holds the user's presets, a prompt asks
  Import as New Group (default) / Replace (can't be undone) / Cancel.
  Agents drive it with ui.dialog.set {"choice":"replace"} / confirm / cancel.
- The status bar names the group the file landed in.

Fixes #10640

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 09:49:33 +01:00
Hector VillarrealandClaude Opus 5.5 b67fb07557 Actions panel: Save Actions… suggests a Windows-safe name for device and control-character sets (CodeRabbit review on crates/ui-egui/src/panels/actions/tests.rs:883)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 01:07:16 -06:00
Hector VillarrealandClaude Opus 5.5 b544021d49 Actions: recording into an empty loaded set keeps its exact name (CodeRabbit review on crates/engine/src/cmd/atn_cmds/files.rs:65)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 00:59:36 -06:00
Hector VillarrealandClaude Opus 5.5 63c541342e Actions: Save Actions… writes an empty New Set set (FredApps review)
An empty set made with New Set lasted only the session, and Save
Actions… on it failed with "no actions in a set named …". Photoshop
saves an empty set to the .atn, and the ATN writer handles a set with no
actions, so `actions.save` now writes it; loading such a file brings it
back as an empty folder (once, however often it is loaded). Saving every
action with none is still refused, as is a set that doesn't exist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 00:29:37 -06:00
Hector VillarrealandClaude Opus 5.5 d244f11a85 Actions: Save Actions… finds a set by its exact name (FredApps review)
A set name with surrounding spaces comes from a Photoshop .atn (load
keeps the file's name) or an older actions.json, and New Action records
into such a set under that exact name. `actions.save` trimmed the name
it was given and then compared exactly, so Save Actions… on that set
failed with "no actions in a set named …". It now takes an existing
set's exact name, as `actions.record` does, and trims any other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 00:28:04 -06:00
Mohamed MahjoubandClaude baf8e654e8 Keep a layer's place on its board when dragged to another artboard (#10489)
Artboards share one canvas, so a layer dragged from one board onto another in
the Layers panel (layer.moveTo, including the Alt-drag copy) changed parent but
kept its old canvas position, outside the board it now belonged to, and the
board clipped it away.

apply_move now shifts a layer that lands on a different board by the offset
between the two boards' origins, through move_layers (the Move tool's path).
Reordering within a board, leaving or joining a board from the top level,
position-locked layers and moving a board itself are unchanged. The Layers
panel's drop preview uses apply_move, so it shows the same result.

Locks are read before the drop as well as after, so a layer whose position
was locked only through the board it left stays put (review follow-up).

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-01 05:14:15 +01:00
aiongg 799e129628 Merge pull request #10145 from davidmann4/integration-batch-10
Integration batch: 18 PRs on main
2026-10-31 13:31:44 -04:00
Khawar Zamman Wani f3caa319a2 Keep brush state intact when preset tip loading fails (#10288) 2026-10-31 13:07:18 -04:00
David MannandClaude Opus 5.5 e46be131e9 Merge #9274's current head (7da275ea6) into the batch
The author's main merge brings one new test (full coverage keeps Threshold over
extreme float channels) and rewords the #9483 tone comment; its main side
(b1cfff9d3) is already in the batch. Docs keep the batch's newer status lines and
revision rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:43:14 +01: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
Hector VillarrealandClaude Opus 5.5 c69ad0e86c Actions: recording into an existing set keeps its exact name (CodeRabbit follow-up on actions_cmds.rs:373-378)
Test: record_keeps_an_existing_sets_exact_name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 03:42:23 -06:00
Hector VillarrealandClaude Opus 5.5 02af2370b1 Actions panel: New Set makes an empty set, and New Action records into the selected set (#7535)
Photoshop's Actions panel has a folder button and a New Set… menu item that make an
empty action set (default "Set 1"); New Action lands in the selected set. PhotoCraft
showed sets from Load Actions but couldn't make one.

- actions.newSet: an empty set (default "Set N"), kept for the session until it
  holds an action; actions.record takes a `set` for the new action.
- Actions panel: the folder button and New Set… open a name dialog; empty sets show
  as folder rows; a click on a folder row selects it; New Action records into the
  selected action's set, else the selected set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 02:08:22 -06:00
Hector VillarrealandClaude Opus 5.5 e57d9d1cbe ATN export: Stroke steps write the settings they leave at default (#8303)
Photoshop opens the Stroke dialog for a Stroke descriptor without its blend
mode, even with dialogs off, so an exported action stopped there. stroke_to
now writes edit.stroke's defaults for location (center), opacity (100),
mode (Normal) and preserveTransparency (false) when the step leaves them out,
as Photoshop's own recorded Stroke carries them all.

Fixes #8303

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:58:26 -06:00
marshiyar da1b2e0070 fix(engine): refuse strict .atn export of a partial Levels edit (#8530)
layer.setAdjustment changes only the settings it names, but the Levels
descriptor writes each range whole and fills the end an edit did not name
with 0 or 255. A recorded {"kind":"levels","inBlack":31} was exported as
Inpt [31, 255] with nothing skipped, so replaying it also reset a retained
Input White. This is the Levels half of #8701 (Color Balance and
Hue/Saturation, #8531).

levels_complete accepts a Levels edit only when, for the master curve and
every channel object, inBlack/inWhite are both given or both absent, and
likewise outBlack/outWhite. set_to uses it as the levels arm of the
existing completeness check, so strict export refuses the step and a
non-strict export lists it as skipped.

Test partial_levels_edits_refuse_strict_export fails without the fix (the
export succeeds with Inpt [31, 255]) and passes with it.

Fixes #8530
2026-10-30 20:12:26 -07:00