Commit Graph
41 Commits
Author SHA1 Message Date
Ufuk Efe fe04f6acaa docs: credit Ufuk Efe in people.toml (#3809) (#5056) 2026-10-13 11:46:50 -04:00
Ivan · VanTan a999ce4a77 i18n(ru): fix counted status agreement and font weight labels (#4787) 2026-10-13 02:49:34 -04:00
f5f0f73f25 feat(ui): Linux caption buttons follow the desktop layout, style and icon theme (#4439)
* feat: linux caption buttons follow the desktop: layout, style and icon theme

The title bar's minimize/maximize/close buttons now match the desktop:
- Layout and side: GNOME button-layout via the settings portal (live),
  KDE kdeglobals ButtonsOnLeft/Right, else gtk-3.0 settings.ini.
- Style family (GNOME round, Breeze chevrons, Windows) follows the
  desktop, then the GTK theme.
- Glyphs come from the icon theme's window-*-symbolic.svg (freedesktop
  lookup with Inherits and hicolor; unlisted theme directories such as
  symbolic/ui are searched lazily), rasterized with resvg at the real
  pixel size, recoloured and cached; missing icons fall back to the
  built-in vector glyphs. Override: PHOTOCRAFT_CAPTION_ICONS=<theme>|none.
- Theme and portal work runs on a worker thread (10-22 ms per theme).

* feat: linux caption buttons follow the desktop: layout, style and icon theme

The title bar's minimize/maximize/close buttons now match the desktop:
- Layout and side: GNOME button-layout via the settings portal (live),
  KDE kdeglobals ButtonsOnLeft/Right, else gtk-3.0 settings.ini.
- Style family (GNOME round, Breeze chevrons, Windows) follows the
  desktop, then the GTK theme.
- Glyphs come from the icon theme's window-*-symbolic.svg (freedesktop
  lookup with Inherits and hicolor; unlisted theme directories such as
  symbolic/ui are searched lazily), rasterized with resvg at the real
  pixel size, recoloured and cached; missing icons fall back to the
  built-in vector glyphs. Override: PHOTOCRAFT_CAPTION_ICONS=<theme>|none.
- Theme and portal work runs on a worker thread (10-22 ms per theme).

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 23:01:08 -04:00
c96842f7d9 The wheel steps the options bar's Mode dropdown for Brush, Pencil and Gradient (#2061) (#4447)
The options bar's Mode dropdowns now use widgets::dropdown_wheel_hovered, the wheel stepping the
Layers panel's Blend Mode already has (#1747): down picks the next mode, up the previous one, one
per notch, stopping at the ends, over the button or the open list. The Brush and Pencil list keeps
Photoshop's Clear after Dissolve. Adds my contributors/people.toml entry.


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

Co-authored-by: megaDon101 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-12 21:56:14 -04:00
465a663540 feat(ui): developer tile grid overlay (ui.set tileGrid) (#4217)
Draw the active layer's storage tiles over the canvas: a faint
TILE_SIZE grid over the document, every stored tile filled and
labelled tx,ty, tiles with identical pixels in one colour per group
and unique tiles in blue. It shows how sparse a layer is, which tiles
copy-on-write shares, and which ones .pcraft saves once.

It has no menu item and isn't a View > Show item. The control channel
and MCP turn it on with ui.set {"tileGrid": true}; ui.inspect reports
it as view.tile_grid, and View > Extras doesn't hide it.

Grouping hashes each distinct tile allocation once, in parallel (or
serially while jobs hold the pool, #1554), cached per document
revision and active layer. A 36 MP layer of all-different tiles groups
in 3.9 ms (8-bit) and 7.5 ms (16-bit) on 24 cores in release; the
ignored tile_grouping_36mp test reproduces this.

Also documents tileGrid in docs/control-protocol.md and
docs/development.md, and adds my contributors/people.toml entry.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-12 21:31:32 -04:00
003f9b4a18 Affinity import: pie shapes with huge angles no longer hang the reader (#4054)
* Affinity import: pie shapes with huge angles no longer hang the reader

The reader raised the end angle of a pie shape (`ShPi`) by `TAU` in a
loop until the end angle reached the start angle. Both angles come from
the file. The loop ran about `(AngS - AngE) / TAU` times, and it never
ended once adding `TAU` no longer changed the end angle: with
`AngS` = 1e308 and `AngE` = -1e308, reading the document did not return.

The reader now computes the pie's angles in constant time. The start
angle is `AngS` modulo `TAU`. The sweep is `AngE - AngS` capped at one
turn, or its remainder modulo `TAU` when `AngE` is below `AngS`. When
`AngE` is below `AngS` and that difference overflows to negative
infinity, the sweep comes from the remainders of the two angles.

The change and its two tests come from Riccardo Balloch's commit
79c5d673 in storytold/vectorcraft, which fixed the same loop in
VectorCraft's copy of this reader. Here the tests read the document on
a thread of their own and fail when reading takes more than 20 seconds.

Co-authored-by: Riccardo Balloch <[email protected]>

* Contributors: add dotCooCoo (Robert Lee)

---------

Co-authored-by: Riccardo Balloch <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 20:16:56 -04:00
2338492b12 Type: Space inserts a space while editing type (#4224)
* Type: Space inserts a space while editing type

Space was grabbing the Hand tool during canvas type edit even though
Photoshop keeps Space typing a space. Skip temporary-tool holds when
ui.text_edit is active.

* Credit @Hevarh1 with sync_github_name in About

* Format

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 20:11:42 -04:00
7366a0b429 Italian: native-speaker review of the UI catalog (#4290)
* Italian: native-speaker review of the UI catalog

Fix mistranslations: Trap (Trapping, was overprint), HDR Toning (Mappatura tonale, was chemical toning), Display Text (Mostra testo), Vine Color, Override Action "Save" Commands. Improve phrasing: Bevel & Emboss, East Asian / Middle Eastern Features, linked smart objects, Conditional Mode Change (metodo, as in Image > Mode), bit depth labels. Record the review and new glossary terms in the localization docs. Credit lucAmbr0 in people.toml.

* Tests: allow Italian's compact 8/16 bit labels, which match the source

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 19:38:55 -04:00
9f98df582a Stylus: a light Windows pen tap is pressure 0, not a full-pressure click (#4072)
winit's Windows pointer path reports pen pressure 0 as no force, so a tap
lighter than the pen's first pressure level arrived as a force-less touch.
Stylus::update ignored those, saw no pen, and the tap painted at 1 like a
mouse click.

Track touch contacts: a touch move outside a contact (hovering) or any
reported force marks a pen, and a pen contact with no force yet is pressure
0. A finger on a screen without force sensing still paints at 1. A contact
that starts in the frame another lifted now keeps its force too.

Also credit @Furash in contributors/people.toml.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-12 19:27:14 -04:00
e2a35b04fd Windows: keep full screen and maximized apart; never restore full screen (#4396)
* Windows: keep full screen and maximized apart; never restore full screen

winit on Windows keeps the maximized style on a window that goes full
screen from maximized. The shell then draws the taskbar black instead of
hiding it, and on the way back the window stays maximized at the
monitor's full size, over the taskbar (#3550, #4018, #4230). Un-maximize
before entering full screen and maximize again after leaving it.

eframe also restores full screen from ui.ron, as full screen and
maximized at once; drop that flag in the window builder hook, so
PhotoCraft starts in Standard Screen Mode as its own state says.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* contributors: add aweussom

* view_cmds: mark the winit#4756 workaround and how to retire it

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* rustfmt view_cmds.rs

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-12 19:05:37 -04:00
6037ade5ed Fix straight-path anchor insertion at the hit position (#3856)
Co-authored-by: jonathnwxh <[email protected]>
Co-authored-by: Codex <[email protected]>
2026-10-12 08:32:15 -04:00
304720b1a0 Gradient Map: edit color stops on the ramp (#3982)
* Gradient Map: edit color stops on the ramp (#2958)

* Gradient Map: keep stop removal scoped to the editor

* Gradient Map: fit selected stop controls in narrow Properties panels

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 06:30:14 -04:00
FredApps 62f4b9d453 Menus: keep clear of the taskbar when Wayland hides the window position (#3737) (#3739)
* test(ui): scaffold reported menu viewport coverage (#3737)

* chore: opt FredApps into public profile credit

* Menus keep clear of the taskbar when the window position is unknown (Wayland) (#3737)

On Wayland winit can't report a window's position, so egui has no
inner_rect/outer_rect, only the monitor size. work_area::visible_part
then returned the whole window, and fit_window never maximized an
oversized window, so long menus (Layer, View, Window...) were sized to
the full window and ran under a bottom panel. Without a position, cut
the window to the monitor size less the taskbar (the most that can be
visible wherever the window is), and use the content size as the
outer size for fit_window.

Replaces the scaffold test with one that fails on main.

---------

Signed-off-by: FredApps <[email protected]>
2026-10-12 04:33:54 -04:00
Atos LinsandBrandon Thomas da3da0942c Linux: StartupWMClass is the window's app id (#3481)
* Linux: StartupWMClass is the window's app id

The desktop entry said StartupWMClass=photocraft, but the window carries
the app id ai.storyteller.photocraft (APP_ID) on Wayland and under X11
alike, so nothing that matches a window to its launcher by that key ever
matched it. VectorCraft, GridCraft and WordCraft already use the app id.

A test reads the entry and compares it with APP_ID.

* Contributors: add atoslins (Atos Lins)

---------

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-12 03:28:09 -04:00
72bf3e13b6 i18n(cs): translate the dynamic labels the Czech catalog was missing (#3454)
* i18n(cs): translate dynamic labels the literal scanner can't see

The Czech catalog covered every enforced key (menus, tl! literals, blend
modes, preferences) but not the labels built at run time: filter and
Filter Gallery parameter names and choices humanized from their keys,
Camera Raw controls, the Flame / Picture Frame / Tree render filters,
measurement columns, file info fields and the macOS application menu.
Those showed in English under Czech.

Adds 853 entries (2,441 -> 3,294) for keys that the other catalogs
already translate and that still exist in the code. Existing entries are
unchanged. Translated clean-room from the plain meaning of the English
labels, with the terminology already used in cs.tsv; no proprietary
localisation was consulted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* i18n(cs): credit the Czech catalog in ATTRIBUTION.md and LICENSE-translations.txt

cs.tsv is a bundled translation asset but had no row in ATTRIBUTION.md
and was not named in LICENSE-translations.txt. Add both, worded like the
other catalogs (PhotoCraft contributors, original translations, MIT OR
Apache-2.0).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* i18n(cs): terminology review fixes for the new dynamic labels

Changes from an independent review pass over the added block:

- "Create By" (De-Interlace: how the missing lines are made) reads
  "Vytvořit pomocí", not "Vytvořil" (author)
- luminance is "jas" in Camera Raw, as in the existing "Luminance" label
  ("Detail jasu", "HSL – jas", "Jas")
- slider labels are nouns, like the existing "Redukce šumu" (Reduce Color
  Noise, Sharpen Details, Lighten Grout)
- grain and flame choices agree in gender with their neighbours; "Shadow"
  matches "Highlight"/"Světla" as "Stíny"; "At" is "Pozice"; "Water Paper"
  is "Mokrý papír"; "Leaves" is "listů" everywhere

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* contributors: add my own people.toml entry

Own entry only, as AGENTS.md describes: credited by the name on my public
GitHub profile (sync_github_name), otherwise by @username.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* i18n(cs): drop three rows that main added in the meantime

"Columns", "Margin" and "Automatic" were added to cs.tsv on main while this
branch was open, with the same translations, so the appended block no longer
needs them (the catalog has no duplicate keys).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* i18n (cs): translate two strings that are still missing from cs.tsv

"Drag (lasso) or click points (polygonal) ..." and "The current gradient
(pick one in Window > Gradients)" exist in the other catalogs but not yet
in cs.tsv on main.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* i18n (cs): translate context-menu, channel and layer-style labels

These labels are rendered through tl!() with a variable, so the literal-based coverage test does not see them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Localize Czech status messages and notice wrappers

Translate shell messages before interpolating file names and engine errors.
Use the existing plural API for counted objects and preserve English fallback.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Localize Czech UI labels and hardware information

Translate brush controls, accessibility names and rasterization prompts at
render sites. Preserve adapter, driver and display/profile identifiers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Localize Czech composed labels and distinguish Navigator zoom

Translate profile, preset and shortcut messages before interpolation, and use
a Navigator context for the zoom level. Keep shortcut warning state separate
from translated text and preserve document names and shortcut codes.
Cover remaining visible counts and add rendering/glyph regression tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Fix Czech UI localization review follow-ups

Keep copied System Info and API diagnostics in English while localizing the
on-screen text. Avoid double translation of accessible names, honor command-id
shortcut labels, and translate stored auto backend values only for display.

Refine Czech warning punctuation and catalog wording, simplify template blocks,
and correct the documented catalog count to 3,522 rows. Add four regression tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* people.toml: keep axlecoffee's sync_github_name with their entry

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Losminek <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-11 17:23:27 -04:00
f92f13490b Fix/3144 taskbar maximized (#3308)
* Window frame: trim a caption-less maximize clear of the taskbar (#3144)

* Credit axlecoffee in the About window

* rustfmt

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-11 15:08:18 -04:00
026432815c Preferences: wire Tools › Show Transformation Values (#204) (#2953)
* Preferences: wire Tools › Show Transformation Values (#204)

The size and angle readouts beside the pointer (marquee W/H, Crop frame
W/H and angle, Crop Straighten angle) now go through
canvas::draw_value_readout, which draws them only while
tools.showTransformationValues is on, as in Photoshop. The brush HUD
isn't a transformation value and keeps drawing.

The preference leaves HIDDEN_UNTIL_IMPLEMENTED, so Preferences › Tools
shows it (its label is already translated). Settings that do nothing:
43 → 42 of 149.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Contributors: add my display name

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-11 08:57:49 -04:00
ec28fdd03d Add Photoshop-style guide layout dialog and live preview (#2930)
* feat(ui): add guide layout dialog with live preview

* test(ui): restore path helper used by save dialog assertions

* chore(i18n): avoid conflicts with appended catalog entries

* chore: avoid append conflict for contributor credit

* Merge main: one people.toml entry for Skyfuller1303, drop the duplicate joined helper

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-11 08:23:03 -04:00
SkyfullerandBrandon Thomas 51f0a287b1 Refine gradient stop controls in Properties and Gradient Editor (#2859)
* feat(ui): refine gradient fill properties

* feat(ui): use combined stops in Gradient Editor

* test(ui): restore cross-platform save path helper

* Refine Properties with a shared gradient ramp

---------

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-11 07:54:47 -04:00
2189a1610e fix(crop): stabilize resizing and apply ratio changes immediately (#3206)
Compute snapped handles from press-time geometry to avoid feedback from
Auto Center Preview. Paint the image and crop frame with the same camera.

Apply ratio and size options immediately as centred rectangles contained
in the current frame. Respect saved ratios, keep Swap and X consistent,
and let Enter apply output sizes without an initial resize.

Use container padding for the options bar's text allowance so right-aligned
crop actions do not introduce horizontal overflow.

Add regression coverage for handle snapping, rendered frames, preset/Enter
and Swap/X interactions, and options-bar scrolling at multiple UI scales.
Enable public GitHub profile name syncing for TheTeslak's contributor credit.

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-11 05:54:09 -04:00
FinnNkandClaude Opus 5.5 0c4016a4db feat(ui): hold Alt+Space to zoom out, as in Photoshop (#2877)
* feat(ui): Alt turns a held temporary Zoom In into Zoom Out

Alt reverses the Zoom tool's direction, but a held Zoom In (Cmd+Space)
ignored it: with Zoom Out unbound or rebound, Cmd+Alt+Space zoomed in.
held_tool now reports Zoom Out whenever Zoom In is the held binding and
Alt is down, unless Alt is part of the Zoom In key itself (a rebound
Alt+Z still zooms in). The default keys don't change here.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(ui): hold Alt+Space to zoom out, as in Photoshop

Photoshop zooms out with Alt+Space (Option+Space on the Mac), usually
pressed as Space first, then Alt, then click. PhotoCraft's Zoom Out
(hold) default was Cmd+Alt+Space only, so Space then Alt still panned.

Zoom Out (hold) now defaults to Alt+Space. Holding Cmd+Alt+Space
matches both Cmd+Space and Alt+Space equally; the previous commit's
Alt rule resolves that to Zoom Out, so it keeps zooming out. Saved
overrides are kept. Holding Space first also avoids the Mac's Spotlight
shortcut and the Alt+Space window menu on many Linux desktops.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(contributors): credit FinnNk by GitHub profile name

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 18:56:04 -04:00
alexanderalberandBrandon Thomas ee4373521e Ctrl+Tab and Ctrl+Shift+Tab switch to the next and previous document (#2340) (#2353)
* Ctrl+Tab and Ctrl+Shift+Tab switch to the next and previous document (#2340)

Photoshop's document-cycling keys, on macOS too (Cmd+Tab belongs to the
system). They follow the tab order and wrap around at either end, and they
reach the documents even while a field or widget has the keyboard focus,
since nothing edits with Ctrl+Tab. Plain Tab still hides the panels.

generated with Claude Code

* Contributors: add my name for the About window

generated with Claude Code

---------

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 14:00:26 -04:00
Erica FrancoandBrandon Thomas 1e2194a054 fix(ui-egui): ui.key with ctrl does not trigger shortcuts off the Mac (#2430)
* fix(ui-egui): ui.key with ctrl does not trigger shortcuts off the Mac

`ui.key` and `ui.pointer` built egui's modifiers with `command` and `ctrl` as
independent fields. Off the Mac they are the same physical key: egui asks for
both to carry the same value and matches shortcuts on `command`, so a request
with only `ctrl` set produced a state a real key press never produces and
matched nothing. `ui.key {"key":"N","ctrl":true}` replied ok and did nothing,
while `command: true` opened New Document.

One `modifiers_from` helper now serves both: off the Mac the two flags are
paired, on the Mac they stay apart as ⌘ and Control. Callers already sending
`command` are unaffected.

* contributors: add my own entry (GitHub profile name)

---------

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 13:38:12 -04:00
F4LCONandBrandon Thomas ae03e7cd66 OpenEXR/HDR export: linearize grayscale documents too (#2386) (#2448)
* OpenEXR/HDR export: linearize grayscale documents too

Fixes #2386. The EXR/HDR branch of export_flat converted RGB images to
linear sRGB but left gray ones as stored, so an sGray #808080 was written
as 0.502 instead of 0.216. Gray images are now converted from their
profile (sGray when untagged) to linear gray. An untagged grayscale
OpenEXR opens tagged linear gray (an EXR Y channel is linear luminance),
and the open policy keeps that profile without a mismatch prompt, as it
already does for linear sRGB.

* contributors: add itsF4LCON

---------

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 13:13:40 -04:00
Valentin KleinandClaude Opus 5.5 28edd59339 Zoom tool: double-click zooms to 100% (#2508) (#2527)
* Zoom tool: double-click zooms to 100% (#2508)

Double-clicking the Zoom tool in the toolbar now selects it and runs
view.actualPixels, as in Photoshop, next to the existing Hand tool
double-click (fit on screen). With no document open it only picks the tool.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* contributors: add vangel76 (sync GitHub name)

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 13:08:57 -04:00
SilentYetiandClaude Opus 5.5 db0fb07862 Add native layered Paint.NET (.pdn) import (#1998)
Squashed onto main, with the maintainer's asks applied:
- Photoshop's blend menus (layers, brush, pencil, gradient, layer style) stay as they are;
  the Layers panel and Blending Options list a Paint.NET mode only for a layer that uses one.
- The six Paint.NET mode labels are translated in every catalog, and the blend-mode i18n test
  iterates layer_modes().
- pdn::pixels rejects truncated uncompressed (version 1) data before allocating its buffer.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-09 21:05:00 -04:00
Ahmed Moussa 1cfc56db56 fix(Text): served fonts become script fallbacks on the web (#2109)
A host's font manifest can now list the scripts each font covers (`scripts` field). The first served family for a script the bundled fonts lack (Arabic, Hebrew, Devanagari, Thai, Japanese, Chinese, Korean) becomes that script's fallback. When a layout contains text in that script, it requests the family. The font database adds the family to the fallback stack once it arrives, and type layers drawn before then are re-rendered.

The web shell also fetches the fallbacks for the browser's languages (`navigator.languages`) at startup, so an Arabic reader gets the Arabic font before typing anything.

The manifest's SHA-256 is now turned into a Subresource Integrity value (`sha256-<base64>`) and passed with the font fetch, so the browser rejects any file whose bytes don't match.

Also adds a contributor entry for hamada147 to `contributors/people.toml`.

Signed-off-by: Ahmed Moussa <[email protected]>
2026-10-09 20:04:04 -04:00
AyanandBrandon Thomas 3953d78378 Selection distance transforms: 7.6-10.6x faster and correct past 4K (#2093)
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-09 19:28:33 -04:00
SimonandClaude Opus 5.5 72cabac616 Layers panel: the scroll wheel over the blend-mode menu steps through the modes (#1961)
The wheel over the Layers panel's blend-mode dropdown (closed or open) steps to the next or previous mode, one per notch and one undo step each, stopping at the ends, as in Photoshop. Fractional wheel deltas add up to whole notches, and the wheel there no longer scrolls the panel (#1747).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 16:34:01 -04:00
Max MurphyandClaude Opus 5.5 27263bb16d Canvas: zoom is physical at any display scale (#1978)
View zoom now means device pixels per document pixel, so 100% shows one image pixel per physical
display pixel on a scaled (125%, 200%) display, as in Photoshop. Fit, Actual Pixels, pan/zoom anchoring,
scrollbars, the navigator, the pixel grid and every hit tolerance go through canvas_ppp / point_zoom.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 16:10:49 -04:00
Krzysztof CieślikandClaude Opus 5.5 0bf7a1e15f Preferences: wire Large Tabs, Export Clipboard and Show Channels in Color (#1986)
Large Tabs makes the document tab strip taller (34 px) with larger type; turning off Export Clipboard stops copies being mirrored to the OS clipboard; Show Channels in Color tints the Channels panel thumbnails (RGB from black to the primary, CMYK from white to the ink). Settings that do nothing: 48 of 141 (#204).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 16:03:44 -04:00
Stevan MarjanovicandClaude Opus 5.5 b81808f6bc Double-click the Hand tool to fit on screen (#1796)
Double-clicking the Hand tool's toolbar button selects it and runs View > Fit on Screen, as in
Photoshop; a single click only picks the tool, and with no document open the double-click is
harmless. Also credits the author in contributors/people.toml.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 12:27:49 -04:00
Elvis33LEandClaude Opus 5.5 7533254791 Keep PNG text metadata through document saves (#1684)
PNG tEXt/zTXt/iTXt entries (and TIFF ASCII tags) were dropped on open with a warning. The
document now keeps them in Metadata.text, the .pcraft manifest saves them (older files still
load), and PNG/TIFF saves write them back; Metadata: None and embed_metadata=false strip them,
and PSD/PSB and palette PNG exports warn that they are dropped. XMP in a tEXt or zTXt chunk is
read as XMP too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:53:37 -04:00
9b4562f2a0 Open dialog: upper- and mixed-case extensions on Linux and the BSDs (#1439)
The portal and zenity match rfd's *.ext globs case-sensitively, so IMG_0001.JPG or DSC_0001.NEF didn't show. On Linux and the BSDs each letter of an Open filter extension becomes a [jJ] class; Windows and macOS keep plain extensions.
Duplicate #1682 by @switch87 took the same approach.

Co-authored-by: switch87 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 09:10:04 -04:00
Willy Foo 83d1f3115d perf(algo): specialize photo blur and resample channel loops 2026-10-09 11:12:52 +07:00
StreetblockandGPT-6 7fd610741b Fix PNG text metadata decode budget bypass (#1460)
Bound decoded PNG text and XMP by one shared max_alloc budget before
growing output, retaining CRC validation and malformed ancillary handling.
Cover compressed, plain, Unicode and post-IDAT metadata with small
synthetic regression fixtures, and document the budget's scope.

Opt Streetblock into public GitHub profile-name credits at their request.

Co-authored-by: GPT-6 (Codex) <[email protected]>
2026-10-09 09:02:07 +07:00
SckraTandClaude Opus 5.5 2619cd2200 fix(ui): wrap New Document preset titles inside their cards (#1401)
Preset card titles were painted with Painter::text, which lays out a single
unwrapped line centred on the card, so translations longer than the 164 px
card (ru "Размер Photoshop по умолчанию", es "Tamaño predeterminado de
Photoshop", ...) spilled past both edges. Lay titles out wrapped to at most
two lines, elide anything longer with a hover tooltip carrying the full name,
and move the size line down to make room.

Test: every preset title in every language fits the card in two lines
without eliding.

Also adds my contributors/people.toml entry.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-08 20:20:24 -04:00
006e1a3432 i18n: translate the Fill dialog, layer style contours and Image Size "(was …)" (#1291)
* fix(i18n): translate the Fill dialog labels, contour names and Image Size "(was …)"

The Fill dialog painted its row labels and the Contents options without
going through the catalog, the layer-style contour presets had no catalog
entries, and Image Size formatted "(was …)" in English. i18n-coverage only
sees tl! literals, so these stayed English in every language while it
reported 100%.

Wrap the Fill labels and the Image Size template in tl!, add the missing
keys to every catalog, and add a live test that checks each complete
catalog covers the Fill options and CONTOURS and that the dialog draws in
the selected language.

* chore(contributors): add my real name to the About credits

* i18n: Polish rows for the Fill dialog, contour presets and Image Size

pl.tsv landed on main after this PR; the translation-coverage tests need
every catalog to carry the new strings.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-08 19:10:19 -04:00
5a97ee30f3 Brush tests: seams, hard edge, zero scatter and ABR tip colour (#213) (#1221)
* Brush tests: seams, hard edge, zero scatter and ABR tip colour (#213)

Adds the four TOOL-213-13 regressions: minimum coverage inside a stroke
(spacing 0.1/0.25/0.5), a hardness-1 rim measured against a soft falloff,
dabs staying on the path at zero scatter, and an imported ABR tip painting
the brush colour instead of black. Scorecard row flipped to done.

* Contributors: add Reynold Hu to people.toml

Real name credited, display name synced from the public GitHub profile at the
next contributors.py refresh (every field is opt-in).

* Regenerate docs/scorecard.md after rebase onto main

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-08 18:28:12 -04:00
Gert Pellin - GPE cbed56b7f1 Desktop: write log records to standard error and a rotating log file (#1143)
* Desktop: write log records to standard error and a rotating log file

The desktop app installed no logger, so every log::warn!/log::error! in
the workspace was discarded: GPU startup fallbacks, a lost wgpu device,
the crash guard's panic report. A launch from a desktop menu has no
terminal either, so there was nothing to attach to a bug report.

apps/photocraft/src/logging.rs (no new dependency):
- standard error plus <settings dir>/logs/photocraft.log; each launch
  rotates the previous log to photocraft.1.log and .2.log, so the log of
  a run that crashed survives the next start; the file is capped at 16 MiB
- records logged before the settings directory is known are buffered
  (bounded) and written once the file is attached
- default levels: info for photocraft*, warn for everything else;
  RUST_LOG replaces them with env_logger-style directives
- never panics: an unusable directory or a failed write leaves standard
  error as the only sink

The logger is installed first in main (so the panic hook is recorded) and
the file is opened after argument parsing (--version and usage errors
leave no file). Documented in docs/development.md (Logs).

* Contributors: credit switch87 as Gert Pellin

* Desktop: report an unusable log folder through the logger

eprintln! panics when standard error is a broken pipe (a GUI launch on
Windows); log::warn! reaches standard error the same way once the file
sink has given up, and never panics. Same fix as storytold/vectorcraft#465.
2026-10-08 16:09:32 -04:00
Brandon ThomasandClaude Opus 5.5 44303db546 About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#898)
* About: contributors and models tabs compiled in from contributors.json (grab bag + table, sortable, name toggle)

Help > About PhotoCraft gets tabs About · Contributors · Models, following the craftrules
standard (standards/contributors.md): contributors/contributors.json is generated by
craftrules/scripts/contributors.py and baked into the binary by crates/ui-egui/build.rs as
static tables (nothing read at run time; the web build has them too). Each contributor line:
@username, opt-in display/real name, PRs, commits, lines +/−/Δ, binary assets +/−, first/last
commit. Sortable by every column; alphabetical ignores case and the leading @. The tab is the
dialog field `tab`, so agents can switch it with ui.dialog.set. New labels are translated in
all eleven catalogs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Contributors docs: craftrules is two levels up (../../craftrules)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Contributors: self-submitted names in contributors/people.toml; AGENTS.md credit-yourself section

build.rs reads contributors/people.toml next to the generated contributors.json and shows a name only
for entries the generator verified as written by that person (craftrules standards/contributors.md).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Contributors: regenerate (verified people.toml entries)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* i18n: credits labels for catalogs added on main since this branch

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Contributors: regenerate after rebase onto main

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Contributors: regenerate (verified people.toml entries)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-07 21:22:11 -04:00