53917dc17 (#9075, Preferences: Skip Transform when Placing, #8492) added
General::skip_transform_when_placing to crates/engine/src/prefs.rs, so the
prefs audit total in the scorecard is 194, not the 193 the committed file
says (the unread count stays 10). The regeneration in 3c4431945 (#11984)
covered #11322's total of 193 but predated this merge, so every open PR's
gate job fails with "docs/scorecard.md is stale: run cargo xtask scorecard
and commit the result".
This is the committed output of `cargo xtask scorecard` at base 67e3457b0:
the Workspace and UI summary row and the prefs-audit prose, 193 -> 194, two
lines, no source changes. `cargo xtask parity` produces no change here.
From the merged tree: the menu count goes to 662 of 662 live, main's 661
plus the Reset Transform item this PR adds, and the prefs audit counts
193 `Preferences` leaves where the committed scorecard still said 192
because main's #11322 added the pref `group_tools` without regenerating.
Both files are `cargo xtask parity` and `cargo xtask scorecard` output,
not hand-edited.
`docs/ui-parity.md`'s status line conflicted: the branch and main each
prepended a clause at the head of the change list, and the branch also
carried the newer `Last updated` date. The merged line keeps main's
toolbar tool-memory clause (#3569) word for word after our Reset
Transform clause (#11662), with one `Change:` header and the later date.
The prefs audit counts 193 settings since Preferences.groupTools landed;
docs/scorecard.md still said 192, which failed the gate and the Windows
staleness test on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review finding on this PR (#11675, discussion_r4499346688): with the warp now cleared as part of
the reset, a smart object whose stack holds a visible filter this build cannot apply took the
Free Transform path's forgiving branch - `refresh_layer` returns false for that stack,
`refresh_text` drops it on the floor, and `restore_unrendered` put the OLD cache back warped by
the relative map. The step recorded a placement at 100 % with no warp whose pixels still showed
the warp.
The step now insists on the guarded refresh the other smart-object edits use:
- `unapplied_filters` is checked before anything changes, so an unsupported Photoshop filter kept
verbatim from a PSD names itself in the error and the document is never touched.
- `refresh_or_fail(doc, id)?` runs at the reset boundary, right after `transform_by_map` and
before the exact map is pinned. It is a cache-warm call in the normal case (same document state
as the render `transform_by_map` just did), and it refuses an unreadable source or a render that
fails outright. Because `Session::edit` runs its closure on a copy and returns early on `Err`,
a refused reset leaves the document, the placement, the warp and the history list exactly as
they were - the fallback pixels are never accepted.
Regression test `reset_transform_refuses_a_stack_it_cannot_re_render` (red before this commit:
the reset returned Ok) asserts the error names the filter, that transform, warp and filter stack
are unchanged, that no history state was added, and that the same object resets normally once the
unsupported filter is off the stack. The four existing reset tests still pass, so the success
path and its warp clearing are unchanged.
No new dependency; photocraft_io's UNSUPPORTED_FILTER was already re-exported by this module.
The ui-parity row, its revision entry and the status line said the Warp survives Reset Transform.
It does not: measured against the reference application 2026-11-06 (report in #11675), the warp is
cleared with the rest of the transform, and the object centres on the average of the placement's
four corners. A 2026-11-07 revision row records the correction; the 2026-11-06 entry stays as the
history of what landed when.
cargo xtask parity and scorecard re-run: unchanged (661 of 661 live), so nothing to regenerate.
Remove the Azerbaijani menu coverage test (red on main), the crop Fill
label coverage test and the has() loop in the pen path Esc test. A
missing string falls back to English; cargo xtask i18n-coverage lists
the gaps. docs/contributing.md now says tests never require a string to
be translated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cargo xtask parity and scorecard on the merged tree: 661 of 661 menu items live (Layer 162 of 162, so the Reset Transform row is counted live), and the prefs audit is 10 of 191 now that upstream removed the Enable animations preference.
Unset or null follows Preferences › File Handling: Always and Ask When Saving
write the real merged image, Never the placeholder, and app.save opens no dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PSD writer always writes the real merged image for those modes (Bitmap and
Indexed are saved without layers; Duotone is exempt), so maximizeCompatibility
false does not give them the placeholder. The MCP params, control protocol and
CLI help now say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Photoshop's Maximize Compatibility checkbox, for one save: `maximizeCompatibility`
(true or false) on Save, Save As, Save a Copy, `app.save`, serve `doc.save`, MCP
`doc_save`/`doc_export`, Batch, Image Processor and Layers to Files decides the
merged image over Preferences > File Handling > Maximize PSD and PSB File
Compatibility; unset, the preference decides. Anything but a boolean is refused
before anything is written. The CLI takes `--no-maximize-compatibility`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pointer hooks read PEN_FLAG_INVERTED from GetPointerPenInfo and
report the pen end through Services::pen_end, so flipping the pen
switches to the Eraser and the tip restores the previous tool, as on
macOS. A WH_CALLWNDPROC hook beside WH_GETMESSAGE catches
WM_POINTERLEAVE, which Windows documents as sent, not posted.
PEN_FLAG_ERASER alone is an eraser button on the barrel and does not
switch tools.
- Draw the Crop task bar only on the active document's tile: a click into
another tile activates its document mid-pass, and the tile drawn after it
put the new frame's bar on itself for that frame.
- ui.inspect reports the bar hidden (and no rect) as soon as it can't show,
not only after the next draw.
- ui.set merges panels.task_bar field by field: {"at": [x, y]} keeps a pin,
{"pinned": true} keeps a dragged place; unknown nested fields are refused.
- Tests for each (each fails with its fix reverted), plus the task bar's
preset dropdown picking 1 : 1 in sync with the options bar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reference application's New Guide dialog has Orientation, Position
(a text field that takes typed units such as "2 cm") and Color, but no
unit dropdown; measured on 2026-11-06. Typed units already work on main,
so the PR only adds something the reference lacks.
This reverts merge 22e03d0bcb.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Linux runner has no GPU, so the GPU tests ran on Mesa llvmpipe over
GL, which can't render Rgba32Float: 57 GPU checks (50 GPU parity, 3
canvas, 2 adjustment preview parity, 2 effect-map recovery) returned
early and passed without comparing anything. A new step after Test
installs mesa-vulkan-drivers and runs the nextest `gpu` group on
lavapipe (Vulkan), reusing Test's build, and fails unless the parity
tests report the Vulkan backend and no test printed a skip.
No code change: photocraft's tests take whatever adapter wgpu returns,
software adapters included. Follows the CI half of lightcraft#1148.
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Add an opt-in WinTab backend for pen pressure on Windows
Windows Ink stays the default. Preferences > Tools > Pen Tablet has a Tablet
input dropdown (Windows Ink (default) / WinTab). WinTab opens a WinTab 1.4
system context on PhotoCraft's window and polls pressure and the eraser end
(tilt on devices that report orientation axes; rotation only with a pen and
driver that report twist) into the stylus feed. The row says to turn Windows
Ink off in the tablet's software utility; the choice applies at next launch.
- crates/tablet: Windows-only wintab/wintab-sys backend. Devices with
orientation axes use the orientation packet; pressure-only devices open a
packet without it and report neutral tilt. Leaving is the driver's hardware
proximity message (WT_PROXIMITY) and the packet's out-of-context status, with
a long fallback for drivers that never report either. Overlap priority
follows window focus, and the message hook requests a frame for each WinTab
message so pressure-only strokes update without cursor movement.
- apps/photocraft: install the backend when the preference is on; poll it at
the start of each frame; the installed wake repaints the UI.
- ui-egui: TabletPollFn service; the Windows-only Pen Tablet row with the
Tablet input dropdown and the note.
- engine: tools.tabletInput (windowsInk/winTab), default windowsInk.
- docs: hardware-parity WinTab gap entry, qualifying tilt to orientation axes
and rotation to twist-reporting pens.
Tested locally on a Wacom tablet with Windows Ink off: pressure, tilt and the
eraser end reach the brush (rotation where the pen reports twist); the default
path is unchanged.
* WinTab: a message that finds the state borrowed is skipped, not a panic in the hook
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* WinTab: rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Scorecard: count the Tablet input setting
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>