Main added a Layer menu item of its own, so after the rebase the menu
counts 663 items; raise the floor to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Free Transform keeps its box in a preview until it is committed; the
document's layers stay where they were. Layer › Copy CSS reads the
document, so with a box moved but not yet committed it put the old
left/top/width/height on the clipboard, not what the canvas showed.
Copy CSS now joins Save for Web (#6737) in the transform tool's guard:
the menu item is disabled under an open box, and any caller (menu,
shortcut, palette, control channel) gets "Commit or cancel the
transform first", with nothing copied and the box left open. After
Cancel it copies the old place, after a commit the new one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scaled 200 % across and 100 % down, a live rounded rectangle keeps one
radius per corner (r times the smaller scale), while its path, which is
what renders, has r·sx by r·sy elliptical corners. The rule wrote the
single circular radius. It now writes border-radius only when the path's
anchors are the ones the live radii draw (in any order, so a flipped
shape still writes its corners); otherwise none, as the module doc says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No test checked the copied border-radius after `shape.edit`'s
`transform` scales the live radii. With radii 12 and a uniform 2x scale
the rule now has to say `border-radius: 24px;`, so a regression in that
scaling fails the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A solid live ellipse got the plain rectangle rule, so pasting it showed a
rectangle. It now writes `border-radius: 50%;` where a rounded rectangle
writes its radii, before the fill. The module doc lists it, and lists the
geometry of other shapes (live polygons, stars and lines, custom paths)
as not written.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A layer named `a */ b` writes no `*/` outside its quoted image name and a
sanitised class; a layer with a drop shadow writes the same rule as
without it, with no shadow and no panic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The module doc said it writes CSS "as Photoshop writes it", but strokes,
layer effects, CSS gradients, blend modes and fill opacity are never
written. List what each kind of layer gives, in order, and what is left
out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A shape layer that is still a live rectangle with rounded corners now gets
`border-radius` before its fill, as Photoshop writes it: one value when all
four corners are equal, else top-left, top-right, bottom-right,
bottom-left, in px. Sharp rectangles write none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every type layer was written like point text: no width or height, placed at its first pixel.
Photoshop 2025 writes paragraph text differently. For a 200 × 120 box at (60, 40) whose two
lines span 156 × 51 px it writes `left: 59.937px; top: 36.948px; width: 156px; height: 51px;`:
placed at the box (within a few px, from its font metrics), moved down by an explicit leading
(36 px gives `top: 72.948px`), and sized by its text rather than the box (centred "Hi" in the
same box: `width: 19px; height: 17px;`). Do the same; point text keeps its position only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A `"` in a layer name ended `url("…")` early, a backslash started an escape, and a `"` in a font
family was turned into `'`, which names another font. Write each name as a CSS string the way
CSSOM serializes one (`\"`, `\\`, hex escapes for control characters). Photoshop 2025 writes
these names unescaped (`url("Say "hi".png")`); ordinary names come out exactly as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A layer named "2nd Box" gave `.2nd_Box`, which isn't a valid class selector. Photoshop 2025
writes `._2nd_Box`: it puts a `_` in front of a leading digit and turns a hyphen into `_` like
the other punctuation ("-2x" is `._2x`, "my-box" `.my_box`). Do the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Photoshop's Layer › Copy CSS puts CSS for the selected layers on the
clipboard. The new engine command layer.copyCSS writes each layer as a
class named after it (characters CSS doesn't allow become _): a type
layer's font size, family, colour, line height and alignment, a solid
fill or shape's background colour, or an image named after the layer;
then its opacity under 100 %, its absolute position and size (none for
type) and its z-index from the bottom, in Photoshop's format and order.
The Layer menu item copies the result to the clipboard.
Fixes#9372
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Raise the synthetic page to a million strokes so the 45 ms watcher
threshold keeps a wide margin on an optimised test build, and record why
the importer keeps one RenderCache per page.
- render_page: forward Interrupt to craft-pdf through an owned flag and a
scoped 5 ms watcher, so dense vector pages stop mid-render.
- Warn when craft-pdf reports a substituted font (the backend does not emit
the flag yet).
- /PageLabels: emit an entry for every page, start at 0, and write no tree
when every artboard name is empty.
- Export budget: charge retained page data plus the largest page's two f32
buffers instead of the whole document canvas, saturating on overflow.
- Render each artboard from a document holding only its group, so
overlapping boards no longer paint on each other's pages. One document
shell is cloned and reused.
- Tests: dense-page cancellation, labels with an empty middle name and all
empty, overlapping artboard pixels, sparse 9000x9000 canvas, seven US
Letter pages.
The Color Sampler menu kept only the sampler's index. If a control
command deleted an earlier sampler while the menu was open, the index
named another sampler and Delete removed that one. The menu now also
remembers the active document and the sampler's position; when either
no longer matches, Delete greys out and the menu closes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts with main's --no-maximize-compatibility (#8064) in the CLI
export args, output mapping and dev-dependencies, and in the
file-format parity status line and revision history: both sides kept.
Co-Authored-By: ljkx <ljkx@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Importing an .abr with select picked its first brush without the 1 %
opacity floor, so a preset saved at 0 % showed 1 % but painted nothing
(#11812).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A brush remembered at 0 % opacity (saved before the 1 % floor) read back
at 0: the options bar showed 1 % but strokes painted nothing. Restoring
now floors opacity at 1 %, like tools.setBrush (#11812).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#9970 reopened Image > Adjustments > Photo Filter... at the filter, colour,
density and Preserve Luminosity of its last OK. Measured in the reference
application: after OK at 60, 70 and 74 %, with a preset or with Color and a
custom colour, it reopens every time at Warming Filter (85), the orange
swatch and 25 %; only Preserve Luminosity carries over (off after an OK with
it off, on after an OK with it on). Opening now starts from the neutral
settings and takes just that one value from the last OK; Alt still reopens
at all the last settings (#8900). Tests and the ui-parity row follow the
measured behaviour.
Co-Authored-By: ljkx <177671075+ljkx@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The typed-text draft remembers the setting it was stored with and is only
used while that setting is unchanged, so a value set meanwhile (a tool
preset while the options bar was hidden) shows from the first frame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>