Commit Graph
3466 Commits
Author SHA1 Message Date
Tanish Manku 3cd0f6c954 Keyboard Shortcuts: Accept and Go to Conflict / Undo Changes, and reserved keys (K4a) (#5349)
* Keyboard Shortcuts: Accept and Go to Conflict / Undo Changes, and reserved keys (K4a)

A key that another command holds now offers Photoshop's two answers under the warning.
Accept and Go to Conflict takes the key from the other command at once (its main key or
an alternate) and selects it for a new one. Undo Changes puts the edited command's main and
further keys back as they were. OK without either still accepts, as in Photoshop.

Keys the system takes before the app sees them are refused with a message: Alt+Tab,
Alt+F4, Ctrl+Alt+Delete, Ctrl+Esc and Ctrl+Shift+Esc off the Mac; Cmd+Tab, Cmd+` and
Cmd+Opt+Esc on it.

* Keyboard Shortcuts: a conflict goes stale when the bindings change after it (Reset All, Use Default, Delete, ×, Escape, a set switch); Accept takes the key from every holder; Linux reserves its own keys; doc comment back on shortcut_text
2026-10-15 14:18:14 -07:00
Tanish Manku 04db697b5d Paste Layer Style: a group's Pass Through pastes onto other layers as Normal; style clipboard comment (#6310) 2026-10-15 14:17:22 -07:00
David MannandClaude Opus 5.5 96bff5d7b6 PSD: Smart Sharpen and Dust & Scratches smart filters open and save like Photoshop (#6300)
Smart Sharpen (filter.sharpen.smartSharpen) and Dust & Scratches
(filter.noise.dustAndScratches) smart filters are now mapped both ways in
the PSD filterFX list instead of being dropped on save and opening as
uneditable entries.

- Smart Sharpen: filterID 1131 (numeric), stringID class smartSharpen;
  useLegacy bool, Amnt UntF #Prc, Rds UntF #Pxl, noiseReduction UntF #Prc,
  blur blurType enum. PhotoCraft removes a Gaussian blur, so it writes
  blur GsnB with useLegacy false. Entries with Lens Blur (Photoshop's
  default), Motion Blur (with Angl), legacy mode, any other key, wrong
  types/units or out-of-range values stay verbatim. presetKind
  (presetKindType) is accepted on open but not kept or written: the
  engine takes no such parameter, and Photoshop leaves it out of entries
  with explicit values.
- Dust & Scratches: filterID / class DstS; Rds and Thsh long (radius
  1..500, threshold 0..255; PhotoCraft params are rounded on write).

Measured in Photoshop: applied through Action Manager to a smart object,
saved, filterFX read back (Smart Sharpen default / Gaussian / Motion,
Dust & Scratches 1,0 and 4,20). Tests cover the measured shapes, option
round trips, malformed descriptors kept verbatim and hostile params.
Scorecard FILE-215-2 note and file-format parity doc updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 22:54:43 +02:00
David MannandClaude Opus 5.5 a99ad58591 PSD: Diffuse, Trace Contour, Crystallize and Pointillize smart filters open and save like Photoshop (#6302)
smart_map.rs maps four more filters both ways in filterFX:
- Diffuse: filterID `Dfs `, Fltr `Dfs ` { Md = DfsM enum Nrml / DrkO / LghO /
  anisotropic (long id) } <-> filter.stylize.diffuse `mode`.
- Trace Contour: `TrcC` { Lvl long 0-255, Edg = CntE enum Upr / Lwr }
  <-> filter.stylize.traceContour `level`, `edge`.
- Crystallize: `Crst` { ClSz long 3-300 } <-> filter.pixelate.crystallize.
- Pointillize: `Pntl` { ClSz long 3-300 } plus the entry's BckC as the
  `background` colour <-> filter.pixelate.pointillize.

Measured in Photoshop: applied through Action Manager to a smart object,
saved, filterFX read back. Our random `seed` has no PSD key and is not
written. Missing keys, wrong value types, out-of-range numbers and unknown
enum values keep the entry verbatim; values with no PSD equivalent on save
are reported instead of guessed.

Scorecard FILE-215-2 note and docs/file-format-parity.md updated (stays
partial).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 22:48:16 +02:00
David MannandClaude Opus 5.5 7d36b417e5 PSD: parameterless smart filters (Blur, Sharpen, Find Edges, Solarize…) open and save like Photoshop (#6296)
Blur, Blur More, Average, Despeckle, Find Edges, Facet, Fragment, Sharpen,
Sharpen More, Sharpen Edges and Solarize now map both ways in a smart
object's filterFX. Photoshop stores these filters with hasoptions false, no
Fltr descriptor and only the four-char filterID as a long (Blr , BlrM, Avrg,
Dspc, FndE, Fct , Frgm, Shrp, ShrM, ShrE, Slrz). PSD saves write exactly that
item (Nm, blendOptions, enab, hasoptions, FrgC, BckC, filterID) instead of
dropping the filter with a warning, and Photoshop's entries open as the
editable PhotoCraft filter instead of a verbatim pass-through. An entry that
carries a Fltr with settings, or an unknown or mistyped filterID, is still
kept verbatim.

Measured in Photoshop: applied through Action Manager to a smart object,
saved, filterFX read back.

Scorecard FILE-215-2 note and docs/file-format-parity.md updated (25 filters
mapped).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 22:46:47 +02:00
David MannandClaude Opus 5.5 ef4e921408 PSD: Surface Blur, Smart Blur and Radial Blur smart filters open and save like Photoshop (#6298)
Map three more smart filters both ways in filterFX (smart_map.rs):

- Surface Blur: filterID 1135 (numeric), Fltr class `surfaceBlur`,
  `Rds ` UnitFloat #Pxl -> radius, `Thsh` long -> threshold.
- Smart Blur: filterID `SmrB`, `Rds ` / `Thsh` doubles, `SmBQ` enum
  (SBQL/SBQM/SBQH -> low/medium/high), `SmBM` enum
  (SBMN/SBME/SBMO -> normal/edgeOnly/overlayEdge).
- Radial Blur: filterID `RdlB`, `Amnt` long, `BlrM` enum (Spn/Zm ->
  spin/zoom), `BlrQ` enum (Drft/Gd/Bst -> draft/good/best).

Measured in Photoshop: applied through Action Manager to a smart object,
saved, filterFX read back. Every measured Fltr descriptor is written back
byte for byte (checked against the measured files locally; they are not
committed). Entries with missing keys, wrong types, out-of-range or NaN
values, unknown enum values or keys we don't model stay verbatim, as does
Radial Blur at its defaults (no Fltr stored). An off-centre Radial Blur
centre and unknown enum names are reported on save, not guessed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 22:46:42 +02:00
David MannandClaude Opus 5.5 d089908158 PSD: Offset and Custom smart filters open and save like Photoshop (#6304)
Offset (filter.other.offset) and Custom (filter.other.custom) applied as
smart filters were left out of PSD saves with a warning, and the same
filters in a Photoshop PSD opened only as verbatim, non-editable entries.
smart_map.rs now maps both ways in filterFX:

- Offset: filterID/class Ofst; Hrzn and Vrtc as long (signed), Fl  as the
  FlMd enum: Wrp  = wrap, Rpt  = repeat, Bckg = transparent ("Set to
  Transparent" on a layer). Unknown enum values, wrong types or missing
  keys keep the entry verbatim.
- Custom: filterID/class Cstm; Scl  and Ofst as long, Mtrx as 25 longs,
  row-major 5 x 5. Written as the engine renders it (no kernel = identity,
  missing cells 0, values clamped to -999..999, 1..9999, -9999..9999);
  fractional values have no PSD equivalent and are reported. On open,
  values outside Photoshop's ranges or a matrix that is not 25 longs keep
  the entry verbatim.

Measured in Photoshop: applied through Action Manager to a smart object,
saved, filterFX read back (Offset Wrap 0/0, Set to Background 12/-7,
Repeat 5/3; Custom default sharpen and a blur at Scale 2, Offset 10).
The measured files open as the editable filters and survive export and
re-import unchanged (checked locally, not committed).

Scorecard FILE-215-2 note and the file-format parity doc list the two
filters (still partial).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 22:45:46 +02:00
vangel76andClaude Opus 5.5 49438e71d2 Merge upstream/main into fix/5645-type-edit-keeps-subscript
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 22:36:12 +02:00
David Mann c84886adc2 WIP: PSD: Offset and Custom smart filters open and save like Photoshop (#6304) 2026-10-15 22:22:00 +02:00
David Mann 2058f2e180 Merge remote-tracking branch 'origin/main' into feat/ui-217-4-animated-zoom 2026-10-15 22:12:00 +02:00
David MannandClaude Opus 5.5 7f0e7cbee0 Zoom tool: holding the button zooms continuously with Animated Zoom on (#6210)
With Preferences > General > Animated Zoom on (the default), a Zoom tool
press held still past 0.3 s zooms continuously about the press point
(2 doublings a second, frame-rate independent, within the zoom_levels
range) until release; Alt zooms out. The release is then no click step.
A quick click still steps, Scrubby Zoom and the zoom rectangle are
unchanged, and with the preference off a hold is one click step.

The hold logic lives in the new zoom_hold.rs; canvas.rs only calls it.
general.animatedZoom leaves HIDDEN_UNTIL_IMPLEMENTED; prefs that do
nothing 25 of 166.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 22:11:45 +02:00
Tanish Manku c11ef015b7 Paste Layer Style carries Blend If and channels; align to an active selection with several layers (#5881)
* Paste Layer Style carries Blend If and channels; align to an active selection with several layers

From the layers audit (L2):
- Copy / Paste Layer Style copied the effects, blend mode, Fill and advanced blending but
  dropped Blend If and the channel restrictions; Photoshop's layer style carries all of its
  Blending Options. Across colour modes only Blend If's Gray range carries over and the
  channels reset (RGB channel ranges mean nothing in CMYK). The copied style is now a named
  struct (CopiedStyle) instead of a tuple.
- With an active selection, aligning (Layer › Align, "auto") aligned two or more layers to
  their own bounds; Photoshop aligns them to the selection (Layer › Align Layers to
  Selection), as it already did for one layer.

* Paste Layer Style keeps Blend If normalised across modes; Align docs say it follows an active selection for any count; undo, group and 16-bit tests
2026-10-15 13:10:28 -07:00
Tanish Manku 77a258b9a0 Clip groups: Release Clipping Mask takes the layers above; merges keep clipping; Delete Hidden settles (#5868)
* Clip groups: Release Clipping Mask takes the layers above; merges keep clipping; Delete Hidden settles

From the layers audit (L2, silently wrong results):
- Release Clipping Mask on a clipped layer also releases the clipped layers above it, as
  Adobe documents ("removes the selected layer and any layers above it from the clipping
  mask"). Before, they stayed clipped to the released layer.
- Merge Down of two clipped layers, and Merge Layers of clipped layers of one chain, give a
  layer still clipped to their base (it was always unclipped). Merge Down's result also keeps
  the lower layer's colour label and link; Merge Layers' keeps the top layer's label.
- Delete Hidden Layers releases the layers clipped to a deleted hidden base (clip_group rule
  3, as single Delete already does), instead of leaving them clipped to the layer below.

* Merge Layers keeps the result clipped only when the clipped layers share one clip chain

* fmt
2026-10-15 13:10:22 -07:00
Muhammad Saad a4cdc63d88 Retouch: admit the rendered stroke extent, not an estimate (#4961, #6110)
Review of f116c32 was right that the guard did not establish the bound it
advertised. reject_bounds ran on the value stroke_coverage() returned, and that
function calls dense_coverage() internally, so the dense Vec was already
allocated by the time it was checked. reject_huge_stroke, the only check that
ran first, estimated from the raw points and half the tip, which brush dynamics
exceed: Scattering offsets a dab centre by its amount times the tip size.

Split rendering from dense extraction. stroke_coverage_within renders the stroke
into the renderer's tiles, which hold only what the dabs touch, reads the extent
once every dab is placed and refuses before allocating the width x height
buffer. retouch_cmds::coverage wraps it at MAX_STROKE_PIXELS and every stroke
command goes through it, so the bound now holds whatever the dynamics do.
reject_bounds is removed; it could not do the job it was named for.

spot_heal_surface returned a bare Rect and so had nowhere to refuse; it returns
Result<Rect> now. reject_huge_stroke stays as the cheap early-out and pads by
the scatter reach and the dual-brush radius so it no longer falls short.

The allowance is unchanged, so no stroke that worked before starts failing.

Tests: coverage_within takes the budget as a parameter, and a scattering brush
proves the rendered extent exceeds what the points alone suggest, is refused one
pixel under it and fits at it. Every command's refusal now asserts the pixels
and the history step count are untouched, and the ordinary stroke uses a valid
fixture and must succeed rather than merely avoid the size error.
2026-10-15 23:50:55 +04:00
e4b2868a02 Check deep EXR tile count tables against max_alloc (#5632)
* Check deep EXR tile count tables against max_alloc

A deep tile's sample-count table was decompressed at the header's full tile size with no
budget check, so a 20 KB file could build a 4 MiB table and a scratch copy under a 1 MiB
max_alloc. Try that size only when max_alloc has room for both; otherwise read the clipped size.

Fixes #4958.

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

* Keep deep EXR decompression within the size it was admitted at

The count-table check admits the table and its scratch copy, 2x the table, but Vec doubling
gave a 4 MiB ZIPS table 8 MiB of capacity (a 3 MiB RLE table 4 MiB), so an admitted table
and its copy could take 3x. Grow the decompressed buffer by doubling capped at the expected
size, inflating straight into it, and allocate it and the scratch copy fallibly.

Tests: decompressed buffers never reserve past the expected size; a near-maximal tile with a
clipped table decodes under every budget; per-tile admission; half and uint channels after
full-size tables; the 8 MiB boundary for uncompressed tables too.

Refs #4958.

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

* Tests: show deep tile tables are admitted one tile at a time

The 17x17 image in 16x16 tiles had an interior tile, which never reaches the max_alloc check,
so only three edge tables (6 KiB) were checked against the 8000-byte budget and a check that
summed admissions across tiles still passed. Use a 48x1 image instead: its three tiles are all
edge tiles, and under 4096 bytes a summed check fails at the third tile.

Refs #4958.

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

* Complete deep EXR count-table allocation admission

Account for scratch space on clipped and interior compressed tables while preserving raw-table budgets. Borrow packed input before admission and make initial RLE reservations and raw copies fallible.

Add single-channel admission boundaries, raw fallback controls, and hostile-size borrowed-read regressions.

* ci: run deep EXR regressions independently

* Drop the separate deep EXR CI job

It was added so these tests could run while main's workspace suite was
failing. Main is green again, and the regular test jobs run them.

This reverts commit 5aa4869b45.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: jonathnwxh <258340642+jonathanwxh-cell@users.noreply.github.com>
2026-10-15 13:40:50 -06:00
Michael HigginsandClaude Opus 5.5 64520f645e Control channel: connections must authenticate within 10 s, however active (#4965) (#5229)
The only limit on an unauthenticated control connection was the 30 s
idle timeout, which starts again with every read. A local client that
kept sending bytes or blank lines without ever completing `auth` kept
its connection, and one of the 16 connection permits, for as long as it
liked; sixteen of them locked every other client out.

AuthDeadlineStream wraps the read half of a connection: until the
connection authenticates, reads fail once AUTH_DEADLINE (10 s) has
passed since it connected, and a blocking read waits no longer than
what is left. Both transports use it (the desktop app's control server
and the headless `photocraft-cli serve`). After `auth` succeeds only
the idle timeout applies, as before.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:39:19 -06:00
Michael HigginsandClaude Opus 5.5 b24c0360f1 Dependencies: require ruzstd 0.9.1 for RUSTSEC-2026-0344 (#4952) (#5225)
ruzstd before 0.9.1 lets a crafted zstd input under 128 KB allocate
several gigabytes at once. PhotoCraft decodes zstd when it opens .pcraft
and Affinity files, so such a file could end the process with an
out-of-memory abort.

Raise the minimum in both crates that use it and update the lockfile
from 0.9.0 to 0.9.1.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:37:56 -06:00
David Mann 22f1d694c9 Merge remote-tracking branch 'origin/main' into feat/ui-217-4-animated-zoom 2026-10-15 21:35:40 +02:00
David MannandClaude Opus 5.5 e8f50f69c3 scorecard: TOOL-214-4 note and the pasteboard menu row catch up with main
Dragging layers onto another document's tab landed in #851 (#589), and the
pasteboard colour menu in #2633 (#2462); the TOOL-214-4 note and the
context-menu parity row still listed both as open.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 21:17:33 +02:00
David Mann 0aed96c7fd Merge remote-tracking branch 'origin/main' into docs/scorecard-stale-rows-2
# Conflicts:
#	docs/ui-parity.md
2026-10-15 21:17:33 +02:00
Khawar Zamman WaniandClaude Opus 5.5 1b666f4790 Pen: Auto Add/Delete on the path being drawn (#6140) (#6172)
While a path was being drawn, the Pen's Auto Add/Delete was off, so adding
a point meant finishing the path and switching to Add Anchor Point. Now a
click on a segment of the path in progress adds an anchor, a click on a
middle anchor deletes it, and drawing carries on from the last anchor. The
first anchor still closes the path and the last still breaks its handle.

The hover highlight draws over the path in progress, and the rubber band
hides where a click adds or deletes instead of placing an anchor.


Claude-Session: https://claude.ai/code/session_01JFTxZ57wCmWEDXxCn1Zuni

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:12:35 -06:00
ZuujisandClaude Opus 5.5 99083fca4c Quick Mask: ⌘I inverts the whole mask, unstored black included (#6095) (#6108)
* Quick Mask: ⌘I inverts the whole mask, unstored black included (#6095)

Image › Adjustments on an alpha channel or the Quick Mask ran on the surface's
stored content only. The black outside a selection-made Quick Mask (or a new
black alpha channel) is the surface default, never stored, so Invert left it
black: the whole mask turned red and leaving Quick Mask selected nothing.
Channel targets now go through `pixels::adjust_mask`, as layer masks do, which
adjusts the default pixel too.

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

* Quick Mask: test ⌘I with Color Indicates Selected Areas too (#6095)

A rectangle comes back as everything but the rectangle, and with nothing
selected ⌘I selects everything. Both failed on main.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:11:42 -06:00
artman67andClaude Opus 5.5 c4de02978b Menu bar: releasing Alt after Alt+scroll no longer focuses the bar, so Space can't run File > New
Releasing Alt counted as a tap (keyboard focus to the menu bar, File lit) unless a key, text or
pointer-button press came in between. A mouse-wheel event was not on that list, so zooming with
Alt+wheel and letting go of Alt focused the bar; Space then opened File with New... lit, and a held
Space's key repeat ran it. A wheel event, or the window losing focus while Alt is down (Alt went
to a desktop shortcut), now cancels the tap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:14:15 -04:00
skylartaylor d68a1cbc2d ci: preserve separate nextest and doctest results 2026-10-15 19:56:43 +02:00
skylartaylor bcf2cefd4b ci: preserve nextest failures on Windows 2026-10-15 19:56:43 +02:00
jonathnwxh 0caa7ed54a Merge upstream CI ordering while retaining clipboard regression jobs 2026-10-16 01:53:19 +08:00
skylartaylor 4f6e617555 ci: retain the font filter with integrated nextest checks 2026-10-15 19:44:21 +02:00
skylartaylor 40a7c20430 ci: integrate nextest with early checks and runtime limits 2026-10-15 19:44:21 +02:00
skylar taylor-barrick 5f9190f8cc ci: run cheap checks before tests (#6124)
Run the existing Clippy, dependency layering and scorecard checks before the test passes so failures are reported earlier.

Preserve the existing commands, test coverage and runtime limits. Linux, Windows, corpus, WASM, web and packaging checks passed.
2026-10-15 11:42:53 -06:00
skylar taylor-barrick d2fd1a012a ci: add comfortable runtime limits to unbounded jobs (#5931)
Add explicit runtime limits to eight previously unbounded CI, documentation and packaging jobs, preserving existing limits and quality gates.

Validated the timeout-only diff and completed Linux, Windows, corpus, WASM, web, documentation and packaging checks.
2026-10-15 11:26:52 -06:00
midasmatterhorn c06eca9b68 fix(psd): preserve PSB foreign-order block lengths
Choose PSB block length width from the canonical big-endian key before converting the key for little-endian TIFF output. Preserve foreign payloads, padding and conversion of following blocks.

Addresses #5599, finding 2 only.

Fixes #6104.
2026-10-15 11:23:57 -06:00
David MannandClaude Opus 5.5 a1837706ee Performance: a cancelled Gaussian Blur job stops within budget (#6205)
Cancelling a 24 MP Gaussian Blur r 50 job took up to ~400 ms (P25, budget
200 ms max). Stage timings showed two stretches without a cancel check:

- The transparency-lock pass (keep_alpha) that puts a Background layer's
  alpha back after the filter ran serially over every changed tile, about
  400-450 ms on 24 MP, with no cancel check. It now fixes the tiles in
  parallel, each in its own scratch surface, and checks for cancellation
  before each tile. Results are bit-identical to the serial pass (tested
  against it at 8, 16 and 32-bit).
- Box-blur column strips checked only before each strip; with ~31 strips
  per band and 32 threads one strip is tens of ms. line_passes now checks
  between passes.

P25 max: 337-399 ms -> 13-44 ms over five runs. The uncancelled job is
also faster: p50 about 690 -> 400 ms.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 19:17:07 +02:00
Nigel Ihza Firdausy 9a25a8a390 style(svg): format expanded conversion state initializer 2026-10-16 00:14:38 +07:00
Nigel Ihza Firdausy 159cd42ae9 fix(svg): honor U8 storage budget and avoid charging rejected layers
Preflight oversized pixmaps and conversion peak before allocating.
Only charge the U8 retained-surface allowance after render succeeds.
Clarify the warning and cover later small raster groups with regressions.
Merge upstream main to refresh the pull request branch.
2026-10-16 00:13:40 +07:00
David MannandClaude Opus 5.5 6f1984d126 Tools: each painting tool's brush persists across launches; restored tool options are clamped (#6212)
Tool options and the selected tool were already remembered (tool_memory, #2814), but
each painting tool's own brush (UiState.tool_brushes) was lost at quit, and the tool that
owned the session brush restarted as the Brush, so after quitting with the Eraser its
brush became the Brush's.

- Preferences.toolBrushes {owner, brushes}: saved change-driven when the pointer is up and
  on exit, restored at launch with stored tips as references (loaded on swap).
- brush_cmds::remembered_settings / restored_settings: shared with lastBrush; restored
  brushes pass the tools.setBrush checks and are clamped (size, hardness, opacity, flow,
  smoothing).
- tool_memory::sanitize clamps restored tool options to their options-bar ranges.
- Reset Tool / Reset All Tools clear the persisted state through the same path.
- Scorecard UI-217-1 note updated (stays partial: font recents).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 18:59:22 +02:00
David MannandClaude Opus 5.5 a3cbc880e6 Preferences: Save in Background off saves in the foreground (#6207)
Preferences > File Handling > Save in Background was never read. Saves
already run as a background job; the setting now picks how the app
waits for it, as in Photoshop:

- On (default): you keep working while the file is written; progress
  shows in the document tab and the status bar. The modal progress
  dialog no longer appears for these saves.
- Off: the same job, worker and atomic write, but the modal progress
  dialog shows at once and no shortcut reaches the app until the file
  is complete; a close or quit asked for meanwhile waits for it.

The setting now shows in Edit > Preferences. Prefs audit: settings that
do nothing 26 -> 25 of 166.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 18:48:58 +02:00
David Mann 2a31c28c80 WIP: Zoom tool: hold to zoom continuously with Animated Zoom (#6210) 2026-10-15 18:12:32 +02:00
jonathnwxh ce0ba03572 Use the actual pixel grid Show preference in regression 2026-10-15 23:56:30 +08:00
jonathnwxh 173e7fd6bd Test successful Object Selection restoring hidden Extras 2026-10-15 23:51:18 +08:00
jonathnwxh 2cea742a00 Cover remaining interactive selection routes and preserve no-op hiding 2026-10-15 23:50:21 +08:00
jonathnwxh 6fc1004b50 Cover Extras restoration through actual layer thumbnail clicks 2026-10-15 23:47:34 +08:00
jonathnwxh 6bef23a715 Reveal Extras when interactive selection commands succeed 2026-10-15 23:45:34 +08:00
ersoynarin-code 7d42b5c838 fix(perf): validate regression minimum threshold 2026-10-15 18:38:34 +03:00
jonathnwxh 67a2938cab Guard intentional hidden selections against failures and unrelated edits 2026-10-15 23:32:18 +08:00
jonathnwxh 0a608e63db Cover marquee pointer gesture and subsequent explicit hide 2026-10-15 23:31:23 +08:00
jonathnwxh 14ceeb927e Reproduce new selections remaining hidden after Extras is disabled 2026-10-15 23:27:15 +08:00
David MannandClaude Opus 5.5 24a77a18c0 Shapes: a soft shape's transform frame follows its path (#5846)
A shape below 100 % Density renders over the whole canvas, and a Feather
grows its pixels, so Free Transform, the Move tool's transform controls,
snapping and Properties > Transform framed the canvas or the blur. As in
Photoshop the frame now follows the path and stroke whatever the mask
Density and Feather: photocraft_compose::hard_shape_bounds (shape_bounds
without the soft reach) feeds whole_layer_bounds and the Properties
Transform fields. Rendering keeps the soft bounds; other layers and hard
shapes are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:08:05 +02:00
David MannandClaude Opus 5.5 442987528f Merge main into fix/4017-huge-new-document
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 16:52:49 +02:00
David MannandClaude Opus 5.5 eeb955649b Merge main into perf/p33-cmyk-dab
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 16:52:46 +02:00
David MannandClaude Opus 5.5 275576543f Merge main into feat/tool-213-7-live-retouch
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 16:38:01 +02:00