Review follow-ups:
- items_per_target maps each requested row on its own. Rows deleted or
duplicated together, one shared and one not, fell back to the literal
indices in every object, so the other objects could lose the wrong
items. The first object takes its own rows; each other object takes
its copies of the shared ones only.
- effect.dialog takes `aligned` (the panel passes it on a mixed
selection's shared effect row), and its OK and preview run
effect.setParams with it, so the dialog edits each object's own copy
of the effect instead of the first object's index in every object.
Tests: removing a shared stroke with an unshared fill leaves the other
object's fills; an aligned dialog's command carries `aligned`.
With several objects selected whose appearances differ in anything (two
circles whose fills differ but whose stroke and effect are the same),
the Appearance panel showed only "Mixed Appearances": no stroke, effect
or opacity row, so attributes the objects share couldn't be edited
together. It now lists what they share, as the issue describes:
- `vectorcraft_doc::appearance::SharedStack` (with `align`, a stacking-
order longest-common-subsequence match) gives the fills, strokes and
object-level effects every object has an equal copy of, and where each
is in every object's own stack.
- The panel's object row names the objects' common kind ("Path",
"Ellipse"; "Mixed Objects" only when kinds differ, as the Control bar
does), with an italic "Mixed Appearances" row under it when they
differ, then only the shared rows with their usual controls. The
Opacity row reads "Mixed" when opacity or blend mode differ (an edit
sets them all). The object row shows no fill chip when the fills
differ. Rows aren't dragged in a mixed selection: where a row would go
in each of several different stacks isn't one place.
- The engine maps a shared row to each object's own item: the active
item (a row of the first selected object's stack) always does, so the
Color, Stroke and Transparency panels edit every object's matching
item, and the item and effect commands do with `aligned: true`
(appearance.setItem / removeItem / duplicateItem, effect.remove /
duplicate / setParams / lightingPreset, transparency.set), each as
one undo step. moveItem and effect.move refuse aligned rows of
differing stacks. Without `aligned`, explicit indices keep their
meaning for single objects and scripts.
- "Mixed" is added to every catalog.
Tests: the shared stack of the issue's case, of a stroke at another
index, of nothing alike and of equal stacks, and that matches never
cross; editing the shared stroke edits both strokes (at index 1 and 2)
in one undo step that restores both, also through stroke.set on the
active row; removing a shared effect and adding a fill reach both, and
reordering differing stacks is refused; the panel lists kind, note,
Stroke, Feather and Opacity, not Fill, the full stack for alike objects
and "Mixed Objects" for a path and type. The existing object-row test
now expects the kind for two rectangles and the shared rows when their
opacity differs.
An auto-hidden taskbar reserves no work area, so the guard that keeps
the custom title bar's window borderless left a maximize covering the
whole monitor. The shell treats a caption-less window that covers its
monitor as a full-screen app, and in PhotoCraft, which has the same
guard, the hidden taskbar then did not show when the pointer reached
its edge (storytold/photocraft#4845).
While a maximized window covers its whole monitor, the guard now finds
the taskbars (the Shell_TrayWnd and Shell_SecondaryTrayWnd windows),
takes the monitor edge nearest each one's center, and stops the window
one pixel short of the monitor on each of those edges where the work
area reaches the monitor's edge. A taskbar that reserves a work area
is still cleared by the move into the work area. This ports
Higgs32584's fix in PhotoCraft (storytold/photocraft#6878).
Docs: docs/target-app-parity.md.
Co-authored-by: Higgs32584 <mhiggins217@gmail.com>
With the taskbar on the top edge, a maximize, a restore from the
taskbar or a drag to the top of the screen showed a classic title bar
above the app bar (#3242, #3723, #2072). The guard that keeps the
custom title bar's window borderless trimmed only the bottom of a
parked maximize to the work area, so with the taskbar on the top or a
side the window still covered it, and the strip between the window's
edge and the client area, which winit keeps on the work area, was
drawn as an old-style caption.
The guard now moves a parked maximize inside the work area on every
edge, keeping the resize border it was parked with, which it reads
from an edge without a taskbar. A rect it already moved, a dragged or
snapped window and a monitor with no reserved taskbar are left alone.
The move uses SWP_FRAMECHANGED and SWP_NOCOPYBITS and invalidates the
window, so the whole window is drawn again, and the style strip uses
SWP_NOCOPYBITS too. This ports FredApps's fix in PhotoCraft
(storytold/photocraft#5237).
Docs: docs/target-app-parity.md.
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
The parity doc's line on Create Outlines, Expand and Expand Appearance
on a frame of threaded text gave no source. It now links the public
help pages on creating and removing text threads, and says that they do
not describe outlining a threaded frame, so the handling is VectorCraft's
own behavior.
OK in a new scatter, art or pattern brush's Brush Options ran brush.new
without the art the dialog had captured for its preview, so brush.new
read the selection again. A selection changed while the dialog was open
gave the brush other art than the preview showed, and a cleared
selection made OK fail. OK now passes the captured art (the side tile
for a pattern brush) to brush.new.
Found while testing crates/mcp/src/resources.rs:169 (decode)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/create.rs:344 (polygon) and :358 (star; shape.spiral `segments` at :507 reads the same way)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
proxy_png decoded with image's default limits and no header check, while
every other decode path in the project uses MAX_RASTER_ALLOC counted as
RGBA. The header now decides first, and the decoder gets the same budget,
so an image too large to draw gets no preview by header alone, and a
large-but-in-cap image the app itself can open now gets its preview.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
MAX_JSON was 1 GiB on every target, but on wasm32 the 4 GiB address space
also holds the document the JSON deserializes into, several times its size.
The cap is now a quarter of the native one there, mirroring MAX_BLOBS,
and Unpacked::within makes the cap exercisable wherever a test runs.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
MAX_JSON was 1 GiB on every target, but on wasm32 the 4 GiB address space
also holds the document the JSON deserializes into, several times its size.
The cap is now a quarter of the native one there, mirroring MAX_BLOBS,
and Unpacked::within makes the cap exercisable wherever a test runs.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>