1954 Commits
Author SHA1 Message Date
aiongg 360150dcd4 Merge pull request #7281 from wanikhawar/claude/blur-tool-lag-684245
perf(retouch): releasing a Blur/Smudge/Dodge stroke takes its live pixels
2026-10-24 11:13:32 -04:00
Ayan 47d9266fa1 Make Crystallize 4x and Pointillize 3x faster on 24 MP layers (#6200)
* Speed up Crystallize and Pointillize with cell-space rasterization

* Measure pixelate commands on unlocked layers and Background separately

* Record pixel-exact 24 MP filter and command benchmarks
2026-10-24 11:13:03 -04:00
Dylan M. Taylor 1c3db2e5c3 security(engine): bound script, batch, adjustment, import and export work (#8954)
* security(engine): bound script, batch, adjustment, import and export work from file input

Scripts calling scripts share depth and step budgets; batch inside a played action charges the playback tree; destructive adjustments and mask adjustments visit stored tiles when the content bounds are a huge sparse grid; Copy Layers checks group depth; aux flattening and brush import refuse oversized canvases; video footage frames are budgeted, Render Video names frames after the leaf of the document name and reads no linked files for restricted sessions; ATN names are clipped; profile paths are read as regular files only; prefs.set no longer treats colorSettingsX as colorSettings.

* style(engine): rustfmt

* engine: address review - refuse unbounded sparse adjustments, carry batch playback charges, charge played steps to scripts, clip ATN targets, stricter colorSettings paths, cap image-sequence frames
2026-10-23 22:09:29 -04:00
David Mann 2b0deddec2 Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/ui-egui/src/panels.rs
2026-10-24 00:51:37 +02:00
Omar SaidahmedandBrandon Thomas afd5127dc8 perf: skip redundant smart object resamples and per-pixel reads (#6406)
* skip redundant smart object resamples and per-pixel reads

* resample smart objects only when their re-render fails

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 18:36:54 -04:00
aaaa990314 Crop: Delete Cropped Pixels keeps type and smart objects whole (#7116) (#7117)
* Crop: Delete Cropped Pixels keeps type and smart objects whole (#7116)

crop_doc cropped every surface for_each_surface visits, including the
rendered caches of type layers and smart objects. Those move by shifting
their stored pixels, so the cut stayed and showed as soon as they were
moved. Like Photoshop, Delete Cropped Pixels now deletes pixel-layer data
(and fill caches) only; shapes are re-rendered as before.

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

* Address review: Delete Cropped Pixels crops stored video frames again (#4956)

The pixel-layer-only crop path skipped a video layer's stored frames and
original footage, so scrubbing brought the cropped pixels back. Crop them
with the displayed content, as before, and test a shrinking crop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Address CodeRabbit review: the turned crop keeps type and smart objects whole before rotating

The pass that drops what a turned crop deletes before the rotation still cut
every cached surface. The rotation re-renders type and smart objects, but a
smart object whose source is unavailable keeps its cache, so it stayed clipped.
That pass now uses the same pixel-layer-only rule as the crop itself.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 18:36:28 -04:00
Lucas RodriguesandBrandon Thomas 15db2d8560 Paste: keep the pasted image on the canvas (#6759) (#6798)
* Paste: keep the pasted image on the canvas (#6759)

Paste centred the clipboard image on the view centre with no clamp, so an
image that fit the canvas could land partly off it, and one larger than
the canvas could leave blank edges. Now, per axis, an image that fits stays
wholly inside the canvas, as close to the view centre as it can, and one
larger than the canvas starts at the canvas's top-left edge, as in
Photoshop. Paste Into / Outside still centre on the selection
("clampToCanvas": false). Centres are computed edge by edge in f64, so
extreme coordinates no longer overflow.

* Paste: doc comments for Paste Into and the paste placement tests

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 18:35:21 -04:00
6e52641508 Auto-Align: a locked layer becomes the reference instead of being moved (#7080) (#7081)
* Auto-Align: a locked layer becomes the reference instead of being moved (#7080)

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

* Address review: Auto-Align moves the Background as a normal layer

With the Background selected and a Cylindrical or Spherical result (every
layer is reprojected), or another layer as the reference, the Background's
own position lock made Auto-Align refuse. Like Free Transform and Photoshop,
the Background's built-in position lock no longer pins it: it becomes a
normal layer ("Layer 0") when it has to move. Lock All, Lock Pixels and a
user's Lock Position on any other layer still keep it as the reference.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Address CodeRabbit review: the Auto-Align params doc names the locked layer as the default reference

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 18:35:08 -04:00
Perchenko AntonandBrandon Thomas f3941972df fix(actions): preserve scalar Selective Color settings in ATN export (#7681)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 18:34:54 -04:00
David Mann 9c5a7b907a Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/ui-egui/src/i18n/tr.tsv
#	docs/ui-parity.md
2026-10-24 00:34:11 +02:00
David MannandClaude Opus 5.5 f38004e721 Address CodeRabbit review: an opacity-only style preset added with ⇧ sets the opacity (#4049)
`style.presets.new` with `includeBlending: false` and an explicit `opacity` stores only that
opacity, but `apply` with `add: true` skipped the blending block unless the preset had a blend
mode, so the layer kept its opacity. Test: an_opacity_only_style_added_sets_just_the_opacity
(also checks the layer's blend mode and Fill stay).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 00:23:15 +02:00
David Mann 8c04b175ec Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/automation/src/rpc.rs
#	crates/automation/src/server.rs
#	crates/gpu/src/health.rs
#	crates/text/src/tests.rs
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/panels.rs
#	docs/control-protocol.md
#	docs/ui-parity.md
2026-10-24 00:23:15 +02:00
fc0b40dae5 fix(atn): keep Photoshop's other transform interpolation modes on import (#8698)
The .atn importer kept only bicubic, bilinear and nearest from a
transform's Intr, so a recorded Bicubic Automatic, Sharper or Smoother
Transform Selection replayed with the command's default (bilinear) and
re-exported without its choice. The bicubic variants are now kept (the
transforms already render them as Bicubic), Automatic becomes Bicubic
Automatic and Preserve Details Bicubic, and both transform commands list
the variants (#8697).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 17:55:54 -04:00
5750ed40f9 Fix pasted shape group bounds and effects across documents (#8992)
* fix(clipboard): render copied shapes in destination canvas space

When a copied shape group is placed beyond its source canvas, rebuild its vector caches against the destination canvas and pixel format. Previously a centered paste into a larger document could clip the caches to an empty source-sized frame, leaving no transform bounds and incorrect effects until a point edit refreshed each shape.

Restore missing shape caches and refresh source-clipped caches when canvas dimensions change. Cover Paste and Copy to Document, nested group contents, 8/16-bit destinations, rendered cache equality, transform bounds and movement Undo.

* fix(clipboard): measure shape placement before destination clipping

Restore missing source-space shape caches before measuring copy bounds, then adapt and translate for the destination. Re-render empty caches when shapes move back onto the canvas instead of shifting an empty raster.

Add focused integration coverage for 32 combinations of partial and full destination clipping, grouped shapes, missing caches, 8/16-bit output, centered and point placement, and Undo/Redo. Clippy and WASM checks pass.

---------

Co-authored-by: kotulsky <7805437+kotulsky@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 17:54:26 -04:00
JGF 35cb32be20 fix(export): report skipped layers in doc_export_layers (#8491) (#8713)
* fix(export): report skipped layers across automation surfaces

* test(export): use the MCP visible_only parameter spelling

* test(export): restore old adapter for hosted regression proof

* Revert "test(export): restore old adapter for hosted regression proof"

This reverts commit 397a5ffa2a.

* docs(export): qualify workspace-only response guarantees
2026-10-23 17:41:19 -04:00
Zuujis 4bda8d7388 test(panic_hunt): give the panic-counting self-test its own deadline (#8473)
contained_panics_count_and_graceful_errors_do_not used the 4 s hang
budget meant for fuzzed commands. With RUST_BACKTRACE=1 the panic hook
prints a backtrace, and on loaded Windows runners the first symbol
lookup took long enough (19.6 s) that run_watched returned None instead
of Some(false). The self-test now waits up to 120 s; the fuzz keeps 4 s.
2026-10-23 17:40:32 -04:00
NetterSilverandClaude Opus 5.5 03dd76022b Reject non-hex characters in engine hex color parser (#8541)
Found while testing crates/engine/src/commands.rs:283-291 (parse_hex)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:40:27 -04:00
Gabriel Luís da Silva 7bc6fd8a4f Write the Levels and Curves composite channel as Chnl.Cmps in .atn export (#8686)
Photoshop records the composite entry of a Levels or Curves Set as
Chnl/Chnl/Cmps and silently leaves the adjustment unchanged when it is
Chnl/Ordn/Trgt, which is what the export wrote. Import already reads both.

Fixes #8543
2026-10-23 17:40:11 -04:00
FinnNkandClaude Opus 5.5 159fe710a2 feat(guides): New Guide Layout takes lengths with units (#8719)
* feat(guides): New Guide Layout takes lengths with units

view.newGuideLayout took pixels only. Its widths, gutters and margins now
also take text with a unit as typed in Photoshop's dialog ("1 cm",
"0.25in", "12 pt", "3pc", "10%"), at the document's resolution; percent
is of the target's width for columns, gutters and left/right margins and
of its height for rows and top/bottom margins, per artboard. The dialog's
Width and Height fields already pass such text through, so they accept
units now too.

Part of #4984

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

* test(guides): New Guide Layout percent resolves per selected artboard

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

* test(guides): assert the full guide lists after a selected-artboard layout

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:40:06 -04:00
FinnNkandClaude Opus 5.5 0986582a61 fix(actions): a recorded Layers-panel drag keeps its drop target by name (#8781)
* fix(actions): a recorded Layers-panel drag keeps its drop target by name

Dragging a layer while recording stored layer.moveTo's target as a layer
id, which means nothing in the document the action later plays in, so
playback stopped with "no such layer". The target is now recorded by
name (with targetOccurrence among same-named layers), as layer.select
records its layer, and layer.moveTo accepts a target by name.

Fixes #7845

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

* fix(actions): count a drag's same-named targets without the moved layer

Steps are recorded after they run, so a target's occurrence among
same-named layers could shift when the moved layer shares its name. It
is now counted among the other layers only, at recording and at replay,
which the move doesn't reorder. Only a drag of the selected layer is
recorded by name; drags of several layers, or of an unselected one, keep
their ids.

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

* fix(actions): a moved group's contents don't count as drop targets

Same-named layers inside a moved group also changed order with the
move, so they are left out of the occurrence count with the group.

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

* fix(actions): leave every moved layer out of a named drop target

A move of several layers given a target by name no longer counts the
moved layers (or their contents) as candidates.

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

* fix(actions): a recorded ⌥-drag copy beside itself replays by name

A copy leaves its source in place, so the source stays a candidate for
the named target, at recording and at replay.

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

* fix(actions): a recorded copy's own contents don't count as drop targets

The step is recorded after the copy exists; the copy (and, for a group,
its same-named contents) is left out of the occurrence count, as at
replay it doesn't exist yet.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:39:45 -04:00
ZFzuofeng 13e4d8ceed fix(engine): select files correctly in Windows Explorer (#8518) 2026-10-23 17:39:08 -04:00
FinnNkandClaude Opus 5.5 51e45fd394 fix(engine): refuse partial Color Balance and Hue/Saturation ATN exports (#8701)
* fix(engine): refuse partial Color Balance and Hue/Saturation ATN exports

A `layer.setAdjustment` step changes only the settings it names, but the
Color Balance and Hue/Saturation descriptors written to the ATN carry
every setting and filled absent ones with defaults, so a partial edit
replayed as one that also reset the rest. Strict export now refuses such
a step (with skipUnsupported it is listed in Skipped); complete ones
export and round-trip as before.

Fixes #8531

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

* fix(engine): refuse Hue/Saturation ranges missing their own settings

A colour range written without its own hue, saturation and lightness
would replay with the missing ones reset to 0, so strict export refuses
it like a partial master setting.

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

* fix(engine): refuse wrong-shaped Color Balance and Hue/Saturation settings

Strict export now checks the triplets, numbers and flags it needs are of
the shape the descriptor writer reads, since it would write anything else
as 0.

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

* fix(engine): refuse malformed Hue/Saturation range bounds

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

* test(engine): cover malformed and valid Hue/Saturation range bounds

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

* fix(engine): refuse fractional Hue/Saturation range bounds

The descriptor stores them as integers, so a fractional bound would be
rounded on export.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:38:48 -04:00
FinnNkandClaude Opus 5.5 272554f971 fix(type): type.edit with new text keeps the styles of unchanged text (#8766)
* fix(type): type.edit with new text keeps the styles of unchanged text

`type.edit {"text"}` replaced the whole string, so every character took
the first run's style and other styled ranges were lost. It now replaces
only the span that differs (after the common prefix and suffix), so the
unchanged text keeps its character and paragraph styles and an added
character takes the style beside it, as the params doc promised.

Fixes #8489

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

* fix(type): text inserted at a paragraph's start joins that paragraph

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:38:36 -04:00
David Mann 27caf2823b Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/engine/Cargo.toml
2026-10-23 23:13:46 +02:00
Dylan M. Taylor 6d4b9690ab deps: roxmltree 0.21 in photocraft-io and photocraft-engine (#8973) 2026-10-23 17:06:29 -04:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
aiongg cf0960b520 Merge pull request #8915 from aiongg/ci-speed-2
CI: no debug info for dependencies, nextest in xtask ci, long sweeps spread over days
2026-10-23 13:48:36 -04:00
Dylan M. TaylorandCodebuff f27656b910 fix: restrict droplet shim permissions and annotate test range (SonarCloud) (#8870)
The generated droplet shim only needs to run as its creator, so 0700 keeps
other local accounts from reading the CLI path it invokes. The reversed
range in the arrow-key test is the case under test, not an accident.

🤖 Generated with Codebuff

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-23 09:57:10 -04:00
Khawar Zamman WaniandClaude Opus 5.5 f58e2f4275 fix(algo,engine): refuse a Select and Mask refine too large to allocate
refine_mask_with builds its result densely over the selection's bounds
(grown by the spread, plus Refine Edge Brush additions). After a thin
edit across a huge canvas, or with a whole-canvas selection there, that
is billions of pixels. matting::refine_area names that area and
select.refineEdge refuses one over MAX_REFINE_AREA (2^30 px) with an
error before allocating, leaving the selection unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FszacAJDjznVimaxQktoJT
2026-10-23 14:41:20 +05:30
Khawar Zamman WaniandClaude Opus 5.5 56bbe6b80b fix(algo,engine): paint Select and Mask Brush, Lasso and Refine Edge Brush tile by tile
The Brush edit, the Lasso edit and EdgeBrush::new allocated a dense buffer
over their bounding box, which the swept-area cap does not bound: a 1 px
diagonal across a 100,000 px canvas sweeps ~141k px but asked for ~40 GB.

- matting::brush_surface (replaces brush_region) and EdgeBrush paint only
  the tiles a stroke touches (painted_tiles), capped at MAX_PAINTED_TILES.
- selection::polygon_surface rasterises only the tiles the Lasso's edges
  cross, byte-identical to selection::polygon; interior tiles share one.
- selection::combine_sparse adds/subtracts visiting only the new shape's
  tiles, keeping the rest of the selection shared.
- select.refineEdge refuses edits and refineStrokes that would cross too
  many tiles before painting anything, leaving the selection unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FszacAJDjznVimaxQktoJT
2026-10-23 14:22:33 +05:30
aiongg 18f4b5348c Tests: spread three format-by-geometry matrices over the days, and build one precheck state per case
The tilt-shift reference tests and tiled_clear_and_fill_match_one_pass_over_the_area cross pixel
formats or channel counts with independent geometry and fill settings. Each run takes a quarter of
the combinations through Sweep, a different quarter each UTC day.

precheck_agrees_with_execute built all five document states for every call and kept one; it now
builds only the state the call needs.
2026-10-23 08:30:48 +00:00
Khawar Zamman WaniandClaude Opus 5.5 2d894eab2b fix(engine): tile-wise Invert edit; a run of Inverts costs one
Review of #8783:

- The Invert edit no longer builds a canvas-sized buffer: the canvas
  takes the inverse of the selection's untouched value (one shared tile
  for every fully covered tile, as fill_rect stores it), then only the
  tiles the selection holds are inverted. Byte-for-byte equal to the
  dense inversion; a 100k x 100k canvas inverts in well under a second.
- A run of Inverts is applied as one or none (pairs cancel exactly), so
  4096 of them cost at most one pass between other edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:42:26 +05:30
Khawar Zamman WaniandClaude Opus 5.5 ca13e355c9 fix(algo,engine): bound brush stroke work and total brush edit area
Review of #8783:

- matting::brush `segments` no longer collects its pieces: they are made
  as they are painted, and each segment is first cut to the painted area
  grown by the brush. A 1 px stroke zigzagging across 2 million px
  (within MAX_SWEPT_AREA) split into ~134 million pieces collected up
  front (~2 GB) in brush_region and EdgeBrush::new; now ~5000 pieces.
- select.refineEdge `edits`: the Brush edits' swept area in all is
  bounded by MAX_SWEPT_AREA, as refineStrokes' is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:14:50 +05:30
Khawar Zamman WaniandClaude Opus 5.5 4970534e05 feat(engine): Select and Mask tool edits as select.refineEdge params
select.refineEdge takes `edits`: the Select and Mask workspace's Quick
Selection, Brush, Lasso, Object Selection, Select Subject, Clear Selection
and Invert, applied in order to the starting selection (or layer mask)
before the edge is refined. Cancel discards them, OK is one history step,
and agents drive them over MCP and the control channel.

- engine/select_mask_edits.rs: parse (bounded, every bad field an Err)
  and a pure apply/apply_one the workspace preview can share
- algo matting::brush_region: soft round brush coverage (hardness)
- ATN export reports steps with edits instead of dropping them
- "Edits" label in the 21 catalogs

Part of #7069 and #8148 (the workspace UI follows in a second PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:30:46 +05:30
David Mann 1efa06b715 Integration: merge main (c36e46c38 #8680, #8668, #6705)
# Conflicts:
#	docs/ui-parity.md
2026-10-22 23:47:54 +02:00
David MannandClaude Opus 5.5 07bb7c2522 Integration: enable Generator in the Image Assets artboard tests (#8254)
#5598 made File > Generate > Image Assets require Preferences > Plug-ins >
Generator, so #8254's two artboard tests got Disabled. Enable it first,
as #5598's own test does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:40:21 +02:00
David Mann adace9a86f Integration: #8254 second fix-up (an empty artboard asset is refused, as in Photoshop's Generator) 2026-10-22 23:40:20 +02:00
David Mann 2bfc24d9f8 Merge PR #8180: fix(actions): strict .atn export keeps Apply Image's flat source fields (#7197) 2026-10-22 23:40:20 +02:00
David Mann 7c9d6ac344 Merge PR #8150: fix(automation): doc.replaceSmartObject replaces Smart Object contents from the read root (#8061) 2026-10-22 23:40:19 +02:00
David Mann 18124238f7 Merge PR #8502: fix(actions): accept Photoshop stringID blend modes (#8454) 2026-10-22 23:40:19 +02:00
David Mann c436f61d53 Merge PR #7202: fix(atn): import a recorded Save As with its destination (#7102) 2026-10-22 23:40:18 +02:00
David Mann ee85da2ffd Merge PR #8527: fix(atn): export Black & White adjustment layers with Photoshop's BanW class (#8481) 2026-10-22 23:40:17 +02:00
David Mann 9ee6c407b6 Merge PR #8254: Image Assets: optionally keep a transparent artboard's full size 2026-10-22 23:40:16 +02:00
David Mann a23c29b53a Merge PR #5878: Preferences: old settings files take Shift-to-switch-tools on (#5647) 2026-10-22 23:40:16 +02:00
David Mann 24051914fd Merge PR #6352: fix(engine): honor lock all on layer mask commands 2026-10-22 23:40:15 +02:00
David Mann 5301a128b8 Merge PR #8509: fix(atn): preserve unsupported event kinds 2026-10-22 23:40:13 +02:00
David Mann 7be48940b3 Merge PR #8518: fix(engine): select files correctly in Windows Explorer 2026-10-22 23:40:13 +02:00
David Mann 6e4d89d13d Merge PR #8473: test(panic_hunt): give the panic-counting self-test its own deadline 2026-10-22 23:40:12 +02:00
8a2d35fcd2 Integration: an unchanged dialog keeps a baked overlay's exact ends (#7335)
After merging main, the Layer Style dialog always sends From/To as 8-bit
hex (#7072). On a baked Gradient Overlay without a source record (PSD or
.asl import) the custom and no-record paths wrote those hex ends back,
so OK without a change rounded the curve's ends (a Smooth bake's white
end 0.99999994 became 1.0, Lab ends became RGB). Like edit_endpoint for
the recorded source, a hex end that only repeats the current end is now
left out. baked_overlay_is_unchanged_until_method_selection_then_edits_
the_whole_curve fails without this.

Co-authored-by: oRE-o <42003350+oRE-o@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:40:12 +02:00
David Mann fff01d8918 Integration: stack #7335 (its merge onto main, conflicts resolved) 2026-10-22 23:40:11 +02:00