Commit Graph
153 Commits
Author SHA1 Message Date
RickiNanoandClaude Opus 5.5 b800c40cfd Don't auto-select off-canvas layers from the pasteboard (#10665)
A Move-tool click on the pasteboard with Auto-Select only cleared the layer
selection (#4991) when nothing was hit there, but `layers_hit` was not clipped
to the canvas: fill layers and pixels hanging past the edge were hit on the
pasteboard, so the click selected a layer that isn't drawn there instead.

Outside the canvas of a document without artboards, nothing is hit now, and
the layer marquee is clipped to the canvas the same way. Artboard documents
keep picking over the pasteboard, where they draw their layers (#5712).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 19:17:45 +01:00
RickiNanoandClaude Opus 5.5 586563fc2b Address review: one group-name set for clashes, test the .grd prompt (#10640)
- free_group's brush check builds the set of group names once instead of
  scanning every preset for each numbered candidate.
- reimporting_a_gradient_file_asks_before_touching_its_group covers the
  gradient branch of the prompt: no prompt for a new group, Cancel imports
  nothing, Import as New Group adds "Fire (2)", Replace swaps "Fire" alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:18:24 +01:00
RickiNanoandClaude Opus 5.5 122e55a4a0 Ask before re-importing a brush or gradient file over its group (#10640)
Importing an .abr or .grd whose name matched an existing group silently
deleted that group's user presets (edited or moved-in ones included); the
preset libraries are outside document history, so it could not be undone.

- brush.presets.importAbr / gradient.presets.importGrd: `replace` now
  defaults to false; a taken group name imports beside it as "Kit (2)",
  "Kit (3)"…, and the result's `group` names the one it landed in.
- UI: when the group already holds the user's presets, a prompt asks
  Import as New Group (default) / Replace (can't be undone) / Cancel.
  Agents drive it with ui.dialog.set {"choice":"replace"} / confirm / cancel.
- The status bar names the group the file landed in.

Fixes #10640

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 09:49:33 +01:00
Mohamed MahjoubandClaude baf8e654e8 Keep a layer's place on its board when dragged to another artboard (#10489)
Artboards share one canvas, so a layer dragged from one board onto another in
the Layers panel (layer.moveTo, including the Alt-drag copy) changed parent but
kept its old canvas position, outside the board it now belonged to, and the
board clipped it away.

apply_move now shifts a layer that lands on a different board by the offset
between the two boards' origins, through move_layers (the Move tool's path).
Reordering within a board, leaving or joining a board from the top level,
position-locked layers and moving a board itself are unchanged. The Layers
panel's drop preview uses apply_move, so it shows the same result.

Locks are read before the drop as well as after, so a layer whose position
was locked only through the board it left stays put (review follow-up).

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-01 05:14:15 +01:00
aiongg 799e129628 Merge pull request #10145 from davidmann4/integration-batch-10
Integration batch: 18 PRs on main
2026-10-31 13:31:44 -04:00
Khawar Zamman Wani f3caa319a2 Keep brush state intact when preset tip loading fails (#10288) 2026-10-31 13:07:18 -04:00
David MannandClaude Opus 5.5 622707a36f Address CodeRabbit review: four findings on the batch
- Channel targeting routes an allow-list of pixel-editing paint commands, not every
  `paint.*` id: the Mixer Brush's Alt-click load and Symmetry no longer follow a colour
  set or get refused with "Rasterize the layer" on a fill layer. Patch and Content-Aware
  Move stay routed (#9274).
- Native vector save-back checks the preview SVG as strictly as stored previews are
  checked on load and PSD export, so a preview with an unreadable embedded image is
  refused instead of stored and later dropped (#8571).
- .pcraft blobs read their zstd frame with framing headroom, so an incompressible
  preview right at its 16 MiB limit still loads; the limit still bounds the data.
- Levels/Curves recall (Alt+shortcut) decides the combined Lab record from the target
  the dialog captured, as remembering does, so a viewed layer mask keeps its settings
  beside a Lab colour set (#9274).

Each comes with a test that fails without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:41:36 +01:00
David Mann e5b4ea6781 Merge main at e7b7cf11e into integration batch 10
i18n catalogs: main's new rows (#10328) appended after the batch's, as a union.
2026-10-31 02:05:31 +01:00
David MannandClaude Opus 5.5 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
David MannandClaude Opus 5.5 f151843591 Type: a copied type layer gets the destination's point size, not a transform
#9594 fixed type copied between documents of different resolutions
re-laying out at the wrong size on its next edit by running the Image
Size resolution step on the copies: the type kept its point size
(24 pt) and got a compensating transform of src/dst ppi, with box and
path text scaled up to match.

The reference application, measured by script, keeps the pixels and
rewrites the point size instead: 24 pt copied from a 72 ppi document
into a 300 ppi one becomes 5.76 pt (24 x 72/300), and 24 pt from
300 ppi into 72 ppi becomes 100 pt. There is no transform, the
Character panel shows the rewritten size, and later edits keep it.

adapt_copies, which both Copy Layers to Document (Duplicate Layer to
another document, dragging layers between documents) and Paste of a
copied layer go through, now calls canvas_geom::retarget_type_resolution:
every size in points is multiplied by src/dst ppi (font size, leading,
baseline shift, first-line/start/end indents, space before/after) and
the PSD TySh is rebuilt. Box, path and transform are in pixels and stay
as they were; tracking, kerning and auto leading are relative to the
size and stay too. A resolution that isn't a positive number counts as
72 ppi (what the text engine lays it out at), and the scaled sizes stay
finite and above zero. Image Size keeps its own behaviour.

Tests: copy and Paste in Place, 72->300 (5.76 pt) and 300->72 (100 pt):
leading, baseline shift and indent scale, tracking stays, no transform,
the same ink bounds as the source, and after an edit the size is kept
and bounds and coverage match the source's render. Paragraph text keeps
its box and wrapping. Bad and absurd resolutions don't panic or produce
non-finite sizes. The #9594 test is replaced by these.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:34:03 +01:00
David MannandClaude Opus 5.5 bf80e2d2fa Select and Mask: Shift Edge is a curve on coverage, so hard edges stay put
#5176 made ±100% move the edge by max(radius, feather, 2σ smooth), at
least 1 px. Measured in the reference application (a 100 px square,
Radius/Feather/Smooth each varied, Shift Edge -100/-50/+100):

- A hard selection does not move at all, at any Shift Edge.
- On a feathered edge the result is a fixed per-pixel curve, the same at
  2, 5 and 10 px feather: the pixel's odds a/(1-a) are multiplied by
  49^(shift/100). At -100% a 50% pixel becomes 2%, at -50% 1/8, at
  +100% 98%. The fit holds within ±0.05 in log-odds on every sample.

So Shift Edge is now that curve, applied last as before. It needs no
neighbours, so the morphology callback and the halo/spread it added are
gone, with the ignored benchmark that timed it (matting::morph itself
stays for Select › Modify and the mask view).

Tests: a hard edge is unchanged at ±50/±100%; seven measured
before→after coverage pairs; a feathered edge is pulled in; the engine
refine tests now check the hard edge stays and a feathered one moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:58:18 +01:00
David Mann c5ad3ed53b Merge PR #9274: Support editing selected color channel sets
# Conflicts:
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-30 14:28:46 +01:00
David Mann b2db24400e Merge main at b1cfff9d3 into integration batch 10
Conflicts resolved as unions of both sides: codecs re-exports (PngRows + TiffBands), smart_cmds source imports
(main's source_surface_from_buffer and cache API with #8571's svg_source_image_in/convert_svg_source; main's
duplicate rendering import dropped), io flat.rs test modules (#8536's banded TIFF tests and main's PNG ones),
menus/invoke.rs (#8571's smart SVG intercept and main's layer.selectUnderCursor), state.rs (#8311's
type_box_overflow and main's type_insertion_style), the canvas_geom test keeps the is_finite guard, docs
status lines keep both change lists, i18n catalogs keep both sides' rows (hi.tsv: main's wording where both
added the same string).
2026-10-30 14:28:02 +01:00
1eec643e9d Integration: keep mask, channel and Fade targets when no colour set is targeted (#9274)
Liquify, Adaptive Wide Angle and the Filter Gallery refused to open with a
layer mask, a smart object's filter mask, an alpha channel or the Quick Mask
targeted ("This filter workspace requires layer pixels or native colour
channels"). Without a colour set they now open on the layer or smart object
again and send no `target`, so the command routes the targeted surface as
before. With colour rows on a smart object they refuse at open, as the
command refuses at OK.

Colour rows stay selected across layer switches, so with Red still targeted
the Brush on a Levels adjustment layer was refused ("Rasterize the layer
before editing colour channels") instead of painting its mask, and type,
shape, smart object and fill layers skipped the rasterize prompt. A colour
set now targets raster layers only; several rows still stop Move and
transforms on any layer, at press as before.

Clicking a pixel layer's mask thumbnail (or adding a mask to it) with colour
rows selected left the rows targeted, so the Brush painted Red instead of the
mask. Targeting the mask now moves the Channels target onto it (a view
change, no recorded step); clicking a row again takes the rows back.

Edit > Stroke and the Magic and Background Erasers are now routed but still
edit the layer with an alpha channel targeted, so Edit > Fade faded the
untouched channel. Fade now picks the channel or Quick Mask only when the
command changed it.

Main's #9483 mixes selections as o*(1-k) + a*k; the fused colour-set tone
path still used o + (a-o)*k, so fused_tiles_match_scalar_oracle_* failed
after merging main (16-bit Levels with a selection). It now mixes the same way.

Co-authored-by: Vlad Grankovsky <4578120+hidoba@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:22:50 +01:00
David Mann f5ae6d5276 Merge main into pr-9274-fixup
# Conflicts:
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/dialogs/adjust_editors/curves.rs
#	docs/gaps.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-30 13:16:39 +01:00
FredApps 722a9bd5cf Merge current main and remove app-local integration workflow additions 2026-10-30 12:22:42 +01:00
FredApps 0f120bbda1 Merge current main, reconcile streamed PNG profiles and workflow inputs 2026-10-30 06:52:05 +01:00
FredApps a4ab4873e8 Merge current main, retain full-sweep and new benchmark inputs 2026-10-30 03:05:00 +01:00
aiongg 7d66766afb Merge pull request #10157 from FredApps/integration/reviewed-performance-2026-10-29
Integrate 16 reviewed performance and UI fixes
2026-10-29 21:07:40 -04:00
David Mann fea300dd4c Merge origin/main into integration batch 10 2026-10-30 01:47:01 +01:00
FredApps 0cb4ec56d3 Merge current main and complete retained Pen messages in Nordic catalogs 2026-10-30 00:48:24 +01:00
FredApps c8c8fc7821 Reduce repeated GPU setup and UI work; partition remaining regression matrices 2026-10-30 00:39:19 +01:00
Dylan M. Taylor 6fdd6bb1b7 packaging: Flatpak finds and prints to CUPS printers (#10196)
* packaging: Flatpak finds and prints to CUPS printers

The Print dialog listed no printers in the Flatpak: the runtime has libcups but not
lpstat/lp, the sandbox couldn't reach the CUPS socket, and driverless network printers
(lpstat -e) need Avahi. Bundle lpstat/lp/lpoptions from CUPS 2.4.20 (Apache-2.0,
statically linked, licence installed) and grant --socket=cups and
--system-talk-name=org.freedesktop.Avahi in both manifests.

* print: lp takes the job only when CUPS lists a printer; the only printer by default; ~/.cups read-only

Review (CodeRabbit) on bundling lp in the Flatpak:
- With lp present but CUPS down or empty, the job failed on lp instead of reaching the
  IPP route and the print portal. lp_route now gives up the route when lpstat lists no
  printer, before anything is submitted.
- With no printer named, no default and one queue, IPP picked it but lp failed. lp_route
  names the only printer.
- The sandbox ignored the user's default printer, saved options and client.conf server:
  grant ~/.cups read-only in both manifests.

* print: no printers for lp means the portal, not IPP; bounded listing; CUPS socket wrapper

Review:
- When lp is there but CUPS lists no printer, the job went to the IPP route, whose
  server detection ignores ~/.cups/client.conf and could print to a local default
  printer instead of the configured server. It now goes to the print portal, or fails
  with no portal.
- The lpstat listing before a job is bounded (10 s, then lp takes the job as before)
  and runs outside SPOOLER, which guards only Windows driver calls.
- Flatpak mounts the host's CUPS socket at /var/run/cups/cups.sock whatever path
  client.conf or CUPS_SERVER names; the CUPS tools now run through a wrapper that
  points a socket path missing in the sandbox there, leaving server names alone.

* packaging: redirect a custom CUPS socket only when it is the one Flatpak mounted

Review (CodeRabbit): Flatpak picks the socket it mounts at /var/run/cups/cups.sock
with stricter rules than CUPS (flatpak-run-cups.c: a socket CUPS_SERVER, else the
first line spelled exactly "ServerName /path" in ~/.cups/client.conf, else the
default), while CUPS reads the keyword in any case and takes the last ServerName. The
wrapper now redirects only when CUPS' socket is the one Flatpak chose, so a job can't
reach a different server; otherwise CUPS fails on the missing path.

* print: a printer listing past its deadline kills and reaps lpstat

Review: the 10 s limit stopped waiting but left a hung lpstat and its thread
running, so repeated print attempts could pile them up. photocraft now runs the two
lpstat listings itself (craft-print's lpstat_command and an equivalent lpstat -e,
parsed by printers_parsed) and kills and reaps each one at the shared deadline. The
output is read on a thread that ends with the process, so nothing outlives the job.

* packaging: never redirect a CUPS_SERVER the sandbox was given

Review (CodeRabbit): Flatpak picks the socket it mounts from the host's CUPS_SERVER,
so one set by flatpak run --env or an override may not be what it mounted. The
wrapper now leaves any CUPS_SERVER alone and redirects only a client.conf socket that
matches Flatpak's choice; otherwise CUPS fails on the missing path instead of
reaching a different server.

* packaging: point CUPS at the mounted socket only when it is the one Flatpak chose, else fail

Review (CodeRabbit): with a custom socket listed first and the default last in
~/.cups/client.conf, Flatpak mounts the custom one at /var/run/cups/cups.sock while
CUPS picks the last line, finds that path, and talks to the custom server. The
wrapper now compares CUPS' socket with Flatpak's choice even when the path exists:
equal, CUPS is pointed at the mount; different, the tool fails with an error instead
of reaching another server.

Also (CodeRabbit):
- A printer named in the job goes to lp even when CUPS lists none: listing can be
  forbidden while printing is not, and the portal can't honour a name.
- A failed lpstat -e (CUPS unreachable, listing forbidden) means the list can't be
  told, so lp takes the job, instead of an empty list sending it to the portal.
- A listing longer than 1 MiB counts as no answer, instead of growing without bound.
- The kill test checks the process with kill -0, which works on every Unix.
- docs/printing.md: the only-printer fallback is on the lp and IPP routes, and the
  portal is Linux only.
2026-10-29 18:51:10 -04:00
FredApps c93c0bb36d Merge main into trimmed integration; reconcile blur, pattern and Liquify behavior 2026-10-29 23:47:53 +01:00
FredApps 29bf828ec2 Merge upstream main into reviewed performance integration
Preserve both UI parity status entries; retain all reviewed performance changes.
2026-10-29 23:35:19 +01:00
FredApps 73502a6b89 Reconcile Arrange no-op regression with retained command contract 2026-10-29 22:28:41 +01:00
FredApps 63637c4d80 Merge current main into trimmed integration, preserving exclusions and interpolation behavior 2026-10-29 21:31:18 +01:00
David MannandClaude Opus 5.5 03a0299bdb Merge PR #7795: Print: Scale to Fit Media fills the driver's printable area (#7782)
Conflicts with main's module moves resolved: driver_printable takes main's
spooler-held settings_for_layout (which already wraps the page query in
with_spooler) and drops the now unused settings/pg lines; take_turned_page and
its test move to dialogs/file_ui/print.rs and dialogs/file_ui/tests.rs;
crates/engine/src/cmd/print_cmds/tests.rs keeps main's version (the PR did
not change it in its own commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 20:15:41 +01:00
David Mann 355831f845 Merge PR #7621: Play .atn steps that switch to another open document
# Conflicts:
#	docs/gaps.md
2026-10-29 20:04:40 +01:00
David MannandClaude Opus 5.5 fefd90d576 Merge remote-tracking branch 'fork/integration-batch-10' into HEAD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:16:12 +01:00
FredApps 255ecc81d0 Remove 21 requested PR contributions from the 108-PR integration
Retain 87 PRs. Reconcile overlapping integration fixes and update testing scope.
Removed: #7153 #7742 #5493 #9282 #3455 #9172 #3139 #3967 #4783 #5453 #8347 #1991 #3345 #4042 #4305 #4883 #4890 #5090 #6621 #5870 #5939.

Combined CI pending for this source; historical portable artifact is not applicable.
2026-10-29 18:49:17 +01:00
David MannandClaude Opus 5.5 528ecf842e Merge remote-tracking branch 'origin/main' into HEAD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:44:24 +01:00
Federico Olivares 796ef557cc Merge main into the integration batch
Main's noop_step now takes the step's params (an Arrange that cannot move the layer is a no-op too); it is public for the CLI's run and batch, which pass them.
2026-10-29 14:19:54 -03:00
David Mann d7ca7a0cf3 Merge PR #8571: Edit embedded vectors with original-format save-back through VectorCraft 2026-10-29 18:15:47 +01:00
1acb1f464b Integration: SVG contents open and render as on main; AI originals for other editors (#8571)
- Smart-object contents (Replace Contents, Convert to Layers, Stack Mode,
  internal Edit Contents) refused any SVG whose canvas area times its
  node count passed 24 MP, e.g. a 1920x1080 layout with 12 shapes.
  decode_source now uses the bounded image parse with File > Open's
  layered import (import_svg_bounded_images). Convert to Layers' shape
  cache cap is 1 GiB in the document's format, the import's own budget,
  not 24 MP (four full-canvas shapes in a 4K document failed).
- The bounded resolver failed the whole drawing on an embedded image it
  can't read (an unknown MIME type, a corrupt PNG); it now leaves it out
  as usvg does, so such SVGs place and render again. Byte and decode
  overruns stay errors.
- resvg gives each embedded SVG image a scratch pixmap the size of its
  target and frees it after, so the scratch budget counts the deepest
  chain of nested SVG images, not every sibling: 12 SVG icons on a 2 MP
  canvas, or one at a 12 MP render, no longer fail.
- Main's shared nesting budget (#9397) now covers the bounded path too:
  511 + 511 levels across a drawing and its embedded SVG overflowed a
  1 MB stack.
- An AI working copy is regenerated through VectorCraft only when
  VectorCraft is the editor; Illustrator or another program gets the
  original bytes. VectorCraft is recognised by bundle id, a known
  install, or its name (vectorcraft[.exe], vectorcraft-<version> AppImage,
  ai.storyteller.vectorcraft).
- updateSvg refuses an AI/PDF/EPS source (updateVectorSource saves it
  back in its own format); the doc no longer calls updateSvg "existing".
- Edit Contents on contents already open in the internal editor switches
  to that document, as main does, instead of an error.
- Stack Mode on an SVG refuses up front when one float frame per layer
  would pass 1 GiB (the layered import no longer refuses for it).
- A stored display preview still fails on an embedded image it can't
  read (strict budget, its own import_svg_preview_with_work; external-
  editor saves and conversions leave the image out as placement does); skipped embedded SVGs still use up the XML byte
  budget; an image under an unknown MIME type or with no pixels is left
  out uncharged.
- Merge: the PSD import reads a preview's native source from the
  document's global blocks; main's owned open moves them out of the
  file, so every stored preview was dropped.
- Main's strict Clippy rules on the PR's app code (explicit import, a
  Unix-only test helper, one long opt-in test).

Co-authored-by: Mocky Digital <282319807+mockydevs@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:13:23 +01:00
aiongg 2231233af9 Merge pull request #6590 from wanikhawar/fix/6501-multi-layer-free-transform
Free Transform moves every selected layer (#6501, #3384, #6096)
2026-10-29 12:14:01 -04:00
aiongg 3bcff338c5 Merge pull request #6421 from wanikhawar/save-for-web-webp
Save for Web: WebP, lossy at its Quality or lossless (part of #6391, #2415)
2026-10-29 12:12:47 -04:00
Khawar Zamman Wani 82096cff3a perf(retouch): avoid duplicate rasterization while drawing (#9532)
* perf(retouch): generate live dabs without unused coverage

* fix(ui): box live stroke variants after retouch state reduction
2026-10-29 12:12:33 -04:00
David Mann afacb057f7 Merge origin/main into pr-8571-fixup
Conflicts: svg.rs (main's bufread inflate and shared render helper, kept
with the PR's bounded raster path), psd_import.rs (the PR's vector
preview moved into main's smart_content), menus/invoke.rs (the SVG
intercept moved into main's split invoke_unguarded), rendering.rs and
the ui-egui import lists (main's explicit imports), Cargo.lock, docs
status lines (unions).
2026-10-29 16:38:24 +01:00
Khawar Zamman Wani 991ef9907b fix(actions): allow no-op Arrange steps during playback (#8457) 2026-10-29 11:34:13 -04:00
David Mann 010ab858e7 Merge PR #7716: Add Pattern Fill option to Layer Panel Footer 2026-10-29 16:26:56 +01:00
3968e24cf0 Integration: the empty-layer check honours the default pixel and video (#7716)
`is_empty_pixel_layer` took any layer without stored tiles for empty, so a
layer opaque only through its default pixel (as KRA imports it,
`Surface::with_default`) or a blank video layer became the Pattern Fill
(and the Stamp Visible target) instead of getting one above. It now
needs a transparent default pixel (alpha only: a mode or profile
conversion gives an empty layer's default pixel other colour bytes) and
no video.

Co-authored-by: Hodd <32705043+hodd1444@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 16:12:49 +01:00
David Mann 0e72c418da Merge remote-tracking branch 'origin/main' into pr-7716-fixup
# Conflicts:
#	crates/engine/src/cmd/pattern_cmds.rs
#	crates/ui-egui/src/panels.rs
2026-10-29 15:45:55 +01:00
David Mann cf66eea4c9 Merge PR #8015: Added advanced selections keyboard shortcuts 2026-10-29 15:45:22 +01:00
David Mann 56d1da1943 Merge origin/main into pr-8015-fixup
Main split crates/engine/src/vector_cmds.rs into cmd/vector_cmds.rs and
cmd/vector_cmds/paths.rs, moved the shortcuts tests into
input/shortcuts/tests.rs and PhotocraftApp::run into app/commands.rs.
The PR's change is ported as it is to the new places; its text-field
test now draws the field every frame, as main's egui drops the focus of
a field that isn't drawn.
2026-10-29 15:29:05 +01:00
David MannandClaude Opus 5.5 7b72ce949e Merge origin/main into #8623's fix-up branch
Main split the files this PR changes: transform_tool.rs into tools/transform_tool/ (begin,
commit, drag_modes, draw, pointer, tests), canvas.rs into canvas/ (display, tool_input,
transform_controls, transform_controls_tests, snap_ui), and transform_cmds.rs into
cmd/transform_cmds.rs + tests.rs. The PR's changes are carried into the new modules as they
were. One adaptation: rotate_dir on main takes the live gesture (#5405), so the shared box's
gesture is passed through a new live_gesture helper instead of the PR's `rotating` bool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 14:37:31 +01:00
FredApps 53dbfe280c Integrate 16 reviewed performance and usability PRs
Includes #8472 #8488 #9461 #9793 #9809 #9813 #9814 #9822 #9826 #9847 #9850 #9852 #9921 #9924 #9961 #10080 at reviewed heads.

Preserves each PR head as a parent. Reconciles CI workflow inputs and benchmark jobs without dropping checks. Excludes #8536 #9825 #9969 #10129. Combined CI pending; no manual validation claimed.
2026-10-29 14:31:15 +01:00
FredApps e40cdadb63 Preserve opening snapshot assertions and refresh generated scorecard 2026-10-29 07:52:28 +01:00
FredApps 964a47b51c Reconcile history, gradient, type safety and UI validation behavior 2026-10-29 07:30:56 +01:00
Khawar Zamman Wani e2cd2a335a Composite smart object sources in tile-aligned blocks
Row strips of a very wide source still allocated whole 256-row tiles
across the full width for every few rows (about 293 MiB per strip of a
300000 px RGBA8 source). Blocks are now whole tiles, at most 4096 px wide
and 1 Mpx in all, and their tiles move into the result without a copy.
2026-10-29 11:48:58 +05:30