Commit Graph
2064 Commits
Author SHA1 Message Date
e580cca07a Port #5212: document Playback Options
Co-Authored-By: Zer0C00L01 <12090083+Zer0C00L01@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 06:13:13 +01:00
David Mann 5385ebd95b Merge PR #10788: Support recorded dialog filters, Calculations and Batch naming
# Conflicts:
#	crates/engine/src/cmd/actions_cmds.rs
#	crates/engine/src/cmd/automate_cmds.rs
#	crates/engine/src/cmd/file_cmds.rs
#	crates/engine/src/cmd/file_cmds/batch_run.rs
#	crates/engine/src/cmd/file_cmds/tests.rs
#	crates/engine/tests/it/main.rs
#	crates/ui-egui/src/panels/actions.rs
2026-11-05 05:19:44 +01:00
David Mann 04291e6055 Merge PR #11210: Expose Move transform controls through the control channel 2026-11-05 04:58:01 +01:00
David Mann 5031d0f2b9 Merge remote-tracking branch 'origin/main' into integration-batch-17 2026-11-05 04:57:59 +01:00
362a307449 Port #8659: show Stop messages and continue called actions after a pause
A Stop step with its dialog toggle on now shows a Message window with
the recorded text and Continue (when the step allows it) and Stop,
keeping the steps already applied; with the toggle off it ends playback
silently, and Insert Stop turns the toggle on, as measured in the
reference application. Allow Continue is read from the paused playback,
not from the dialog's fields. Agents and headless playback keep the
engine's Stop semantics.

A pause inside a called action now carries its callers (at most 16):
confirming finishes the called action and then each caller after its
call, replayed inside the callers' playback frames so recursion checks
and the single history state still hold. A silent stop ends the callers
too. Fixes #8386.

Co-Authored-By: bommerts <44821497+bommerts@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 04:56:41 +01:00
Khawar Zamman WaniandFredApps 1089932ae9 Add a headless GPU backend for composition and flat export (#10842)
* Add headless GPU backend foundation for composition and flat export (issue #8062 PR 1)

* Fix headless GPU readback bounds, API compliance, and error preservation (#8062)

* Fix GPU readback bounds, context thread spawn error, and test parity (#8062)

* Fix BandRenderer Send bound, PNG reborrows, and clippy lints (#8062)

* Fix effect shadow test construction and remove dead code wrappers (#8062)

* Use short reborrows for Headless renderer without lifetime escaping (#8062)

* Fix clippy collapsible_if and ensure multi-band geometry in flat export tests (#8062)

* Fix automation Rect import, add renderer diagnostics, and harden flat export retries (#8062)

* Fix GPU feature test APIs and native target gates

* Verify unsupported GPU effects before testing band seams

* Use compositor buffer ownership API after upstream memory accounting change

* Update export renderer test helper for owned compositor buffers

* Fix headless GPU backend review findings (#10842)

- release a closed document's GPU caches through a BandRenderer hook
- bound the monitored readback wait and report a stalled GPU as device loss
- reject 32-bit float documents before band readback so exports fall back to CPU
- keep previews at 8-bit RGBA and report the palette/Duotone renderer bypass
- retain renderer diagnostics when encoding fails
- strengthen renderer, save-path and GPU diagnostics tests

* Add GPU close-hook and 8-bit preview regression tests (#10842)

* fix(io): correct renderer binding mutability

* Fix remaining headless GPU review findings (#10842)

- release a document's GPU caches when file.close, file.closeAll or
  file.closeOthers removes it through a command
- honor cancellation while band readback waits, polling in 100 ms slices
- fault the device after a readback timeout so later exports skip it
- report single-layer, CMYK ink-mixing, ORA and PDF CPU paths to a
  requested renderer
- keep fallback warnings in their export instead of leaking them into the
  next one
- requeue renderer diagnostics when a renderer-aware save's final write
  fails
- write MCP/serve diagnostics without panicking on a broken stderr
- document the actual colour-space contract and drop F32 from the GPU
  depth list

* Use question mark for cancellable readback errors

* Keep GPU export diagnostics with their attempt and stop bands on cancel

Encode native .pcraft bytes before draining renderer diagnostics, and
requeue export warnings when a workspace write fails, so callers still
report the adapter or fallback reason with the error.

Move a failed GPU attempt's queued diagnostics into its CPU retry result
instead of leaving them for a later successful export to report.

Stop GPU band readback at the next band after cancellation, and check the
interrupt before each CpuRenderer sink delivery.

Band readback uploads half-precision textures, so reject U16 alongside
F32: their samples cannot survive the upload. Tighten the 8-bit CPU/GPU
parity checks to 1/255.

* Keep headless export warnings when a workspace write fails (#10842)

A capability-scoped save encodes the export, then writes it; when the write
fails, the export warnings (folded together with renderer diagnostics) were
pushed back through `BandRenderer::push_diagnostic`, whose default is a no-op.
A custom renderer that overrides only `drain_diagnostics` therefore lost the
adapter or fallback reason that belonged with the error.

Keep them in `Headless` instead and return them from the next
`drain_diagnostics`, alongside the renderer's own queue. The regression uses a
renderer without a `push_diagnostic` override and checks the warnings survive
the failed write and drain exactly once.

Also name the actual source of the depth limit in the headless GPU matrix:
layer content is uploaded as half-precision textures, not band readback.

* Stop CPU bands and CPU retries on cancellation (#10912)

The CPU retry after a retryable GPU error composes through
`FlatSource::bands`, whose shared `emit` sink did not poll the export
interrupt: a cancel was only observed after the whole composite finished.
Check it once per band, as `CpuRenderer` does, for both RGB and CMYK ink
bands. The flat and streamed-PNG retry branches also check before starting
the full CPU composite again, so a cancelled export returns
`IoError::Cancelled` instead of re-rendering the document.

Tests: band-sink cancellation for RGB and CMYK inks, a renderer call counter
proving a second export reaches the renderer, and a pending `wait_slices`
poll observing cancellation set mid-wait.

* Merge origin/main into GPU foundation (#10842)

* Keep GPU-path diagnostics on every failure path, honour cancel in native copy

- save: export warnings of a failed local write stay in Headless pending diagnostics
- render: preview fallback warnings are returned, drained, and added to the doc.render reply
- io: renderer-neutral fallback wording; native single-layer copy checks cancellation per band
- gpu: gate wait_slices tests off wasm32 (#10842)

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 04:53:25 +01:00
Khawar Zamman Wani 0a9717fa64 PDF export: keep the top text and shape layers as vectors, with selectable text (#10355)
* Skip compositing for geometric measurements

* Preserve simple PDF shapes and glyph source ranges

* Preserve text and shapes in PDF export (#10269)

- Export topmost simple text layers as vector glyph outlines with selectable text
- Embed ActualText unicode span and invisible text operators (mode 3 Tr) for 100% extraction
- Support stroked shapes in vector PDF export (centered solid strokes, dashes, line caps/joins)
- Retain glyph style indices in text rendering outlines

* Drop a product name from the glyph outline doc comment

* PDF export: keep path text and synthetic bold raster

* Fix PDF Unicode selection and respect font embedding permissions

* Render the text cache in PDF raster fallback fixtures

* Fix PDF vector selection eligibility and finite text matrices

* Preserve upper PDF vectors and reject overflowing curve controls

* Fix text shape import in PDF regression test

* Cover PDF Unicode map blocks and leading selection whitespace
2026-11-05 03:33:35 +01:00
yoriswind 7029dca60a Merge current main before pointer-control qualification 2026-11-04 11:28:43 -08:00
yoriswind 1720af269d Merge current main while preserving recorded Batch safeguards 2026-11-04 10:47:01 -08:00
David Mann b69d1f26b0 Merge PR #11011: Crop: Fill labels follow Delete Cropped Pixels, and the Selection preset entry (#10872) 2026-11-04 19:27:41 +01:00
David Mann cb323e9aea Merge PR #10965: Group action playback into one history state 2026-11-04 14:51:18 +01:00
David Mann d7e2bbe4a4 Merge PR #11077: Layers panel: filter by layer attribute (#10881)
# Conflicts:
#	crates/ui-egui/src/panels/layer_filter_ui.rs
#	docs/control-protocol.md
2026-11-04 14:51:17 +01:00
David Mann aac944a315 Merge PR #11223: Actions: map supported classic Gradient recordings
# Conflicts:
#	crates/engine/tests/it/main.rs
2026-11-04 14:51:00 +01:00
David Mann fd736f7ddc Merge PR #10986: Actions: map recorded Content-Aware Fill settings 2026-11-04 14:50:49 +01:00
David Mann a7f34dfa9f Merge PR #11222: Actions: support recorded Type Create Work Path 2026-11-04 14:50:47 +01:00
David Mann 3ea385cef3 Merge PR #11017: Layers filter: fill layers match no Kind button, and the Mode list has no Pass Through (#10878) 2026-11-04 14:50:46 +01:00
yoriswind a629a725f0 Preserve source data and diagnostics during Batch saves 2026-11-04 05:48:09 -08:00
David Mann 2c68cc6b95 Merge pull request #11203 from davidmann4/integration-batch-15
Integration batch 15: Crop, Free Transform and Actions epic PRs
2026-11-04 14:18:54 +01:00
Tareq HalabyandFredApps 2472b4a46b PSD: keep stylistic sets in Txt2 for PhotoCraft-native documents (#10313)
* PSD: keep stylistic sets in Txt2 for PhotoCraft-native documents.

needs_txt2 now covers Txt2-only OpenType flags (ss01–ss20 and non-default TXT2_FEATURES). Fresh documents get a framed point-text object with full style runs so Set 1/5 round-trip on save/reopen (#10308).

* Txt2: seed fill colour and font list on PhotoCraft-native blocks.

Fresh style runs get a /53 paint dict, and an empty font list gets a CoolTypeFont entry so index 0 resolves (CodeRabbit on #10313).

* Txt2: tag SimplePaint and ensure numbered /0 on legacy blocks.

CodeRabbit: write /99 /SimplePaint on fresh /53 dicts, and create the numbered resource dictionary before pad_frames when a legacy block lacks it.

* Txt2: treat a missing stylistic-set mask as 0 when comparing styles

A style run without key 85 used to agree with any ssNN mask, so enabling
ss01 could leave the file's runs in place on save. Align with import's
default of 0. Also restore the G8 gaps note after rebasing onto main.

* style(io): rustfmt psd_kerning stylistic-sets test

* Txt2: keep Photoshop /0 stable when seeding fonts and frames

Only seed a CoolTypeFont list and inject /0/8 when the block is
PhotoCraft-native or already had those keys / needs new frames, so
unedited Photoshop Txt2 saves stay byte-identical (#2374).

* style(text): rustfmt oversized-template assert

* PSD: keep the Txt2 scaffolding to PhotoCraft-native blocks

A file's own Txt2 block (any Photoshop era) is written as before: no
numbered /0 or /0/8 frame list is injected, a layer without a template
object gets its stub, a run without /53 isn't given one, and no
minimal style base is used. Only a block PhotoCraft creates itself
(prev.is_none()) gets fresh point-text objects, minimal runs, a seeded
font list and a frame list.

Fresh objects now write one /5 paragraph run per paragraph (UTF-16
lengths, checked conversion), as edited objects are refitted.

Tests: Photoshop blocks with an empty or missing /0 get no native
scaffolding; the oversized-template test is back to main's assertion;
fresh paragraph runs; the PSD round trip checks ss01 and ss05 on every
run.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-04 13:28:06 +01:00
skylar taylor-barrick 68b6dbfa79 Merge pull request #10750 from RickiNano/perf/bench-cmd-refresh-split
perf(bench): split command and refresh time; refresh like the app; stop rows timing setup
2026-11-04 12:39:54 +01:00
Tareq HalabyandFredApps 4d0ecd746f Toolbar: Alt/Option-click cycles the flyout tool slot (#10301)
* Toolbar: Alt/Option-click cycles the flyout tool slot.

Matches Photoshop: plain click keeps the shown tool; Alt-click advances through the slot without opening the menu (including tools Shift+letter skips).

* Toolbar: don't fit-on-screen on rapid Alt-clicks through Hand.

Alt-click only cycles the flyout slot; exclude Alt from the Hand/Zoom double-click handlers and cover inactive-slot cycling.

* Toolbar: ignore Hand/Zoom double-click after an Alt-click.

egui pairs a plain click with a prior Alt-click as a double-click; remember the previous click's Alt so fit-on-screen and actual-pixels only run for two plain clicks.

* Toolbar: read click modifiers from the slot's pointer release.

Match DesignCraft/EffectCraft: bind release mods to the tool rect, treat any modifier as blocking Hand/Zoom double-click, and harden the Alt-cycle test by clearing i.modifiers before release.

* Toolbar: cover 1/2/3-tool Alt-click cycles and hidden-tool slots.

Addresses ReviewerA on #10301: next_slot_tool wrap cases for short flyouts, and visible_sections so Edit › Toolbar hidden tools shrink the Alt-click cycle.

* style(ui): rustfmt after rebase onto main

* Toolbar: next_slot_tool never panics on an empty slot

Index with checked arithmetic and get() (house rule: no panics on
input-derived indices); an empty slot returns the current tool. Test
added.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-04 12:29:11 +01:00
yoriswind 508a35c83d Align Batch playback namespaces and retain child results 2026-11-04 01:51:41 -08:00
Psychotoxical c71677a159 Merge remote-tracking branch 'origin/main' into feat/layer-filter-attribute
# Conflicts:
#	crates/automation/src/server.rs
#	crates/ui-egui/src/control/tests.rs
#	crates/ui-egui/src/panels/layer_filter_ui.rs
#	crates/ui-egui/src/state.rs
#	docs/control-protocol.md
2026-11-04 09:49:44 +01:00
Hamza e85b360248 Merge remote-tracking branch 'origin/main' into fix/layer-filter-kind-and-mode
# Conflicts:
#	crates/ui-egui/src/control/tests.rs
#	crates/ui-egui/src/panels/layer_filter_ui.rs
#	docs/control-protocol.md
2026-11-04 12:48:11 +05:00
yoriswind 85fc78bb64 Merge current main into recorded Actions dialog support 2026-11-03 21:55:00 -08:00
yoriswind 2974772936 Map supported classic Gradient action recordings 2026-11-03 19:32:28 -08:00
yoriswind 16b24885e6 Support recorded Type Create Work Path actions 2026-11-03 19:28:31 -08:00
yoriswind 1bcf7a3c6a Merge batch 14 main before action-history review fixes 2026-11-03 17:27:25 -08:00
yoriswind 1f9f37ae8e Merge batch 14 main into Content-Aware Fill recordings 2026-11-03 17:24:10 -08:00
yoriswind 336f95cd7d Expose Move transform controls through the control channel 2026-11-03 17:17:05 -08:00
David Mann 59b49ca14f Merge PR #9979: feat(ui): live preview in Color Pickers for dialog colour fields and the Crop shield 2026-11-04 01:51:22 +01:00
David Mann 2c4666aa6e Merge PR #10814: Edit › Transform › Distort: side handles move their edge, Perspective's side handles skew (#10085) 2026-11-04 01:51:10 +01:00
David Mann 545d447c10 Merge PR #10998: Actions: map recorded Scale Effects and Create Layers 2026-11-04 00:51:01 +01:00
David Mann e06d1cbdc1 Merge PR #11067: Layers panel: filter by layer effect (#10880) 2026-11-03 22:55:47 +01:00
Psychotoxical 94a6b68a04 Merge remote-tracking branch 'origin/main' into feat/layer-filter-attribute
# Conflicts:
#	crates/automation/src/server.rs
#	crates/ui-egui/src/panels/layer_filter_ui.rs
#	crates/ui-egui/src/state.rs
#	docs/control-protocol.md
2026-11-03 22:09:02 +01:00
Psychotoxical 66b9f2f68b Merge remote-tracking branch 'origin/main' into feat/layer-filter-effect
# Conflicts:
#	crates/automation/src/server.rs
#	crates/ui-egui/src/panels/layer_filter_ui.rs
#	crates/ui-egui/src/state.rs
#	docs/control-protocol.md
2026-11-03 21:55:06 +01:00
yoriswind 3028355333 Merge main and retain action history across Batch and Spelling 2026-11-03 12:42:57 -08:00
yoriswind 1b6bc4136c Merge main into recorded Content-Aware Fill mapping 2026-11-03 12:14:13 -08:00
yoriswind 27f86af55b Merge main into recorded layer style mappings 2026-11-03 12:12:41 -08:00
yoriswind 7763e2578f Merge main into recorded Shear mapping 2026-11-03 12:11:27 -08:00
ljkxandClaude Opus 5.5 a0986a3977 Merge origin/main into feat/color-picker-live-preview
Resolve docs/ui-parity.md: keep main's status line and prepend this PR's entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:41:33 +01:00
ljkxandClaude Opus 5.5 9df16201aa Merge origin/main into feat/distort-edge-handles
Resolve the parity-doc conflict: keep main's status line and entries, re-add this PR's entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:39:56 +01:00
David MannandClaude Opus 5.5 72c2585177 Merge origin/main (287352bf92) into integration-batch-13
Conflict in crates/engine/src/jobs.rs: main's save commit point (#5333,
OPEN/COMMITTED/CANCELLED phase) met the batch's cancel state
(CANCEL_REQUESTED/WORK_FINISHED). Both now share one atomic with a
COMMITTED bit; commit vs cancel and the batch's late-cancel guard each
stay one compare-and-swap. A Batch that refuses a repeated or late cancel
reports CancelRefused::Finishing ("finishing") in jobs.cancel's
notCancelled list, still {"cancelled": 0}. control-protocol.md documents both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:35:44 +01:00
FredApps 287352bf92 Merge pull request #5333 from vistaero/codex/streamed-psb-save
Integrate streamed PSB saves with the shared save service
2026-11-03 18:41:16 +01:00
yoriswind 0edb29a18c Merge upstream main into Shear action mapping 2026-11-03 09:32:21 -08:00
David Mann 3d36330d94 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	docs/scorecard.md
2026-11-03 18:08:29 +01:00
FredAppsandClaude Opus 5.5 d07684a379 fix(ui): Overscroll off keeps an image smaller than the window centred (#10488) (#11085)
* fix(ui): Overscroll off keeps an image smaller than the window centred (#10488)

With Tools › Overscroll off, an image smaller than the window stayed
wherever zooming around the pointer left it, inside the window, so
zooming out drifted it off centre. #1718 measured that on Photoshop 25.4,
but two reports of Photoshop itself (#10488, #4799) say it stays centred
with Overscroll off. The owner chose the centred behaviour:
scrollbars::clamp_axis now centres an axis on which the image is no
larger than the view; a larger image still can't leave its edges, and
Overscroll on keeps the free view.

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

* test(ui): Overscroll off centres a fitting rotated image (#10488)

CodeRabbit on #11085: the rotated clamp path was only tested from an
already-centred view. A 600 px square at 45 degrees, started off centre
in a 900-point view, now ends with its screen bounds centred.

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

* style(ui): keep the rotated centring test within rustfmt's width

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:55:50 +01:00
d21c403e5f Type: add OpenType Numerals submenu (figure styles) (#11051)
* Type: add OpenType Numerals submenu (figure styles)

Type › OpenType › Numerals sets lnum/onum/pnum/tnum as a radio style
(Default through Tabular Oldstyle). Checkmarks follow the selection
start; TYPE-218-5 scorecard/gaps updated; parity floor 640.

* refactor(engine): one style-at-character lookup for the OpenType menus

feature_on_at and numerals_style_at each walked the runs the same way;
they now share style_at. NumeralsStyle::from_id and numerals_style_of
read the existing tables instead of repeating them.

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

* fix(text): persist OpenType numeral tags through PSD EngineData

Map lnum/pnum/tnum as EngineData bools and the width×height combos as
FigureStyle (Krita/Illustrator), so Numerals choices survive PSD round-trip.

* docs(gaps): note Numerals PSD figure-tag round-trip

* revert(text): keep PSD EngineData to the keys Photoshop writes

Backs out the PSD half of 18610324 and its gaps note (58813de3); the
engine reformat in that commit stays.

- `LiningFigures`, `ProportionalFigures` and `TabularFigures` are not
  Photoshop EngineData keys (the commit says Photoshop only has
  `OldStyle`), so writing them puts keys of our own into users' PSDs.
- The `/FigureStyle` code table (1-4) has no oracle: no Photoshop-authored
  file in photocraft-corpus, and neither ag-psd nor psd-tools reads it.

AGENTS.md rule 3 asks for public specs or observation. PSD persistence
of the numeral styles can come back with a Photoshop-authored oracle.
The Numerals menu still round-trips through .pcraft.

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

* test(text): lock PSD EngineData to Photoshop OldStyle for numerals

Document AGENTS.md clean-room: only onum survives TySh round-trip; lnum/pnum/tnum stay .pcraft-only until a Photoshop-authored corpus oracle.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:55:35 +01:00
ljkxandClaude Opus 5.5 0a379e2465 Merge origin/main into feat/color-picker-live-preview
Resolve docs/ui-parity.md: keep main's status line and prepend this PR's entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:46:51 +01:00
David Mann 559d3fe0f4 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/engine/src/cmd/file_cmds.rs
#	crates/ui-egui/src/app/files.rs
#	crates/ui-egui/src/dialogs/tiff_options_ui.rs
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/ui-parity.md
2026-11-03 17:11:53 +01:00