12 Commits
Author SHA1 Message Date
cc772ad9dc Saving onto a dangling symlink writes through to its target (#6605) (#6649)
* fix(format): saving onto a dangling symlink writes through to its target (#6605)

resolve_symlink fell back to the link path when canonicalize failed, so an
atomic save renamed over the symlink and replaced it with a regular file.
Dangling chains now resolve lexically (relative targets included, with a
depth cap), so the bytes land at the target and the link survives; a
symlink loop is a clear error instead of a silent link replacement.

* Follow dangling links without folding .. by text

A folder on the way may itself be a symlink, so leave .. for the OS to resolve.

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

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 03:35:35 -04:00
mroxsoandhighperfocused 57d488032c fix(doc): variable definitions follow layer id remapping on copy and load (#5023)
* fix(doc): variable definitions follow layer id remapping on copy and load

A duplicated document (image.duplicate, history.newDocument) or a .pcraft
loaded with remapped ids assigned fresh layer ids but cloned VariableDef.layer
unchanged, so datasets silently drove no layer (or, after remap, a colliding
one). Remap variable targets through the same id map as layer comps and
slices; a dangling target keeps a non-colliding placeholder. A merged-only
duplicate drops the whole variable binding set, matching how it drops comps.

Closes #4969

* style: cargo fmt duplicate_variables test

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-14 15:02:43 -07:00
mroxsoandhighperfocused a076f617e0 fix(ui): give the Quick Selection tool its own icon (#5116) (#5184)
Quick Selection reused the Ellipse Marquee's dashed circle. It now has a
distinct glyph: Lucide's brush with a dashed selection arc around the
bristles (Photoshop's Quick Selection mark). A rasterized test asserts
the two icons read as distinct and that the arc actually renders.

Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-14 13:07:48 -04:00
mroxsoandhighperfocused b37a148759 ui: remove the title-bar Discord button (#1737)
Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-14 13:05:47 -04:00
mroxsoandhighperfocused 26e003059b fix(ui): a save name without an extension gets the suggested one (#5039) (#5049)
Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-13 12:01:47 -04:00
mroxsoandhighperfocused 41e578b4d3 fix(io): a version 3 Photo Filter colour is XYZ relative to the D50 white (#4987)
Photoshop CS2 stores the phfl version 3 colour as XYZ divided by the D50
white point (X / 0.9642, Y, Z / 0.8249), so white is (32768, 32768, 32768).
We read those values as absolute D50 XYZ, giving every CS2 Photo Filter a
slightly wrong colour: the Yellow filter preset (24191, 25002, 3827) decoded
to #FFE03A instead of #F9E31C, and Warming Filter (85) (15548, 12029, 0) to
#F18600 instead of #EC8A00. Multiply by the white point before converting;
decoded this way, every version 3 colour in the reporter's CS2 files is
exactly a Photoshop preset (#4940).

Fixes #4940

Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-13 11:56:40 -04:00
cee010ef5a Replace Color: a canvas eyedropper picks the From colour (#4143) (#4247)
The dialog had no colour control at all: the engine defaulted the From
colour to the foreground and the spec's `color` param rendered nothing.
It now shows a Color row (swatch opens the Color Picker, seeded from the
foreground) plus a pipette that arms a canvas eyedropper: the next click
on the image samples the merged composite into the field, like the
Curves picker and Photoshop's Replace Color.

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:03:15 -04:00
mroxsoandhighperfocused 23abc4302b fix(ui): default the Move tool Auto-Select target to Group (#4056) (#4093)
Photoshop ships with Auto-Select on and its target set to Group;
PhotoCraft defaulted to Layer. ToolOptions::default and the serde
default now return "group"; saved preferences keep their stored value.

Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-12 18:43:21 -04:00
mroxsoandhighperfocused d9e6cee908 Tests: build save-dialog expectations with Path::join so they pass on Windows (#2745) (#2853)
Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-10 17:57:41 -04:00
4db47f2bcc Esc deselects when no active operation needs cancelling (#2765)
* Esc deselects when no active operation needs cancelling (#2674)

* Esc: keep the selection while a canvas drag is in progress

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

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:36:44 -04:00
mroxsoandhighperfocused d59d2b509f Channels: tint single colour channel views on the canvas with Show Channels in Color (#2385) (#2574)
Interface > Show Channels in Color only reached the Channels panel
thumbnails (#1986); the canvas overlay hard-coded in_color = false, so an
isolated RGB/CMYK channel stayed gray while its thumbnail changed color.
channel_view::ensure now reads the preference (the view cache key already
carries the flag, so toggling rebuilds the texture), and a hidden Lab
Lightness goes neutral instead of black under the preference, so an
isolated a/b channel tints over mid gray like Photoshop. Lightness itself
stays grayscale (Adobe's exception).

Verified with the offscreen snapshot example: RGB green canvas goes
(128,128,128) -> (0,128,0) and CMYK cyan -> (0,176,232) when the
preference is on; unit tests cover render(), the Lab exception and the
cache key.

Co-authored-by: highperfocused <highperfocused@pm.me>
2026-10-10 14:02:38 -04:00
16ad6e1079 Add font previews and keyboard cycling to font menus (#1746)
Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-09 21:43:59 -04:00