Commit Graph
3466 Commits
Author SHA1 Message Date
David MannandClaude Opus 5.5 e4c41e147e Image Size: Resolution with Resample on keeps the print size in physical units (#6979)
With Resample on and the unit in inches/cm/mm/pt/picas, a new Resolution
only stored the resolution; width/height are kept in pixels, so the print
size shrank (720 x 576 px at 72 ppi, 10 x 8 in, set to 300 ppi stayed
720 x 576 px = 2.4 x 1.92 in). Photoshop keeps the print size and
resamples: 3000 x 2400 px. Scale the stored pixels by new/old resolution
in physical units; pixels and percent keep the pixel count as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:38:50 +02:00
NetterSilver 158dcc6731 Fix engine compile error: load export presets from presets JSON
Fixes #6911

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit bb7a5f2309)
2026-10-17 13:34:00 +03:00
PerchAnton 120cfe19a2 Merge origin/main into fix/finite-command-parameters
Resolve Color to Alpha validation conflict while preserving the upstream array guards and finite color parsing.
2026-10-17 13:34:00 +03:00
Finn NewickandClaude Opus 5.5 dd57638d9e fix(psd): make packbits::decode_into crate-private and overflow-safe
decode_into computed out.len() + expected unchecked. With a non-empty
buffer and an expected length near usize::MAX it panicked in debug builds
and, in release, wrapped and returned Ok having decoded nothing. Compute
the target with checked_add and report LimitExceeded, and test each packet
as len > target - out.len() so the bound checks cannot overflow either.

The only callers are decode and decode_rle in this module, and the crate
README documents packbits::{encode, decode} as the public API, so
decode_into becomes pub(crate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 10:11:57 +01:00
itsF4LCON 85aa10b16d Merge remote-tracking branch 'upstream/main' into fix/6085-text-caret-contrast 2026-10-17 11:01:10 +02:00
dotCooCoo 8f971cc79f Mixer Brush: Clean Brush empties the brush, and the automatic Load and Clean can be switched off
The Mixer Brush could not be emptied. MixerState::clean dropped only
the pickup well, paint.mixerBrush refilled the reservoir from the
foreground before every stroke unless loadAfterStroke was false, and
the app always sent the defaults. A stroke on a new layer over an image
always carried the foreground color, and the options bar had no Load
Brush or Clean Brush control.

Cleaning now empties both wells, so a clean brush lays down only the
color it picks up from the canvas. The new commands brush.mixer.load
and brush.mixer.clean load and empty the brush. paint.mixerBrush reads
loadAfterStroke and cleanAfterStroke from the brush and applies them
after the stroke. Both are on by default, and a stroke with both on
paints as before. A stroke whose color differs from the one recorded
by the last stroke, load or clean loads that color first. A load or
clean records the foreground, so choosing a new foreground loads the
reservoir.

The options bar has the Current Brush Load swatch, whose menu holds
Load Brush and Clean Brush, and two toggles for the automatic Load and
Clean. Brush Settings shows the same two options. The new strings have
rows in every catalog.
2026-10-16 22:49:47 -07:00
Higgs32584andClaude Opus 5.5 425e782ac2 Pen: a finished path keeps its anchor squares on screen (#6884)
Closing a path or pressing Enter with the Pen left only the 1 px outline, so the
path looked gone and the anchors to edit were hidden. The Pen, Freeform Pen and
Curvature Pen now draw the active paths' anchors as Path Selection does, as in
Photoshop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:25:15 -04:00
Nicolas FalesyandClaude Opus 5.5 5f65579ecb perf(panorama): Photomerge 4× faster, bit for bit the same (#6489)
N is the whole algo-side Photomerge pipeline on 3 × 8 MP photos (register at the engine's
scale with the Auto layout, gain + vignette fit, seams, multi-band blend), 16 threads:
1042 ms before, 264 ms after, 3.9×, rounded to 4×. The engine's own glue (reading regions,
building weights, writing layers) is not in this number.

Registration and multi-band blending ran almost entirely on one thread per image (four
images on 16 threads), so most of the machine sat idle. Every output is bit for bit what
the serial code produced: a probe that hashes the public Photomerge entry points
(detect_many, match_pairs, align for every layout, photometric, seam_labels, multiband at
1 to 7 levels with 1 and 3 channels, with holes in the alpha, with images sticking out of
the canvas, plus direct harris / describe calls) matches on all 349 outputs before and
after, on 4 × 2 MP, 3 × 8 MP, 97×61, 211×157, 333×251, 320×240 two-image and 40×35 sets,
at 16, 3 and 1 threads.

- features `blur` (Harris window and BRIEF smoothing): the row pass runs rows in
  parallel; the column pass cuts the image into bands of 64 columns, each holding the
  `&mut` row segments of its own columns (no unsafe), run in parallel. Every column keeps
  its own f32 running sum, started from its first window summed in order and updated in
  the same order, so only the loop nest changes.
- `harris`: Sobel, the corner response and the non-maximum scan are parallel by rows (the
  candidates are fully ordered by score, y, x afterwards, so arrival order is irrelevant).
  `describe`: one descriptor per corner in parallel, kept in corner order.
- `reduce`, `expand`, `push_pull`: per-row maps in parallel. `reduce`'s column pass adds
  five whole rows in tap order (same per-element sum, now vectorised); `expand` works out
  its source columns once and truncates instead of calling libm `floor` (the clamped
  position is never negative); `push_pull` builds each coarse row from its two fine rows in
  raster order, which is the order the serial scatter-add used.
- `multiband`: grid fill per image, Laplacian difference, band accumulation (every row
  walks the bands in the original order), normalisation, collapse and the coverage union
  are parallel. Float additions are never reordered.

Left serial on purpose: the peak fold in `harris` (about 1 ms, an order-free max but not
worth the proof), the corner spacing grid, `seam_labels` (a dynamic-programming path whose
rows depend on the previous one), `photometric` sample gathering, and the pyramid levels
themselves (each level needs the one above).

Median of 5 (8 MP rows: 7), release, ms, interleaved before/after runs. The laptop was
busy with unrelated builds the whole time (load average 11 to 18 on 16 threads), so the
1-thread rows are noisy (a single thread is time-sliced against them) and rows can move
by 10 to 20 percent.

| Case (probe)                   | before (16 thr) | after (16 thr) | speedup | before (1 thr) | after (1 thr) |
|--------------------------------|----------------:|---------------:|--------:|---------------:|--------------:|
| register, 4 × 2 MP             |             154 |             60 |    2.6× |            336 |           167 |
| multiband, 4 × 2 MP, 3 ch      |             407 |            108 |    3.8× |            591 |           407 |
| multiband, 4 × 2 MP, 1 ch      |             307 |             73 |    4.2× |            813 |           566 |
| whole pipeline, 4 × 2 MP       |             437 |            160 |    2.7× |            716 |           504 |
| register, 3 × 8 MP (full size) |             327 |            124 |    2.6× |            978 |           391 |
| multiband, 3 × 8 MP, 3 ch      |            2348 |            916 |    2.6× |           1742 |          1150 |
| whole pipeline, 3 × 8 MP       |            1042 |            264 |    3.9× |           2264 |          1280 |

The 8 MP multiband row at 16 threads is the noisiest (minimum of 7: 959 to 224 ms, 4.3×);
the whole-pipeline row is steady. Max RSS for the 3 × 8 MP whole pipeline (the probe's
inputs are about 1.05 GB of it): 2.79 GB before, 2.79 GB after (three runs each).

Tests: the serial `blur`, `reduce`, `expand`, `push_pull` and `multiband` are kept in the
test modules as references and compared bit for bit over sizes that cross every band and
row boundary (1×1, 1-wide, 1-high, odd sizes, 63 / 64 / 65 / 129 columns, radius 0 to
wider than the image, holes, partial and zero weights, ROIs outside the canvas, 1 to 7
levels). Hard-coded hashes recorded from the old code cover multiband, push_pull,
reduce + expand everywhere (plain f32 arithmetic), and harris + describe on Linux x86-64
(they go through the platform's hypot / ln / cos / atan2 / sin_cos).


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 20:20:22 -06:00
wwew bd3b5d9192 fix(automation): refuse a group/world-readable control-token file (#4966) (#5459) 2026-10-16 20:20:03 -06:00
aiongg 7c44b2bc15 Build: use craft-docpath from craft-libs 2026-10-17 02:19:47 +00:00
ZuujisandClaude Opus 5.5 fbcb70b317 Save: ask before overwriting a file another app changed (#6596) (#6652)
* Save: ask before overwriting a file another app changed (#6596) [WIP]

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

* Save: ask before overwriting a file another app changed (#6596)

File › Save now notes the size and modification time of a document's
own file when it is opened or saved, and checks them again before
writing back. If another app changed the file in between, a prompt asks
whether to replace it (Cancel keeps both; Save As saves elsewhere)
instead of silently overwriting the other app's changes.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 20:19:32 -06:00
Korg1ch 4c881326ea fix(windows): keep crash-recovery autosaves out of the roaming profile (#5648) (#6412)
* fix(windows): keep crash-recovery autosaves out of the roaming profile (#5648)

* test(windows): cover the local recovery folder and moving old autosaves (#5648)
2026-10-16 20:19:06 -06:00
MarekSpodBiedry fbfc72ceb5 Close All, Close Others and Quit ask again about documents dirtied during the prompt (#4954) (#5079) 2026-10-16 20:18:47 -06:00
MarekSpodBiedry 20013ef9bd Linked smart objects: don't refresh a document while a background job runs on it (#4973) (#5074) 2026-10-16 20:18:17 -06:00
David MannandClaude Opus 5.5 3d2914368d PSD type: clamp warp bend and distortion before the f32 cast (#6856) (#6857)
A TySh warpValue, warpPerspective or warpPerspectiveOther beyond f32 became inf and the warp was silently dropped; they are now clamped to -100..100 like type.warpText.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:17:27 -06:00
Nicolas FalesyandClaude Opus 5.5 5bd428a239 perf(stack): Stack Mode 11× faster (#6462)
Layer › Smart Objects › Stack Mode (`algo::stack::combine`) was one serial
per-pixel loop (1.0x parallelism, identical at 1 and 16 threads), and Entropy
built a HashMap per channel per pixel.

- Output pixels are computed in parallel in 4096-pixel chunks (`par_rows`, so
  wasm keeps its serial path). Each chunk owns its scratch buffers.
- Each pixel uses exactly the same arithmetic in the same order as before, so
  every mode except Entropy is bit-identical: 110 output hashes (10 modes x 11
  cases: 3 x 24 MP, 7 layers of odd/mismatched sizes, transparency, HDR values,
  1 layer, no layers) match the old code.
- Entropy: a `[u32; 256]` histogram plus a short list of the bins touched
  replaces the HashMap; touched bins are summed in ascending order, so the result
  is now deterministic. The old HashMap summed in random iteration order and
  differed run to run (up to 1.2e-7 between two runs of the old code). New vs old
  output: max abs difference 1.19e-7 (one f32 ulp near 1.0), on up to ~10% of
  values in the 7-layer cases; the 3-layer 24 MP case is identical.
- Modes that never use the mean (Median, Min, Max, Range, Summation, Entropy) no
  longer compute it; Median uses an unstable sort (same result under total_cmp).

3 x 24 MP, release, median of 5 interleaved before/after runs (load average
~3.5-4.5 from an unrelated build running alongside), ms:

| Mode       | before | after (16 thr) | speedup | after (1 thr) |
|------------|-------:|---------------:|--------:|--------------:|
| Median     |   1100 |             99 |   11.1x |          1064 |
| Mean       |    308 |             36 |    8.6x |           338 |
| StdDev     |    342 |             41 |    8.3x |           380 |
| Entropy    |   4727 |            269 |   17.5x |          2730 |
| Kurtosis   |    458 |             55 |    8.3x |           488 |
| Maximum    |    368 |             36 |   10.1x |           346 |

The other modes land between 8x and 10x. Max RSS for Median is unchanged
(1.50 GB, of which 1.13 GB is the input layers).

Tests: hard-coded bit patterns recorded from the old code for every mode but
Entropy; Entropy against hand-computed values and run-to-run determinism; and a
chunk-boundary test comparing a multi-chunk image to per-pixel results.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 20:16:45 -06:00
F4LCON 75978a72f0 Targa: RLE packets end with their scanline (#6382) (#6386)
The image crate's Targa encoder let run-length packets run on from the end of
one row into the next. TGA 2.0 says a packet must not cross a scanline, and
readers that decode a row at a time misread such files: Pillow fails on them
with "buffer overrun", and texture tools and game engines reject them.

Targa is now written by our own encoder (codecs::tga), as Photoshop writes
it: RLE true colour or grayscale, every packet within its row, rows stored
bottom-up (the origin readers that ignore the descriptor assume), the alpha
bits in the descriptor and the TGA 2.0 footer. Reading stays with the image
crate.
2026-10-16 20:15:32 -06:00
jonathnwxh e31905358d Restore export preset field omitted by upstream preset loader 2026-10-17 09:32:44 +08:00
jonathnwxh 1dd50ba859 Merge current upstream while preserving font metadata repair 2026-10-17 09:24:30 +08:00
nrq6993-a11yandCursor c61cf9f0bd fix(engine): load each persisted preset set independently (#5616)
One malformed set in the preferences file (gradients, swatches, styles...)
made load_presets_json ignore the whole presets blob, so saved New Document
presets vanished at every launch: the shell merges back only changed keys,
leaving the damaged set on disk. Each set now loads on its own; null and
malformed sets keep their defaults.

Refs storytold/photocraft#5602

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 19:20:06 -06:00
nrq6993-a11yandCursor a1e19a0b87 fix(text): break paragraph lines at the horizontally scaled width (#5611)
Horizontal scale was applied after line breaking, so paragraph text below
100% wrapped early and text above 100% overflowed the box. Break box text
at the available width divided by the paragraph's horizontal scale (the
largest one when runs mix), scale the first-line indent and alignment
offset back to the box, and keep the scaled trailing space out of the
line extent.

Fixes storytold/photocraft#5565

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 19:19:59 -06:00
Matthew SantiagoandClaude Opus 5.5 d213273c74 GPU composite: lower-stack cache chunks have no texture view (#5423)
The prefix cache's chunk textures are only copied to and from
(COPY_SRC | COPY_DST), but went through Tex::new, which always creates
a default view. Vulkan rejects a view on an image with no sampled,
storage or attachment usage (VUID-VkImageViewCreateInfo-image-04441);
wgpu doesn't check it, so it only shows with the validation layers on,
once per new chunk while painting. The view was never used. Chunks now
hold a bare wgpu::Texture. No rendering change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:19:53 -06:00
Ahmed MagdyandClaude Opus 5.5 4e5c6a018a fix(io): SVG text placed by a transform keeps it on import (#4806) (#5334)
usvg builds the glyph outlines of a text element (Text::flattened) with
identity absolute transforms, and the SVG importer read each path's
abs_transform(). Text positioned by its own or an ancestor's transform,
as Illustrator writes every text element (matrix(1 0 0 1 x y)), lost it:
it landed off the canvas, so nothing showed, or at its untransformed
place and size.

The converter now composes each node's transform from its parent chain,
which equals abs_transform() outside text, and sizes raster fallbacks
from the same transform. A new test checks translate, matrix, scale and
transformed groups against resvg's own render; it fails on main.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:19:47 -06:00
F4LCON a05daee325 Filters: the live preview is never coarser than the screen (#6326) (#6351)
On a document over about 5 MP with Low Resolution Previews on, a filter
dialog's live preview ran on a reduced proxy at every zoom, so at 100 % the
canvas showed the image in k-pixel blocks and hid exactly what Unsharp Mask,
High Pass and Smart Blur do.

ensure_filter_preview now caps its factor with proxy::view_factor(k, zoom),
the same rule adjustment previews got in #5764, so from 100 % the preview is
full resolution and stays reduced when zoomed out. The preview already runs on
a cancellable background worker, so the full-resolution path does not block the
UI thread.

Also in this change:

- canvas.rs and filter_preview_ui.rs share one canvas::filter_preview_factor,
  so the in-dialog preview window cannot size textures from an uncapped factor
  and rebuild them when the first result arrives.
- The cap is gated on cfg!(target_arch = "wasm32"), so the web build keeps its
  proxy rather than running a full-resolution filter inline on the UI thread.
- The end-to-end test moved into mod tests and covers Low Resolution Previews
  off; a new factor test covers 25/50/75/100/400 %, NaN and zero zoom.
2026-10-16 19:14:50 -06:00
Thang Nguyen aaf86e2a9c fix(psd): read the split ("quilt") warp of placed layers
Photoshop writes a split custom warp as `quiltWarp` in the placed layer's
SoLd/SoLE descriptor: a grid of Bezier patches (deformNumCols x
deformNumRows control points, patch boundaries in quiltSliceX/Y). Only a
single 4x4 `warp` mesh was read, so such smart objects rendered unwarped
and in the wrong place. Read the quilt grid into a multi-patch
BezierMesh; anything inconsistent is still refused.

On two real mockups (a sneaker and a V-neck jersey), the mean difference
from another PSD renderer over the design area drops from 14.4 to
0.66/255 and from 13.0 to 0.64/255. Refs #4398.
2026-10-17 08:09:29 +07:00
Tanish Manku 132257912a README: put Community packages after the Get started status note (follow-up to #1149) (#6886)
The Community packages heading from #1149 landed before the maintainers' releasing note and the IMPORTANT status admonition, so those two paragraphs rendered under Community packages instead of Get started. Move the section to just above Documentation; the content is unchanged.
2026-10-16 17:52:24 -07:00
Ivan · VanTan 10fdcbbefc chore: fix workspace Clippy collapsible_match warnings (#5190)
Strict workspace Clippy stopped on collapsible_match in affinity, raw, plugins, engine, ui-egui and xtask. Pure conditions become guarded match arms (each falling through to an existing empty arm), and the ruler drag state and the measurement command result are bound to locals, preserving evaluation order, error messages and fallbacks.

The same lint fixes also appear as a side commit in #5157 (David2766), which can drop it on rebase.
2026-10-16 17:42:36 -07:00
Gustavo Ziger d900daa0ba fix(web): File › Place Embedded is enabled in the browser and places the picked file's bytes (#1209, #5749) (#5585)
With a document open in the web app, Place Embedded was disabled because its menu enablement inherited the engine's native-filesystem check, although the menu path reads the picker's bytes and never touches a path. Place Embedded is now enabled whenever a document and a file-dialog service exist; Place Linked keeps the engine's native guard, since the web could only link to a bare file name (caught in #5817 by Higgs32584). An enablement test runs with the native check forced off, and a file-dialog test covers the browser answer shape, cancel, invalid bytes and the Free Transform handoff.

Fixes #1209. Fixes #5749.
2026-10-16 17:38:56 -07:00
Michael Higginsandahmedhosnypro 3722dfbc63 fix(ui): Arabic and Hebrew layer names and the title read right to left (#5159) (#5385)
egui places font runs left to right with no bidi reordering, so a right-to-left layer name showed every word correctly but in reversed order. bidi_text::visual puts a label into display order with the Unicode Bidirectional Algorithm (unicode-bidi, already in the tree via usvg): right-to-left words stay in logical order for the shaper, everything else in a right-to-left run is reversed around them and brackets are mirrored. The Layers panel's rows and the app-drawn title bar use it; the title keeps a left-to-right paragraph so '— PhotoCraft' stays at the right, and a long right-to-left name is cut at its logical end so its start survives. Display only: stored names and the OS title keep the logical text.

Fixes #5159.

Co-authored-by: ahmedhosnypro <ahhosnyas@gmail.com>
2026-10-16 17:36:06 -07:00
Gert Pellin - GPE 5a33441af3 fix(engine): an unknown layer id or name lists the document's layers, and a name matches regardless of case as a fallback (#5058) (#5133)
A command that fails on a layer id or name the document doesn't have now says which layers there are, top-down as id and name (at most 20, names cut at 64 characters), so an agent can recover from the error alone: ids are not positions, and a freshly opened PNG's Background is layer 2. NoLayer becomes UnknownLayer { id, known } at both command entry points, keeping the 'no such layer' prefix. A layer name that matches no layer exactly matches regardless of case; an exact match always wins.

Fixes #5058.
2026-10-16 17:34:40 -07:00
Gert Pellin - GPE a6a1d2ed22 feat(algo): face detection with libfacedetection's CNN in pure Rust, and image.detectFaces (part of #3603) (#4513)
photocraft_algo::faces::detect finds faces in an RGBA image with a score, a box and five landmarks, through a pure-Rust port of libfacedetection's YuNet network (Shiqi Yu, BSD-3-Clause; licence, NOTICE and ATTRIBUTION rows included). The weights are the library's, converted to half precision (107 KB), parsed defensively. image.detectFaces reports the document's faces to scripts and agents; it is read-only with no history step and no menu entry. The network is compiled out of the browser build, where the command reports itself unavailable. Head outputs are pinned against the C++ reference.

Part of #3603.
2026-10-16 17:31:02 -07:00
Gert Pellin - GPE 1b3bc48267 docs(readme): Community packages section with the Gentoo ::snakebyte overlay (#1149)
Adds a Community packages heading to the README, noting these installers are maintained by community members, with the Gentoo recipe for media-gfx/photocraft-bin from the ::snakebyte overlay. The overlay's ebuild fetches the official Linux x86_64 release tarball with Manifest-pinned hashes that match the release's SHA256SUMS.
2026-10-16 17:28:51 -07:00
Harrison O'Connor-Hoover 8a4ed48354 fix(ui): numeric fields drop oversized text, paste and IME events before layout (part of #2150) (#2258)
A single oversized input event reached egui text layout in a numeric field even though it could never become a valid value. number_edit now drops Text, Paste and IME events longer than 128 scalar values while the field has focus, and the custom parser rejects accumulated text over the cap before normalizing. DragValue keeps focus, accessibility, drag and arithmetic behaviour. Six harness tests cover the event kinds, the Unicode boundary, accumulation, undo, and document text fields staying unrestricted.

Part of #2150.
2026-10-16 17:25:50 -07:00
wwew dd69f0797d fix(engine): reserve the define-brush tip and selection-mask buffers fallibly (#212) (#5469)
brush.defineFromSelection allocated the sampled tip and the selection mask infallibly over the whole selection, so a large selection could abort the process. Both now go through the engine's fallible allocator and return a normal error that leaves the brush, presets, history and document untouched. A failpoint test asserts all four.

Part of #212.
2026-10-16 17:23:08 -07:00
safvanedge aecac608b4 fix(ui): Type tool caret stays solid after typing, navigation or a click, then resumes blinking (#5511) (#5512)
The caret's blink phase was tied to the wall clock, so an arrow key or keystroke that landed in the off half-cycle moved an invisible caret. A transient caret_blink_time on UiState is reset on every handled key event, on a click that places the caret, and while dragging; draw_overlay computes the phase from it so the caret is solid for 530 ms after each interaction, and the repaint is scheduled to the next phase boundary.

Fixes #5511.
2026-10-16 17:20:53 -07:00
Vikram Bhaskaran c7d2c85faf fix(ui): Color Picker's Only Web Colors snaps every edit and is remembered across pickers (#4995) (#5442)
With Only Web Colors on, every picker edit (field, sliders, hex, Lab/CMYK, screen sampler, Current) snaps through the clamped 216-colour ramp. The checkbox is remembered on the Color panel state so the foreground, field, command, Layer Style and type-style pickers open with the same setting. Opening with it on snaps the colour and its original together, so OK without an edit leaves a fill layer unchanged. The hex field follows the colour when another path changes it while focused.

Fixes #4995.
2026-10-16 17:19:03 -07:00
Nigel Ihza Firdausy 9628290b36 fix(ui): Layer › New Fill Layer › Solid Color opens the Color Picker, creating the layer on OK (#5144) (#5166)
Creating a Solid Color fill from the Layer menu or the Layers panel footer now opens the shared Color Picker seeded with the foreground colour. The layer is created on OK in one history step; Cancel leaves the document and history untouched. Parameterised calls (CLI, MCP, actions) still create the layer directly.

Fixes #5144.
2026-10-16 17:13:51 -07:00
Higgs32584andClaude Opus 5.5 c9334c4661 Windows: a maximized custom-title-bar window lets an auto-hidden taskbar show (#4845)
An auto-hidden taskbar reserves no work area, so the maximize guard left the
caption-less window covering the whole monitor and the shell treated it as a
full-screen app, never revealing the taskbar. On each monitor edge with an
auto-hidden taskbar (found via Shell_TrayWnd / Shell_SecondaryTrayWnd through
safe winsafe calls) the window now stops 1 px short of the monitor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:39:39 -04:00
David MannandClaude Opus 5.5 e1db181435 Guides: ruler guide snapping rebuilds its targets when snap options change (#6858)
The cached guide targets were keyed only by document and revision, so showing the
grid or turning Snap To Layers/Document Bounds off kept the stale targets; the key
now includes the Snap To options.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 01:35:33 +02:00
David MannandClaude Opus 5.5 22217e6ab2 CLI batch: skip shell-only Save and view steps inside played action sets (#6860)
Batch strips Save/Save As/zoom steps from every action of a loaded set, called actions
included, and remaps their unchecked/dialog step indices.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 01:32:48 +02:00
David MannandClaude Opus 5.5 921637322c Native format: refuse a .pcraft with out-of-range numbers when it is opened (#6862)
A number past f32's range (e.g. opacity 1e39) decoded as infinity, so the document opened but
could never be saved again; read_manifest now refuses it as corrupt, naming the field.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 01:30:16 +02:00
jonathnwxh b0484eb72c Remove extra blank line at merged test boundary 2026-10-17 07:24:29 +08:00
jonathnwxh 4d32089b4f Merge upstream main and retain both font metadata and paragraph-wrap regressions 2026-10-17 07:23:25 +08:00
David Mann 9d38b28da6 Merge remote-tracking branch 'origin/main' into feat/file-216-2-large-fixtures
# Conflicts:
#	docs/scorecard.md
2026-10-17 01:19:35 +02:00
David Mann 0739ff48c5 Merge remote-tracking branch 'origin/main' into fix/4017-huge-new-document
# Conflicts:
#	crates/ui-egui/src/gpu_canvas.rs
#	docs/scorecard.md
#	perf/budgets.toml
2026-10-17 01:07:37 +02:00
Marco Arnulfo db46713c2c Unsaved-changes prompt: the default answer takes keyboard focus, so arrows and Enter work (part of #6441) (#6466) 2026-10-16 17:03:15 -06:00
Jason Xiang 06ad07ab9b fix(io): color-manage grayscale WebP exports (#5875) 2026-10-16 17:02:54 -06:00
Jason Xiang f61ed26e14 fix(io): honor version-aware OpenRaster group isolation (#5872) 2026-10-16 17:02:23 -06:00
Jason Xiang 840edc311c fix(io): preserve linear grayscale profiles for selected EXR parts (#5867) 2026-10-16 17:01:52 -06:00
Jason Xiang 462adeadd2 test(io): cover mixed-region CMYK TIFF composites (#5588) 2026-10-16 17:01:10 -06:00