#426 gave smart objects a projective placement (`SmartObject.perspective`,
with `transform` as its affine approximation): Distort and Perspective set
it, the layer re-renders from its source through the homography, Warp
composes with it, and the PSD placement reads and writes
`nonAffineTransform` / `Trnf`. The scorecard row still said "missing" with
the pre-#426 note, as did the ui-parity row and the #579 mention in gaps.md.
* panic_hunt: don't fuzz the print commands into a real printer
file.print and file.printOneCopy spool to CUPS, so every run of the adversarial-params test
printed a blank page on the default printer. The hunt now skips those two ids (their params
are validated like everyone else's; spooling is not what the test is for).
* Guard the print spooler itself and the CI environment
- print_cmds: disable_printing() (a process-wide flag the panic hunt sets) and the
PHOTOCRAFT_NO_PRINT environment variable make spool_pdf refuse before running lp; dryRun
still renders and reports the command. Test.
- CI: PHOTOCRAFT_NO_PRINT=1 and LPDEST=photocraft-no-such-printer, so a regression fails
loudly instead of printing. Documented in docs/development.md › Environment variables.
#4093 changed the Auto-Select default to Group, as in Photoshop. The two
layout_perf tests that click layers inside groups with Auto-Select on
expected the layer under the pointer, so they now select the group and
fail on main. They set the target to Layer explicitly, which is what
they test.
- image.imageSize takes percent (both sides; an explicit side wins).
- actions.conditional {if, then, else}: Photoshop's Insert Conditional as a step, 33 conditions
on the document (shape, mode, depth, selection, layers, alpha channels, unsaved) and the
active layer (kind, locks, visibility, masks, effects); a failing branch stops the caller.
- .atn events: placeEvent → file.placeEmbedded/placeLinked (path, scale), open → file.openAs,
imageSize in percent, fill from a point → paint.bucket (pixels), conditional. 105 → 108.
- Panel labels conditional steps as 'If cond → then / else'.
- edit.transform gains the relative form (scaleX/scaleY %, rotate, dx/dy about the frame
centre), as a Photoshop action records a Free Transform; image.applyImage takes a source
layer by name.
- .atn events: transform (layer → translate or relative transform; selection →
transformSelection), applyImageEvent, export (Save for Web, with the txtu path record
decoded and written back; kept verbatim without a destination), newPlacedLayer /
placedLayerEditContents / updatePlacedLayer, close with the Save answer → save + close,
delete of the target channel → layer mask delete, Quick Mask on/off, polygon selections →
select.lasso, Gradient Map (direct and as an adjustment layer). 92 → 105 events.
- Export tries every event listing a command id (Quick Mask on → set, off → clearEvent).
- Docs: event counts in the parity docs.
* Actions panel: set folders, dialog toggle column, Insert Stop (#219)
- Engine actions_edit_cmds: actions.insert (any command at a position; Insert Stop… uses it
with actions.stopMessage), actions.dialog (the per-step dialog flag), actions.assignSet.
Flag sets follow inserted steps; nothing edits during playback.
- Panel: loose actions first, then each set as a collapsible folder row with its count
(ui.actions.collapsedSets); dropping an action among another set's rows moves it there;
Save Set… on a folder's context menu; a dialog toggle column next to the check marks;
Insert Stop… in the panel menu with a small message/Allow Continue dialog.
- Shell playback pauses on a dialog-toggled filter or adjustment step: the command's dialog
opens seeded with the recorded values; OK runs the dialog's values and resumes from the next
step, Cancel stops. Agents play headlessly. The play result reports paused: {step, id, dialog}.
- Docs: control protocol, scorecard AUTO-219-2 done, gaps G11.
* Translate the Actions panel's set, dialog-toggle and Insert Stop strings
* Resume a paused action only after the dialog's background job lands
A filter confirmed from a dialog runs as a background job in the desktop app; the resumed
steps met a locked document and failed. Wait for the job first (test covers background jobs).
* Load and save Photoshop Actions (.atn) (#219)
- photocraft-psd: `atn` parser and writer for version-16 action sets, exact round trip,
truncation/garbage safe, fuzz target and `atn_dump` example. Descriptor fix: `indx` and
`Idnt` reference items carry a class before the number (real action files failed to parse).
- photocraft-engine: `atn_cmds` maps 92 Photoshop events to commands and back (filters,
adjustments per channel, image/canvas/crop/trim/rotate/flip/mode, make/set/select/delete/
hide/show/move/duplicate, fill/stroke, selection, clipboard, purge, profiles, play, stop).
Other steps load as `actions.photoshopStep` (descriptor kept, written back by save, fails at
playback by name); tool selections load switched off. Commands `actions.load`,
`actions.save`, `actions.stopMessage`, `actions.photoshopStep`. `Action` gains `dialogs`
and `set`. `layer` params accept a layer name, so loaded steps replay in any document.
- UI: Actions panel menu › Load Actions… / Save Actions…; Photoshop steps labelled.
- CLI: `batch --actions set.atn`. Automation policy covers the new path params.
- Docs: file-format parity (.atn R/W), gaps G11, target-app parity, scorecard AUTO-219-2
partial, development guide.
* Translate the Actions panel's Load/Save strings in every catalog
The eight new panel strings (Load/Save Actions…, the loaded/saved counts, the kept-step label)
were missing from all 19 catalogs; the languages that were complete are back at 100%.
* Show the Actions entries in the default dock's panel menu
The legacy dock renders its own group menu and only knew the Layers and Swatches entries, so
Load Actions… and Save Actions… were missing there.