61 Commits

Author SHA1 Message Date
aiongg 6d737c219e docs: tidy agent, contributor and release docs 2026-10-31 04:17:45 +00:00
David Mann d9cb0edd8d Liquify: no Twirl Counterclockwise tool, Alt with Twirl Clockwise does it
The reference application's Liquify has Twirl Clockwise only; holding
Alt (Option on macOS) while using it twirls counterclockwise. PhotoCraft
had an extra Twirl Counterclockwise tool on the strip, and #9598 had just
given it a mirrored icon.

Twirl Counterclockwise leaves the tool strip (LiquifyTool::ALL, 11 to
10 tools) and the rotate-ccw icon #9598 added goes with it (asset,
icon_data entry, ATTRIBUTION count back to 113). Alt already reversed
Twirl Clockwise at stroke-down; that stays. The TwirlCcw variant stays as
a stroke's direction: Alt-strokes record `twirlCcw`, and filter.liquify
strokes and older saved strokes naming it still replay as Twirl
Clockwise reversed (the command doc says so). Selecting it as the tool
(control channel `tool: "twirlCcw"`, or a preference saved by an older
version) is refused with an error that says to choose twirlCw and hold
Alt; the rest of the settings still apply and the dialog opens.

Tests: the_tool_strip_has_no_twirl_counterclockwise (no such tool, label
or missing icon on the strip, C is Twirl Clockwise, `twirlCcw` as the
tool is an Err and leaves the tool, an old saved preference naming it
opens); alt_with_twirl_clockwise_twirls_counterclockwise (the field
displacement a dab leaves right of the centre points up without Alt and
down, mirrored, with Alt). twirl_counterclockwise_has_its_own_mirrored_icon
and the twirlCcw row of alt_reverses_twirl_and_swaps_pucker_and_bloat
are removed. The i18n labels for Twirl Counterclockwise stay (the
variant's label).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:34:03 +01:00
FredApps 7f1f867b1d Merge approved PhotoCraft PR #9598: fix(ui): Liquify's Twirl Counterclockwise gets a mirrored twirl icon
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Preserve current attribution exclusions and credit the mirrored Lucide Twirl Counterclockwise icon; keep both Liquify regressions.
2026-10-29 04:21:48 +01:00
FredApps 86fcbf26ee Merge approved PhotoCraft PR #7648: Pen: Path Operations menu, as in Photoshop (#1397) 2026-10-29 03:29:31 +01:00
FredApps e7e6ff1bf2 Merge approved PhotoCraft PR #4547: fix(icons): PhotoCraft icon corner radius matches other Craft apps (#4478) 2026-10-29 03:28:34 +01:00
Khawar Zamman Wani 8b835a103c Merge origin/main into pen-path-operations 2026-10-28 09:22:55 +05:30
Hector Villarreal 47aea89e92 fix(ui): Liquify's Twirl Counterclockwise gets a mirrored twirl icon (#9547)
Twirl Counterclockwise used Lucide's undo-2 arrow, which reads as Undo.
It now uses rotate-ccw, Twirl Clockwise's rotate-cw mirrored (path data
mirrored about x = 12), registered in icon_data and listed in
ATTRIBUTION.md with the other Lucide icons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:39:43 -06:00
D-monized 0056b44a6f Merge origin/main into feature/font-recent-favorites
main now has its own Recent Fonts (#3559), so this branch drops its separate
recent list (prefs `recentFontList`, `type.font.noteRecent`, `type.font.clearRecent`,
the five-font cap) and its font menu uses main's: the list is
`PhotocraftApp::note_recent_font`'s, the rows and arrow keys main's `font_menu_rows`,
and every applied family is recorded, as main does. Favourites, the class and
similar/variable filters, the style expander, format badges and the live preview
stay. Faux Bold keeps the one-pass thickened outline on main's fallible coverage
buffers (#6001), budgeted as a plain pass. Both sides' strings and icon credits are kept;
the scorecard is regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:28:05 -06:00
Khawar Zamman Wani ac3d172acd Merge remote-tracking branch 'origin/main' into pen-path-operations
# Conflicts:
#	ATTRIBUTION.md
#	docs/ui-parity.md
2026-10-22 22:36:15 +05:30
aiongg 1f0177f23c Integration: track t04
# Conflicts:
#	ATTRIBUTION.md
#	contributors/people.toml
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	docs/gaps.md
2026-10-22 15:38:17 +00:00
D-monized 1b2e5b5536 Merge origin/main into feature/font-recent-favorites
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:43:10 -06:00
aiongg 809ecfcfa3 Merge PR #5951: Type: Create warped text button in the options bar, as in the reference app (#5911)
ATTRIBUTION.md: main's icon-exceptions row with the PR's text-warp.svg added to its list.
2026-10-22 02:23:35 +00:00
aiongg 567a0de150 Merge PR #3532: Move tool: show all six Align icons and an icon menu for Distribute (#3490)
ATTRIBUTION.md: the Lucide row is ours plus `align-edge-*.svg` and `distribute-*.svg` from the PR.
2026-10-22 02:23:23 +00:00
Khawar Zamman Wani 85a1c26950 Merge remote-tracking branch 'origin/main' into pen-path-operations
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 06:28:28 +05:30
Korg1ch 5634b5155a fix(ui-egui): Slice Select gets its own icon, the Slice knife with a pointer (#7859) 2026-10-20 15:50:02 +03:00
D-monized ca1d9da607 Merge remote-tracking branch 'origin/main' into pr6526-merge
# Conflicts:
#	ATTRIBUTION.md
#	crates/text/src/tests.rs
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	crates/ui-egui/src/type_font_picker_tests.rs
#	crates/ui-egui/src/type_tool.rs
#	docs/scorecard.md
2026-10-19 05:09:06 -06:00
Khawar Zamman Wani 750c67e20e Pen: Path Operations menu, as in Photoshop (#1397)
The Pen, Freeform Pen and Curvature Pen options bar gets Photoshop's Path
Operations button: New Layer (Shape mode), Combine Shapes, Subtract Front
Shape, Intersect Shape Areas, Exclude Overlapping Shapes and Merge Shape
Components. In Shape mode a new path is added to the selected shape layer
with the chosen operation, so a hole can be cut in a shape (#7533); in Path
mode the operation applies to the subpath added to a targeted vector mask,
the selected path or the Work Path.

- engine: path.mergeShapeComponents for work, saved and layer paths, sharing
  bake_path with the shape-layer command
- control/MCP: ui.set penMode and penPathOp
- original path-op icons, tooltip translated in every catalog

Closes #1397. Closes #7533.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:30:53 +05:30
vangel76 f3a727cc94 Merge upstream/main into fix/3490-move-align-bar
Resolve ATTRIBUTION.md: keep main's ellipse-dashed.svg entries and this PR's align-edge/distribute icon entries.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 02:28:09 +02:00
Michael Higgins a914c58a63 fix(ui): wide ellipse for Elliptical Marquee, dashed C + brush for Quick Selection (#6875) (#6917)
The Elliptical Marquee used a dashed circle and Quick Selection a brush
inside a dashed circular arc, so the two read alike. Photoshop draws the
marquee as a horizontal ellipse and Quick Selection as a brush in an
upright dashed rounded C. New original ellipse-dashed.svg; quick-selection.svg
redrawn; tests check the ellipse is wider than tall and the C runs down
the left edge.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 03:34:42 -04:00
D-monized e11e296944 Font menu: favourites, recent fonts, class and similar-font filters, live preview
The font menu gains Photoshop's local font library and filters (everything but
Adobe Fonts):

- Favourites: a star on every row (never picks the font) and a favourites-only
  filter. Kept in the preferences (type.favoriteFonts).
- Recent fonts: up to five, most recent first, above a divider. Wires the dead
  Preferences > Type > Recent Fonts (now 0..5, default 5; older saved values
  are capped on load).
- Class list: All Classes, Serif, Slab Serif, Sans Serif, Script, Monospace,
  Handwritten, Blackletter, Decorative, read from each font's OS/2
  sFamilyClass, PANOSE and post tables (photocraft_text::font_class), else its
  name. Fonts that state nothing stay unclassified.
- Show Variable Fonts, Show Similar Fonts (PANOSE/class/weight distance to the
  current font) and Clear All; the class list collapses with «.
- Each row: style expander (">") that picks a family in a given style, and an
  O / TT / VAR format badge.
- A preview at the top sets the type layer's own text (or the selection being
  typed) in the font under the pointer.
- The search field and filters stay fixed while only the list scrolls, and the
  search field lines up with the font list.

Commands: type.font.favorite, type.font.noteRecent, type.font.clearRecent,
type.font.library. Two original icons (font-similar, font-variable).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 02:18:41 -06:00
Mara Johnson a08f7a2295 Merge remote-tracking branch 'origin/main' into fix/4478-icon-corner-radius 2026-10-15 14:56:43 -04:00
Fazmin a4b1e2a607 Type: Create warped text button in the options bar, as in Photoshop (#5911)
The Type tool's options bar now has Photoshop's Create warped text button
after the text colour swatch. It opens the same Warp Text dialog as
Type > Warp Text... for the selected type layers (or the layer being
edited), and is greyed when there is no type layer to warp.

- assets/icons/text-warp.svg: original glyph (a T above a bowed arc) on
  Lucide's 24 px grid and stroke; listed in ATTRIBUTION.md.
- The tooltip is translated in all 20 catalogs, reusing each language's
  existing "Create Warped Text" wording (new for Russian).

Test: options_bar_warp_button_warps_the_selected_type_layers.

Co-Authored-By: Claude
2026-10-14 23:09:05 -04:00
vangel76 9303c10bb6 Merge upstream/main into fix/3490-move-align-bar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:44:23 +02:00
mroxso 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
vangel76 2b3df66ff0 Merge remote-tracking branch 'upstream/main' into m/x
# Conflicts:
#	ATTRIBUTION.md
2026-10-13 21:55:40 +02:00
Vikram Bhaskaran 24930b3188 Draw adjustment thumbnails as a half-filled circle. (#4628)
The contrast glyph put a smaller semicircle inside the ring, which read as concentric rings on a layer thumbnail. It is now one circle with a filled half. (#4561)

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-13 00:13:39 -04:00
Korg1ch 4122bc6918 fix(ui): use a linear gradient ramp glyph for the Gradient tool icon (#4172) (#4335)
* fix(ui): use a linear gradient ramp glyph for the Gradient tool icon (#4172)

* test(ui): verify Gradient tool icon rasterizes a left-to-right ramp (#4172)
2026-10-12 20:56:09 -04:00
Mara Johnson d84ad4c22f fix(packaging): Windows and Linux icons keep the rounded-square corners (#4478) 2026-10-12 19:02:42 -04:00
vangel76 958af5f518 Merge upstream/main into fix/3490-move-align-bar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:54:58 +02:00
Roaldo Sulejmani 5025fd2895 Color Sampler tool (#1046) (#2753)
* Color Sampler tool (#1046)

Add the Color Sampler to the Eyedropper (I) flyout: click to pin numbered
sample points to canvas pixels, drag to move, Alt-click (or drag off-canvas)
to remove, Clear All in the options bar, at most 10 per document. Each point
reads one composite pixel (RGB 0-255 and naive device CMYK, same as the
pointer readout) and the Info panel lists them under their number with the
position in the ruler unit; the readouts refresh when the document changes.

Points are per-document engine view state (DocState.color_samplers): they
belong to their document, are discarded on close, and are not saved to
.pcraft or PSD. They are driven by commands shared by the UI, CLI, control
channel and MCP (view.colorSamplers.add/.move/.delete/.clear/.list); the
mutations change no pixels and add no History step.

Points follow the canvas geometry (crop, Canvas Size, Image Size, Image
rotation/flip, rotated crop) through a pending Session.sampler_map that
Session::edit applies before culling the points that land outside the new
canvas; the count removed is reported in the status bar.

- engine: new sampler_cmds.rs (commands + shared readout helpers) and the
  sampler_map wiring in lib.rs, image_cmds.rs, mode_cmds.rs, commands.rs,
  analysis_cmds.rs (compound rolls points back on error).
- ui-egui: Tool::ColorSampler + original sampler.svg icon, toolbar slot,
  Info panel list, options bar, pointer handling, overlay markers, cursor.
- docs/scorecard.md regenerated (TOOL-1649-1 no longer says the tool is
  missing). No menu item, so parity is unchanged (627/627).

* Color Sampler follow-up: tool count, README, i18n, roadmap

The full ui-egui suite (not just the filtered tests) surfaced these gaps:

- state.rs: Tool::ALL.len() assertion 52 -> 53.
- README.md: "52 tools" -> "53 tools" and add Color Sampler to the list,
  so readme_tool_list_matches_tool_all passes.
- i18n: the options-bar hint, "{n} of {max} sample points" and
  "No document open" need entries in every catalog that claims complete
  menus (every_tl_literal_is_translated); added to all 16 (coverage
  1584/1584). The translations were drafted alongside the tool.
- docs/roadmap.md: drop Color Sampler from the missing-tools list and note
  it landed.

* Color Sampler: fit main's new tools and Image Size resample type

Reset Tool covers ColorSampler, the test ViewXform sets aspect, and
Image Size checks Method::None instead of an Option.

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 11:43:09 -04:00
vangel76 8607654fa7 Merge upstream/main into fix/3490-move-align-bar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:33:05 +02:00
Sir Starch d8d8d9f3d2 Tools: Convert Point tool in the Pen flyout (#3204) (#3211)
Convert Point is a tool of its own, built on the existing path.convertPoint
edit that Pen + Alt already drives. A click makes a smooth anchor a corner, a
drag from an anchor pulls out smooth handles, a drag on a handle moves it
alone. The tool selects the anchor it converts so its handles show; a press on
empty canvas or a segment does nothing, and Cmd/Ctrl is Direct Selection, as
with the Pen. It shares the Pen's key (P).

Original glyph with an ATTRIBUTION.md row; tool name in all catalogs; README
tool list and the G6 / ui-parity / roadmap counts updated (tools 56 of 68, the
earlier 53 was stale).

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:09:38 -04:00
vangel76 0a75c37c7a Move tool: show all six Align icons and an icon menu for Distribute (#3490)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:44:26 +02:00
MathMattTech ade5799165 Mirror the shell for right-to-left UI languages (DIST-220-4) (#3114)
* Mirror the shell for right-to-left UI languages (DIST-220-4)

Arabic, Hebrew, Persian and Urdu read right to left, but the window's
chrome was hard-wired left to right, so an RTL catalog (#2505) would
put Arabic labels into a left-to-right layout.

The reading direction now follows the language code
(i18n::dir_for_code: ar, ckb, dv, fa, he, ps, sd, ug, ur, yi), so a
catalog needs no extra setting and the LANGUAGES rows are unchanged.
In a right-to-left language the shell mirrors:

- the menu bar starts at the right with File; menus open under their
  title's right edge, and the arrow keys step through them in screen
  order
- the toolbox docks on the right, the panel dock and its rail on the
  left; tool flyouts open towards the canvas and their corner marks
  and the header chevrons follow
- the title, options and status bars read from the right, with the
  workspace controls and the Fit/progress group at the left end; the
  title and window-drag gap use the free span between them

New module direction.rs holds the helpers (start_panel/end_panel,
row/trailing layouts, horizontal_centered, the popup alignment). Left
to right, every change lays out as before.

No RTL catalog ships yet, so PHOTOCRAFT_UI_DIRECTION=rtl forces the
mirrored layout for review (read once, native only). Panel interiors,
dialogs, menu rows, submenus and document tabs stay left to right, and
egui still draws UI labels without Arabic shaping or bidi reordering;
docs/localization.md and the parity docs say so.

Adds Lucide's chevron-left (the status bar's info menu arrow points
left in RTL; Timeline's Previous frame button asked for it too and drew
the fallback square).

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

* Localization docs: egui shapes Arabic words; what's missing is a font and bidi word order

egui 0.36 shapes right-to-left runs with HarfRust, so letters join within
a word. The gaps are an Arabic fallback font in the UI and paragraph bidi
(words are placed left to right; emilk/egui#8576, #8577).

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

---------

Co-authored-by: MathMattTech <334629259+MathMattTech@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:06:18 -04:00
Valentin Klein 42e4af6416 Tools: Frame tool (#2914) (#2915)
The Frame tool (K, its own toolbar slot after the Crop group) draws a
rectangular or elliptical frame; the options bar picks the shape, Shift
constrains to a square or circle and Alt draws from the centre. A frame
drawn over the selected pixel layer's content takes that layer in;
otherwise it is an empty frame.

Frames are built as Frame from Layers builds them: a group clipped by a
layer mask (rectangular or anti-aliased elliptical). The new engine
command layer.new.frame does it in one Session::edit, so one undo step.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 08:20:15 -04:00
Valentin Klein 017d1aea1c Tools: Perspective Crop (#1043) (#2864)
New Perspective Crop tool in the Crop flyout (C): drag a box or click four
corners, drag corners independently onto a skewed object, perspective grid
(Show Grid), Enter commits, Esc cancels. W/H/Resolution come from the Crop
tool's fields (Front Image, Clear), or the quad's mean side lengths.

Engine: image.perspectiveCrop {corners, width?, height?, resolution?,
interpolation?} maps the quad onto an upright canvas through the
quad-to-rectangle homography (Heckbert's square-to-quad map, inverted):
every raster layer, mask, alpha channel and the Quick Mask, one undo step.
The Background stays opaque over the background colour; type, shape, fill
and smart object layers are rasterized first; vector geometry follows the
nearest affine map. Non-convex, self-crossing, counter-clockwise or
degenerate quads are refused with an error.

algo: warp_surface_clipped bounds the per-tile warp by the new canvas even
when a layer reaches past the map's horizon; convex_quad, quad_size,
quad_to_rect.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:28:11 -04:00
Valentin Klein 2e0c426692 Tools: Artboard tool (#2858) (#2862)
Photoshop's Artboard tool in the Move flyout (V, ⇧V cycles Move ↔ Artboard):
dragging on an empty spot draws a new board (`layer.new.artboard`), clicking a
board selects it, dragging it moves it with its contents, and the selected
board's edge and corner handles resize it (`layer.artboard.set`,
moveContents: false). Each gesture is one undo step. Options bar: Size preset,
W/H, Make Portrait, Make Landscape, Add New Artboard. Original toolbar glyph
(assets/icons/artboard.svg, ATTRIBUTION.md row).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 07:12:53 -04:00
Valentin Klein 5866cd2cf6 Tools: Horizontal and Vertical Type Mask (#1055) (#2861)
The Type flyout (T, Shift+T cycle) gains Vertical Type Mask and Horizontal
Type Mask, in Photoshop's order. They type like the Type tools, with the
canvas under Quick Mask's colour and the glyphs clear while editing. On
commit the glyph coverage replaces the selection; no layer is left behind
and the whole session is one "Type Mask" history step. Escape always
cancels and leaves the selection unchanged.

The commit runs a new engine command, type.createSelection: a type layer's
glyphs (optionally removing that layer, the tools' hidden working text) or
type.create's keys directly, with new/add/subtract/intersect and an
anti-aliasing override. type.create's layer building is shared through
type_cmds::new_text_layer.

Original dashed-T icons (ATTRIBUTION.md), tool label translations in every
catalog, README tool list and the gaps/parity docs updated.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 07:06:10 -04:00
Valentin Klein c436aa7294 Tools: Freeform Pen and Curvature Pen (#1054) (#2856)
The Pen flyout (P / Shift+P) now holds Pen, Freeform Pen and Curvature Pen.

Freeform Pen: a drag draws freehand; on release the trace is fitted with
cubic Beziers within the options bar's Curve Fit (default 2 px) by
photocraft_vector::trace::fit_trace (Schneider, "An Algorithm for
Automatically Fitting Digitized Curves", Graphics Gems 1990, the fitting
Make Work Path already uses), keeping turns sharper than 60 degrees as
corners. Releasing on the start point closes the path.

Curvature Pen: each click adds a smooth point and the path runs through
every point (Catmull-Rom style handles from
photocraft_vector::edit::curvature_knots); dragging a point moves it,
double-clicking toggles corner/smooth, clicking the first point closes,
Enter/Esc finish and Backspace removes the last point, as with the Pen.

Both commit through the Pen's path: a shape layer in Shape mode, else the
targeted vector mask, else the work path, as one History step.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 06:56:44 -04:00
Fabio Lussi d96bb50161 Add Anchor Point and Delete Anchor Point tools (#1044) (#2756)
Paths could be reshaped after drawing since #865, but the number of
anchors could not change. Two new engine commands do that, and two tools
in the Pen flyout drive them on the canvas.

- photocraft_vector::edit::add_anchor splits the segment at t with de
  Casteljau, so a curve keeps its shape and gets a smooth anchor; a
  straight segment gets a corner on the line. delete_anchor joins the
  neighbours keeping their handles, drops an emptied subpath and refuses
  to remove a path's only anchor.
- path.addAnchor / path.deleteAnchor take the same targets as the other
  point edits (work path, saved path by name, shape layer path, vector
  mask) and record one history step each.
- Tool::AddAnchorPoint / DeleteAnchorPoint join the P group after the Pen.
  They act on Direct Selection's paths, highlight the segment or anchor
  under the pointer, show a +/- cursor badge, swap with Alt and turn into
  Direct Selection with Cmd/Ctrl. An edit clears a Direct Selection on the
  same path so it never points at a shifted anchor.
- Icons derived from Lucide pen-tool (ATTRIBUTION.md), hints and tool
  names in all 16 catalogs, README tool list, roadmap.

Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 05:48:07 -04:00
Fabio Lussi 073e1cc1d2 Add the Color Replacement tool to the Brush toolbar group (#1045) (#2719)
The engine already had paint.colorReplacement, but the desktop UI had no
tool for it: no Tool entry, no toolbar slot, no options bar and no stroke
routing, so only agents and the CLI could use it.

Tool::ColorReplacement joins the Brush (B) flyout after the Mixer Brush,
so B / Shift+B cycle to it and ui.set / ui.inspect name it. Its options bar
has Mode, Sampling (Continuous / Once / Background Swatch), Limits,
Tolerance and Anti-alias, all mapped to the command's params, with the
foreground as the replacement colour. Strokes are drawn live through
LiveDab, which can now preview the stroke's finishing dab, so the preview
at the last move matches the commit. The command refuses a mask, channel
or Quick Mask target instead of silently recolouring the layer. New icon
derived from Lucide's brush; label translated in all 16 catalogs.

Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 05:20:00 -04:00
Luniiya e44d96e1d4 Favorite themes from the appearance button's right-click menu (#2445)
* feat(ui): favorite themes from the appearance button's context menu

Hovering a theme in Preferences > Interface shows a star; clicking it saves
the theme to interface.favoriteThemes (filled accent star once favorited).
Right-clicking the title-bar appearance button opens a menu with Appearance
Settings, then the favorite light themes and the favorite dark themes.

* Regenerate the scorecard for the favorite themes pref

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 03:09:47 -04:00
Valentin Klein 325c5b3194 Tools: Single Row Marquee and Single Column Marquee (#2837) (#2848)
Both tools join the Marquee flyout (no key, outside the M / Shift+M cycle,
as in Photoshop). A click selects the one-pixel row (column) under the
pointer across the whole canvas; a press-drag moves the line until the
release. The selection goes through select.rect, so Shift adds, Alt
subtracts, Shift+Alt intersects and the options-bar modes apply; Feather
applies, Anti-alias and Style/Width/Height are greyed. Selection-mode
cursor badges, the selection right-click menu and snapping cover them.
Original row/column glyphs with an ATTRIBUTION.md row; tool names in all
catalogs; README tool list and the G6 / ui-parity rows updated.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 19:10:34 -04:00
JohnnYoru 897f9f37ad Add the Remove tool with non-local patch completion (#2193)
* Add the Remove tool with non-local patch completion

Tool::Remove (J flyout) paints a hole that paint.remove fills from its
surroundings as a background job, in one undo step. A stroke around an
object also removes what it encloses; the selection limits the hole.

The fill (photocraft_algo::nonlocal) follows Newson, Almansa, Gousseau and
Pérez (IPOL 2017): texture features in the patch distance, onion-peel
initialisation, match fields upsampled between levels and a best-patch
final copy, plus gradient-domain hiding of edge and interior seams.
Sparse holes are filled in tiles. inpaint::complete (Content-Aware Fill,
Spot Healing, Content-Aware Move) is unchanged.

On 20 holes in public-domain photos (remove_quality) the fill keeps 0.89
of the texture it replaces, against 0.54 with the Wexler/PatchMatch
completion. 24 MP (remove_bench): 100 px ring 120 ms, 1000 px ring 2.5 s.

* Translate the Remove tool's label and tighten comments

Adds "Remove Tool" to every UI catalog, following each one's pattern for
tool names and its existing word for Remove. Corrects comments that no
longer matched the code: the benchmark's description, the tile and margin
minimums, and the reasons given for two parameters.

* Put the Remove tool first in the J group

The J flyout's slot now shows Remove until another J tool is picked, and
J from another group selects it; repeated J goes on to Spot Healing Brush,
Healing Brush, Patch, Content-Aware Move and Red Eye.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 05:19:11 -04:00
Luniiya 8c3b6f8e5f Appearance modes: sync with system, separate dark and light themes (#1981)
* Appearance modes: sync with system, separate dark and light themes

* Appearance button cycles Auto, Light, Dark

* Appearance: default stays Dark, follow the portal signal instead of polling, split out the Preferences resize

* Preferences: size both appearance cards for the five dark themes

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-09 21:34:49 -04:00
Shayan Horbank a342ab8820 Add centered and transformable painting symmetry axes (#1821)
Photoshop's Vertical, Horizontal, Dual Axis and Diagonal paint symmetry presets beside the
path-based symmetry: axes start at the document centre, Transform Symmetry moves and rotates
them (Shift snaps to 15°, Enter applies, Esc cancels), and all copies merge into one coverage
pass so overlaps don't build opacity. New paint.setSymmetry command and preset icons. Fixes #1648.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:44:24 -04:00
Unemars 0d10f11733 Toolbar colour chips, Color Picker and panel footers, as in Photoshop (#1729)
The colour chips sit centred in the toolbar with Default Colors and Switch Colors above them. The Color Picker gains L/a/b radios and the out-of-gamut and web-colour warnings, each with a click to take the nearest colour. Panel footers are a full-width bar with their own mask and adjustment glyphs, keeping the right-click footer menus from #1680.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:03:37 -04:00
Unemars a273d3b27d Layers panel: ⌥-click on the line between layers, measured against Photoshop (#1642)
#1066 added the gesture; this makes it behave as Photoshop 25.4 does (measured by sweeping
⌥-hover and ⌥-click a pixel at a time across each line, results read back by script):

- the line takes the pointer from 3 px above it to 5 px below it (was ±3);
- a release frees the layer above the line together with the clipped layers stacked above it,
  in one history step, and makes it the active layer (was: that layer alone); a clip keeps the
  active layer, as before;
- clipping and releasing have their own cursors: PhotoCraft's original clip-below (a hooked
  arrow onto the layer below) and clip-release (the same arrow with a small "no" badge),
  replacing Lucide corner-right-down for both.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 16:05:52 +07:00
BFlat 938c079d68 Layers panel: Alt-click the line between two layers clips the upper one (#967) (#1066)
Holding Alt (Option) over the line between two layers in the Layers panel
shows the clip cursor; a click runs Layer > Create Clipping Mask on the upper
layer, or Release Clipping Mask when it is clipped already, as in Photoshop.
The line only counts between siblings that are next to each other in the
stack (not inside an open group or across filtered-out rows), and stays
clear of the eye column and the effects triangle. The cursor is the Lucide
corner-right-down icon.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 15:42:00 -04:00
BFlat 75ff41a26b Direct Selection tool: reshape pen and shape paths after they're drawn (#790) (#865)
* Direct Selection tool: reshape paths after they're drawn (#790)

Adds Photoshop's Direct Selection tool (A, in the Path Selection flyout) and
the Pen's Cmd/Ctrl (Direct Selection) and Alt (Convert Point) modifiers. It
selects and drags anchors, direction handles and segments of the work path,
the active shape layer's path and a targeted vector mask.

The geometry lives in photocraft_vector::edit; new engine commands
path.moveAnchors, path.moveHandle, path.bendSegment and path.convertPoint
apply it (one history step per drag, drivable over MCP and the control
channel), and the canvas previews a drag with the same code.

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

* Tool::ALL holds 49 tools now that Direct Selection and Magnetic Lasso both landed

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-07 22:58:31 -04:00