100 Commits
Author SHA1 Message Date
Nigel Ihza Firdausy 0df333f623 fix(gpu): require consent to persist CPU recovery (#3539) 2026-10-13 12:46:33 -04:00
Nigel Ihza Firdausy 5320cecb6d fix(adjust): preserve midtone luminosity with multiple sliders (#4941) (#4990) 2026-10-13 12:07:03 -04:00
Nigel Ihza Firdausy 625141c2ad fix(ui): display uppercase hex values in the color picker (#4859)
* fix(ui): show uppercase RGB hex values in shared color picker

* test(text): expect uppercase hex in text style color picker

* fix(ui): uppercase displayed hex without changing color storage

* test(text): keep color serialization independent of picker display casing
2026-10-13 12:03:37 -04:00
Nigel Ihza Firdausy 35710a94ad Fix: native gesture fallback dedupe (#5080)
* fix(input): avoid double-applying native pinch during legacy fallback

* test(input): cover native trackpad rotation and preference gating
2026-10-13 12:03:11 -04:00
Nigel Ihza Firdausy ccb33741f9 fix(psd): reserve imported layer IDs when allocating section dividers (#4885) 2026-10-13 11:56:55 -04:00
Nigel Ihza Firdausy 40c9c3c451 Validate merged PSD channel indices before decoding (#4994)
* fix(psd): validate merged channel index before decoding (#4981)

* test(psd): compare decoded channel bytes as vectors
2026-10-13 11:56:47 -04:00
Nigel Ihza Firdausy c0e62673f8 fix(macos): default droplets to a writable user directory (#4746) 2026-10-13 03:06:27 -04:00
6cbf4bd24d fix(input): support precision trackpad pinch zoom on Linux (#4786)
* fix(input): translate Linux precision Ctrl-wheel pinch gestures

* fix(input): decode Wayland high-resolution point pinch events

* docs(input): describe precision pinch support on Windows and Linux

* rustfmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:49:40 -04:00
Nigel Ihza Firdausy 00802b03de feat(new-document): add ISO B3 and B4 print presets (#4784)
* feat(new-document): add ISO B3 and B4 print presets

* test(new-document): compare dimensions without signed integer conversion
2026-10-13 02:49:27 -04:00
5b1b6b075e feat(export): offer ICO in Export As with dimension validation (#4744)
* feat(export): offer ICO in Export As with format-size guidance

* fix(export): use theme warning token for oversized icons

* i18n: translate the Export As ICO size warning

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:48:25 -04:00
Nigel Ihza FirdausyandBrandon Thomas cb95e17fc1 fix(type): preserve keyboard focus when searching for fonts (#4745)
* fix(type): leave keyboard input to focused font search

* test(type): focused font search cannot alter the edited type layer

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-13 02:48:13 -04:00
4fdf1a723b fix(batch): skip recorded Quick Export steps when writing batch output (#4747)
* fix(batch): ignore recorded Quick Export and use batch output

* fix(batch): ignore recorded Quick Export and use batch output

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:48:04 -04:00
Nigel Ihza FirdausyandBrandon Thomas 5cff9ebed2 fix(clipboard): prompt to save new documents containing pasted pixels (#4683)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-13 01:11:20 -04:00
Nigel Ihza FirdausyandBrandon Thomas c8de7870d5 perf: reuse sampled composite across channel visibility toggles (#4080)
* perf: reuse sampled composite across channel visibility toggles

* test: borrow full composite for sampled-grid oracle

* test: compare the entire channel view after invalidation

* style: keep channel cache construction readable

* fix: format channel test and share band renderer

* fix: invalidate sampled channel composite after edits and previews

* refactor: keep channel rendering optimization isolated from compositor

* test: cover both partial-damage and full channel redraw paths

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 23:44:50 -04:00
Nigel Ihza Firdausy ab5a6c5ed2 fix(layer-picker): include text bounds in right-click layer list (#4288) 2026-10-12 21:26:49 -04:00
Nigel Ihza Firdausy 94b6042289 fix(linux): follow GNOME's light default when the portal has no preference (#4289) 2026-10-12 20:53:58 -04:00
9712f5e7d5 fix(selection): reject incomplete magic wand pixel buffers (#4046)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:14:41 -04:00
0761234c2f fix(algo): reject oversized Magic Eraser masks without overflow (#4043)
* fix(algo): reject oversized Magic Eraser masks before multiplying dimensions

* Format the oversized Magic Eraser mask test

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:14:33 -04:00
Nigel Ihza Firdausy 799f28167f fix(selection): avoid panics when sampling incomplete region masks
Use checked mask indexing so incomplete or extreme regions return zero coverage instead of panicking or wrapping.

Validated with the full algo suite, independent native and Wasm regressions, and green PR checks.
2026-10-12 18:10:27 -06:00
4a53fad835 feat(codecs): add XPM2 plain-text image import support (#4264)
* feat(codecs): import XPM3 images with transparency (#3692)

* Correct XPM test expectation for green palette name

* feat(codecs): decode XPM2 plain-text image files

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:01:35 -04:00
5981a02e64 feat(codecs): support full X11 rgb.txt color names in XPM (#4263)
* feat(codecs): import XPM3 images with transparency (#3692)

* Correct XPM test expectation for green palette name

* feat(codecs): resolve full X11 rgb.txt color names in XPM

* XPM: fix Format::ALL length after DPX, reject '+' in hex colours, fix non-ASCII byte-string test, fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:59:36 -04:00
Nigel Ihza Firdausy 5c4ad593de fix(ui): reject rotation angles outside f32 range (#4044) 2026-10-12 19:17:31 -04:00
Nigel Ihza Firdausy fd4ba52f79 fix(ui): cancel unfinished polygon on lasso tool switch (#4021) 2026-10-12 19:17:25 -04:00
e54adc250c feat(codecs): decode legacy PCX images and palettes (#3692) (#3701)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:12:55 -04:00
4705fc5980 feat(io): open Krita .kra composites as editable raster documents (#3692) (#3700)
* feat(io): open Krita .kra composites as editable raster documents (#3692)

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:01:20 -04:00
Nigel Ihza Firdausy 6ca7e60cb0 fix(ui): make checkbox labels nonselectable with pointer cursors (#4002) 2026-10-12 06:19:44 -04:00
Nigel Ihza Firdausy 73520f8681 fix(ui): Open layer color choices from the visibility icon (#4001)
* fix(ui): offer label colors from the layer eye icon

* fix(ui): offer label colors from the layer eye icon

* test(ui): cover layer eye color context menu

* style(ui): keep extracted color menu indentation consistent
2026-10-12 06:19:28 -04:00
Nigel Ihza Firdausy 84aa8df05a fix(transform): keep nearest-neighbor previews on original texels (#3688) 2026-10-12 04:10:55 -04:00
Nigel Ihza Firdausy c5a9e7f9b2 fix(ui): don't reopen floating Brush Settings on restart (#3570) 2026-10-12 04:03:07 -04:00
f0322f74e8 fix(selection): scope Color Range to the active selection (#3313) (#3366)
* fix(selection): keep Color Range inside active selection and reject out-of-bounds samples

* fix(ui): scope Color Range dialog and canvas previews to the active selection

* test(selection): cover Color Range scoping, sampling, invert, Add and undo

* test(ui): ensure Color Range preview and pipette honor existing selection

* test(selection): avoid Debug bound when asserting prepared-query error

* test(selection): validate malformed Color Range sample orders without selection interference

* Color Range: format, and scope the OK undo test's prior selection (#3313)

The existing ok_applies_one_undo_step test picked a prior selection that
excluded the red it then expected Color Range to select; with Color Range
scoped to the active selection it now selects the red inside it.

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:52:08 -04:00
Nigel Ihza Firdausy 6f6d2ea467 fix(format): save the target file name in native bundle manifests (#3312) (#3319) 2026-10-11 21:26:13 -04:00
59da288daa fix(type): preserve formatting for trailing empty paragraphs (#3198)
* fix(type): preserve formatting for trailing empty paragraphs

Represent an explicitly styled terminal empty paragraph with a zero-byte paragraph run, keep it through normalization, style only the caret's paragraph, and transfer that style when text is inserted at EOF. Cover line geometry, typing, and PSD text serialization.

* fix(type): retain imported terminal paragraph marker style

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:36:52 -04:00
8a9f4722d2 fix(shapes): preserve transformed outline on unchanged live geometry edits (#3197)
* fix(shapes): preserve transformed outline on unchanged live geometry edit

A nonuniform scale stores an anisotropic outline but live rounded rectangles only store scalar corner radii. Avoid regenerating the path when all live values are unchanged, preserving transformed corners and PSD vector source. Cover no-op edits, real radius edits and undo at all depths.

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:23:55 -04:00
Nigel Ihza Firdausy d8217b3476 fix(image): keep smart-filter masks aligned through Image Size, Canvas Size and Crop (#3195) 2026-10-11 05:18:52 -04:00
09d079278e fix(video): retain original footage for Restore Frame and Show Altered Video (#3192)
* fix(video): retain original footage for restore and comparison (#3059, #3060)

* Clamp original-frame inserts to the original stack; cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:02:29 -04:00
6cfed3a7dc fix(masks): bake feathered coverage when applying pixel masks (#3044) (#3191)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:49:03 -04:00
01833cfac2 fix(actions): avoid replaying Browse script edits twice (#3190)
* fix(actions): avoid replaying Browse scripts twice (#3057)

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:38:07 -04:00
21326ded43 fix(vector): preserve scaled live-line thickness during endpoint edits (#3184)
* fix(vector): keep scaled live-line weight after editing endpoints (#3063)

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:31:47 -04:00
a9aabcdfde fix(vector): pivot explicit path transforms on target layer (#3062) (#3183)
* fix(vector): pivot explicit path transforms on target layer (#3062)

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:23:08 -04:00
f6dd0aa6f3 fix(io): preserve group fill opacity in OpenRaster exports (#3181)
* fix(io): preserve group fill opacity when exporting OpenRaster (#3048)

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:16:30 -04:00
5c9d9116bc fix(layers): select Shift ranges in visible panel row order (#3041) (#3180)
* fix(layers): select Shift ranges in visible panel row order (#3041)

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:12:27 -04:00
Nigel Ihza Firdausy 37929405df fix(history): restore empty layer target on undo and redo (#3042) (#3179) 2026-10-11 04:02:23 -04:00
64ec252dbe fix(layers): remap linked-layer groups when copying between documents (#3178)
* fix(layers): remap link groups when copying across documents (#3045)

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:48:33 -04:00
Nigel Ihza Firdausy 3188866b25 fix(masks): retain off-canvas alpha in From Transparency (#3043) (#3177) 2026-10-11 03:43:18 -04:00
2785699464 fix(type): use the correct line at soft-wrap caret boundaries (#3174)
* fix(type): use the next visual line at shared wrap boundaries

At soft-wrap and forced-break character offsets, resolve the caret's visual line from the last matching line rather than the preceding one. Make line-edge navigation share that choice and add regression tests for wrapped, forced and paragraph breaks.

* test(type): use actual break characters in caret boundary regressions

* fix(type): preserve the visual line when hit testing a shared wrap offset

Keep caret index preference on the following line, but use pointer geometry for click-hit line identity at an adjacent line's end.

* Update the caret round-trip test for wrap-boundary lines; cargo fmt

The existing round-trip helper expected the first line containing a
shared soft-wrap offset; the caret now reports the following line, where
it is drawn and where hit testing lands.

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:37:27 -04:00
ee3839955b fix(type): exclude text inside hidden groups from automatic hit testing (#3173)
* fix(type): skip text in hidden ancestor groups during automatic hit testing

Honor the effective visibility of every ancestor when locating a type layer by point. Explicit layer targeting remains available, with a regression test for nested hidden groups.

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:17:46 -04:00
f9c5486ffe fix(shortcuts): reject malformed bindings and correctly display Plus (#3006)
* fix(shortcuts): reject malformed key bindings and format Plus

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:05:09 -04:00
Nigel Ihza Firdausy d5bab13674 fix(transform): commit Free Transform on an outside click (#2909)
* fix(transform): accept a free transform on an outside click (#2851)

* fix(transform): accept outside clicks in the rotation zone (#2851)
2026-10-11 02:51:59 -04:00
Nigel Ihza Firdausy 03d45a80ee fix(brush): preserve pen pressure when joining Shift-click strokes (#2827) (#2890) 2026-10-11 02:46:01 -04:00
ea4579ea11 fix(canvas): update Move transform-control cursors on first hover (#2887)
* fix(canvas): show Move transform control cursors before first interaction (#2871)

* Fix the Move transform-controls cursor test after merging main

ViewXform gained an aspect field on main, and the rotation-band probe sat
within the top-centre handle's grab radius; probe between handles instead.

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:39:28 -04:00
e59bb2909b feat(ui): allow disabling Studio canvas background dots (#2317)
* feat(prefs): persist an optional canvas dot pattern setting (#2264)

* feat(canvas): respect interface canvas dot preference (#2264)

* test(prefs): cover canvas dot toggle persistence and reset (#2264)

* Translate the Show canvas dots preference and regenerate the scorecard

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:15:06 -04:00
7687380604 feat(prefs): add search across preference sections and settings (#2679)
* feat(prefs): search visible settings across sections (#2593)

Filter the Preferences sidebar by section and editable field labels,
switch to the first match, and show matching settings in the editor.
Keep the draft intact while searching and hide Reset Section during filtering.

* Preferences search: translate the field hint and empty state; rustfmt

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

* Merge fixups: pass the new search argument in a main test; search test uses a visible Type setting

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:32:04 -04:00
817678d6a0 fix(ui): prevent Layers opacity controls from overflowing narrow docks (#2325)
* fix(ui): fit Layers opacity controls within narrow docks (#2309)

* Layers Opacity row: keep the label at the default dock width

Show the label while the blend dropdown keeps 80 px, so the default
Layers dock looks as before and only long translated labels on narrow
docks give way.

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:03:45 -04:00
a28d786638 fix(navigator): zoom the document with the mouse wheel over the preview (#2724)
* fix(navigator): zoom with wheel over the preview (#2716)

* rustfmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:43:57 -04:00
4908fde283 fix(ui): make painting symmetry available across all themes (#2704)
* fix(ui): expose painting symmetry for all themes (#2659)

Render the existing symmetry control for Brush, Pencil and Eraser
independent of palette. Add a per-theme, per-tool UI visibility test.

* rustfmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:26:54 -04:00
5d9e812efc feat(prefs): add intermediate UI scale options from 80% to 95% (#2678)
* feat(prefs): add 80–95% UI scale presets (#2492)

Offer finer five-percent steps between 75% and 100% for small screens.
Exercise the new values through the live preference and egui scaling test.

* Label the 80-95% UI scale choices as percentages

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

* Translate the 80-95% UI scale labels in every catalog

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:16:47 -04:00
cebdbb21ea fix(ui): unify Spectrum menu spacing and hover contrast (#2323)
* fix(ui): unify Spectrum popup padding and hover contrast (#2187)

* fix(ui): unify Spectrum popup padding and hover contrast (#2187)

* fix(ui): unify Spectrum popup padding and hover contrast (#2187)

* fix(ui): unify Spectrum popup padding and hover contrast (#2187)

* fix(ui): unify Spectrum popup padding and hover contrast (#2187)

* fix(ui): remove unused menu token after sharing popup style

* test(ui): cover Spectrum popup hover contrast and theme isolation

* cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:00:56 -04:00
Nigel Ihza Firdausy f8744c6b81 fix(ui): dim unchecked layer style effect labels even when selected (#2288) (#2321) 2026-10-10 13:40:10 -04:00
Nigel Ihza FirdausyandBrandon Thomas 8d13aff784 fix(layers): preserve Background behavior across merge, duplication and canvas resize (#2419)
* fix(layers): keep Background semantics across merge, duplication and canvas size

Preserve destination locks in Merge Down, share the normal Background-copy rule with multi-layer duplication, and convert a Background to a paintable layer when extending its canvas transparently.

Add depth-aware regression coverage and ordinary-layer controls for #2380, #2381 and #2384.

* refactor(layers): copy lower-layer locks without cloning Copy data

* Format background_layer_consistency test

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 13:26:11 -04:00
743d81307b Fix/2361 scrub layer labels s20261010 t083444 z og3kyw (#2397)
* fix(layers): scrub opacity and fill by dragging labels (#2361)

* test(layers): compare translated scrub labels as text

* test(layers): query egui label roles for scrub gestures

* cargo fmt

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

* Layer label scrub test: find the text label by its value on the popup arrow's row

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 13:24:54 -04:00
Nigel Ihza Firdausy 24fbf4a84f fix(document): restore layer comps when duplicating documents (#2429)
* fix(document): keep layer comps valid across document duplication

Remap saved comp and Last Document State references when cloned layers receive new IDs, including historical states and nested groups. Drop obsolete comp metadata in merged-only copies instead of carrying references to removed layers.

Add focused engine regressions for #2382 and related copy workflows.

* style(test): apply rustfmt layout to layer-comp regression tests
2026-10-10 13:12:15 -04:00
Nigel Ihza Firdausy 13d89378d8 fix(properties): expose equal-gap layer distribution (#2349) (#2398) 2026-10-10 13:08:22 -04:00
41d54036fc fix(selection): constrain Polygonal Lasso segments with Shift (#2352) (#2396)
* fix(selection): constrain Polygonal Lasso segments with Shift (#2352)

* Polygonal Lasso: make the Shift constraint helper visible to its tests; cargo fmt

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 13:08:18 -04:00
Nigel Ihza FirdausyandBrandon Thomas 91a2b58725 fix(ui): remove the gap beneath Spectrum document tabs (#2265)
* fix(ui): remove Spectrum document tab-to-canvas spacing (#2188)

* scrollbars test: compare the centred view within float error

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 03:40:16 -04:00
Nigel Ihza Firdausyandtiya nur alfiyanti 1e7c38b98f Expose Clone Stamp opacity and flow and apply them to strokes (#2256)
Co-authored-by: tiya nur alfiyanti <tiyanuralfiyanti24@gmail.com>
2026-10-10 02:20:27 -04:00
d502c5de27 ci(web): validate browser builds and wasm size on pull requests (#1689)
* ci(web): build browser release and enforce wasm gate on PRs

* docs(web): describe PR browser build and size checks

* CI web gate: run only when web-relevant files change (main always)

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:05:15 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 bdbd43e63f Color Lookup: Load 3D LUT opens the native file picker (#1947)
The Color Lookup properties' Load 3D LUT… entry opens the platform file picker (filtered to .cube, .3dl and .look, on desktop and web) and embeds the chosen LUT into that adjustment layer, replacing the typed-path field. A cancelled or invalid pick leaves the layer unchanged (#1749).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:20:13 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 a4fbb1245d Levels applies each channel before the master, like Curves (#1929)
Levels and Curves composed their master and per-channel records in opposite orders (#975). The
Photoshop-authored corpus decides it: channel first, then master. Levels now does that, bringing
psd-tools levels_rgb.psd from 0.447 to 0.012 max error; Curves (curves_rgb.psd passes) is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:19:29 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 748821864e Panels: Close and Close Tab Group on a dock tab's context menu (#1924)
Right-clicking a dock tab offers Close (hides just that tab; the last one hides its group) and Close Tab Group, as in Photoshop. Hidden tabs are kept by name in the dock layout, so they survive theme switches and workspace saves, and the Window menu brings a tab back (#1753).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:14:10 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 04b9db61e9 Filter dialogs remember their last applied settings (#1944)
A schema-built filter dialog reopens with the values it was last applied with, as in Photoshop. Only schema-valid numbers, choices and booleans are kept (no paths, document indices or free text), and a failed or cancelled dialog saves nothing (#1757).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:57:03 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 a82408e3e2 Actions: Play runs the first action when none is selected (#1990)
The Actions panel's Play button falls back to the first action when no row (or a stale row) is selected, as Photoshop does, and a failed playback request now shows in the status bar instead of being dropped (#1951).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:53:33 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 25e349b048 Dialogs: the whole title band drags the window (#1945)
A dialog's drag handle spans the full title row plus a few pixels of padding around the title, so grabs slightly beside, above or below the title text move the window (#1921). The title keeps its height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:50:27 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 765495e53b Free Transform: rotation cursor and live angle readout (#1948)
Just outside a corner the pointer becomes a curved rotate arrow, and while rotating the angle is shown
next to it. Distort and Warp keep their cursors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:50:07 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 c02ffa9755 Spot Healing: Proximity Match samples the original pixels (#1968)
Proximity Match copied its translated patch within the buffer it was writing, so overlapping offsets
re-copied fresh output and left streaks. It now reads from the pre-heal pixels, with checked bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:46:58 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 3eef2c6bb4 Type: imported PSD tabs render as whitespace, not a box (#1942)
A literal tab in PSD text shaped as .notdef. Layout now shapes it as a space, keeping byte, style
and caret offsets intact. Tab stops are still to come. Part of #1881.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:34:38 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 cc4a13b58d Paint: keep stylus pressure on quick taps (#1967)
A release that egui reports as both a drag stop and a click no longer replays the click over a live
stroke (which painted a second full-pressure dab), and the click fallback now uses the pen's pressure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:34:21 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 54d7b07acf Convert to Smart Object keeps a pattern fill's pattern (#1937)
The embedded document built by layer_to_smart now carries the document's patterns, so pattern fills
and pattern effects keep rendering inside the smart object and after Edit Contents. Fixes #1867.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:33:20 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 d6802c62c0 Proximity Match: bound search radii to the image (#1969)
best_offset clamps the search radius and ring to the image extent and rejects zero or negative radii,
so hostile values no longer overflow i32 loops or search for billions of iterations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:29:16 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 3cae29abfa Grid: percentage spacing and snapping use each canvas axis (#1914)
A percentage grid now spaces vertical lines by the document width and horizontal lines by its height,
so rectangular canvases get proportional cells, and grid snapping uses the same per-axis steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:27:30 -04:00
3c5dc002d2 macOS: unsaved-changes prompt shows plain button labels (#1936)
On macOS the save prompt reads Save / Don't Save / Cancel without printed key letters, like the native alert; D, C, S, Return and Escape still answer it. Windows and Linux keep their mnemonics. Shortcut test from #1963 (arthurdeka).

Co-Authored-By: arthurdeka <arthurdeka@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:22:26 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 d402631411 File > Export > Color Lookup Tables: export selected adjustment layers (#1493)
file.export.colorLookupTables takes scope all|selected or an explicit layers list and bakes only those top-level, visible adjustment layers, always in document stacking order; the dialog defaults to the selection when it is all exportable adjustments. Invalid selections and sizes are errors rather than silent fallbacks; LUT sizes outside 2-129 (the reader's limit) are now refused instead of clamped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:36:51 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 0f2900da34 Edit > Stroke: a full options dialog with Fill's blending controls (#1477)
Edit > Stroke… from the menus opens a Photoshop-style dialog (Width, Color, Location, Blending:
Mode, Opacity, Preserve Transparency) remembering its last values; called with params it runs
directly. edit.stroke gains mode and preserveTransparency, blending through Fill's compositing
(blend_color_mask), and validates them. The Blending section is shared with Fill
(dialog_blend_ui.rs). Fixes #1463.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:21:21 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 ffb62ec975 Pen: Ctrl/Cmd+Z undoes in-progress points (#1478)
While a Pen path is being drawn, Undo (its customisable shortcut or Edit > Undo) and Backspace/
Delete remove the last uncommitted anchor instead of undoing an earlier document step, as in
Photoshop; once the draft is empty, Undo works on the document again. Fixes #1466.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:18:00 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 75e6bad453 Control: ui.inspect reports the open Brush Preset picker (#1672)
ui.inspect now has brushPicker: null while closed, {pos, list} while the Brush
Preset picker is open (list is its collapsed/filter/view/renaming state), so
agents can see what a right-click with a painting tool opened. Fixes #1558.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:50:01 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 8bc9861224 Control: ui.menu.invoke respects the modal menu policy (#1671)
A control-channel menu click could run an edit behind an open dialog (a rotation
under Image Size), which the native menu bar already refuses (#1354). The modal
gate moves to menus::modal_allows and ui.menu.invoke checks it; view navigation
stays live, engine.execute is unaffected. Fixes #1556.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:49:56 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 c017b119ff Gradient tool: the options-bar swatch opens the Gradients presets (#1681)
Clicking the gradient swatch in the options bar did nothing; as in Photoshop it now opens a
popup with the Gradients preset browser, so a preset is picked without opening Window › Gradients.
Fixes #1651.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:17:21 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 681e7f3b89 Layers panel: right-click opens the fx and adjustment footer menus (#1680)
As in Photoshop, a secondary click on the Layers footer's Add a layer style and Create new fill or
adjustment layer buttons opens their menus too (a primary click still toggles them), and a small
arrow marks them as menu buttons. Fixes #1579.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:17:02 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 efa62efa0c Liquify: test that Alt-reversed strokes survive replay, undo and redo (#1675)
Follow-up to #1494: Alt captured at stroke-down records the reversed tool, a
later stroke records the selected one, and the field rebuilt from the strokes
matches after undo and redo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:11:07 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 616f206c5d Filter previews: only for the active document, and computable without a GPU (#1676)
A command dialog's preview is never shown in another tab, and the preview is computed even without a GPU texture, so headless tests can check it (90° Arbitrary rotation, Preview off, document untouched).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:09:45 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 864d9ce991 Selection outline: trace serially while background jobs run (#1690)
A long filter could occupy every worker in Rayon's global pool, and the marching-ants trace on
the UI thread then waited for those workers, so the job's Cancel control froze. While jobs run
the outline is traced serially; a test checks it matches the parallel trace. Fixes #1554.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:04:35 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 351736ef6b docs(mcp): grant scoped file roots in headless registration examples (#1673)
The PHOTOCRAFT_AUTOMATION_*_ROOT variables are read only by the desktop app, so
headless photocraft-cli mcp/serve need the --automation-*-root flags. Say so,
and pass explicit roots in the .mcp.json and claude mcp add examples.
Fixes #1551.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:03:58 -04:00
Nigel Ihza FirdausyandClaude Opus 5.5 6b1075e240 docs(control): align method signatures with accepted parameters (#1674)
ui.set gains rotation, gradientBlendMode and gradientClassic in its signature
and the stale duplicate ui.set line goes; ui.menu.invoke documents params,
ui.pointer tool, app.save an optional path, and serve's doc.save tiffLayers.
Fixes #1550.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:03:30 -04:00
Nigel Ihza Firdausy d80455ad25 feat(ui): edit independent rectangle corner radii with narrow-panel controls and history tests 2026-10-09 11:12:49 +07:00
Nigel Ihza Firdausy ce35c1455b fix(pen): reshape the final outgoing handle while preserving incoming geometry and linkage 2026-10-09 11:12:49 +07:00
Nigel Ihza Firdausy 0d058f4c29 fix(psd): write Photoshop-compatible Photo Filter adjustment blocks (#1473)
* fix(psd): emit Photo Filter phfl block with required four-byte alignment (#1455)

* test(psd): golden phfl length, padding, legacy compatibility and Photo Filter variants

* test(psd): compare quantized Photo Filter channels with encoding tolerance

* fix(psd): normalize legacy 18-byte Photo Filter blocks on re-save

* test(psd): full exporter/importer roundtrip and repair of legacy invalid phfl blocks

* style(psd): format Photo Filter golden-block regression test

* style(psd): apply rustfmt layout to exporter compatibility tests
2026-10-09 09:01:57 +07:00
Nigel Ihza Firdausy d3632f361b style: remove extra blank line after deleted helper 2026-10-09 06:20:20 +07:00
Nigel Ihza Firdausy 38257f0209 style: apply rustfmt layout for multi-layer drag handler 2026-10-09 06:20:20 +07:00
Nigel Ihza Firdausy 408975423c test(ui): verify multi-selected group drop payload and single-layer fallback 2026-10-09 06:20:20 +07:00
Nigel Ihza Firdausy bc2b0a3ca6 test(engine): batch group move, stable stacking, undo/redo, nested selections and atomic rejection 2026-10-09 06:20:20 +07:00
Nigel Ihza Firdausy aec1aa1980 refactor(engine): share cycle checks through batch move implementation 2026-10-09 06:20:20 +07:00