- copied_file: check the read policy on the real path when it is
valid UTF-8; skip non-UTF-8 paths while automation roots are
active instead of checking a lossy name (path traversal via
symlink, CodeRabbit #3872 thread 1).
- full_bytes_cached: collect matching cache candidates under the
FULL lock, then run the read checks after releasing it, so
filesystem walks no longer stall other FULL users (thread 2).
With objects and ruler guides selected together, object.nudge moved the
guides by the art's delta turned through General > Constrain Angle, so at
90 degrees a Right nudge moved a horizontal guide vertically. The guides
in that edit now move by the page-axis delta, as guides selected alone
already did (CodeRabbit finding on #3745).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh
* Links: use resolve_absolute for stored paths
* CI: release only with git dependencies from storytold
* Release: check git sources from the parsed Cargo.lock and cargo config
The grep let any character stand for the dot in github.com, passed a missing or stale
Cargo.lock, and missed source replacement in .cargo/config.
search_in_a_loop_runs_out_of_the_budget searches about 3 GB before the operation budget runs
out: 14 s on a fast desktop, over the 20 s limit on the FreeBSD runner. The 20 s limit stays
for the tests that use it as a speed bound.
New Window was enabled in the menus and in the control channel's menu
listing, but choosing it opened nothing and answered with the internal
note "multiple windows land with M11.5". It is now disabled, as Window >
Arrange's unimplemented items already are, and a direct call fails with
a message meant for the user, translated in every catalog.
A Selection tool press within reach of an anchor or the centre grabs the
object by that point, but the drag threshold was measured from that point
instead of from the press. A press 3-6 px from it was already past the
threshold, so the first pointer wobble started a move and jumped the
object by the offset. The threshold now counts from where the pointer
went down; a real drag still moves the object by the grabbed point.
A variable font's COLR layers follow its instance, a colour glyph turns with type on a path, and the canvas draws an SVG glyph's art in its own colours and the type's fill.
The editing data writes a gradient mesh as its patches, each with its
place in the grid (`/P`) and the grid's size (`/Size`); the importer
read each patch as a mesh of its own, in a group, so moving a mesh
point tore a patch away and left a hole, and a patch with anchors on
its edges was cut into cells on its own.
The patches are now placed by `/P` in the `/Size` grid and joined at
the corners they share into one GradientMesh. Which index of `/P` runs
along the columns is taken from the one under which the shared corners
meet. Where an edge bends through anchors, a mesh line runs through the
whole row or column of patches there, each patch cut at the same place,
so every cell keeps single-cubic edges. A mesh whose patches don't say
where they are reads as before; one whose patches fill the grid but
don't join up reads as before with a warning. Sizes and lines are
capped; non-finite values fall back.
Hand-written tests: a 2 x 2 grid as one mesh with the old patches'
colours, a moved inner point shared by its four cells, an anchor on a
shared edge adding a line through the mesh, a collapsed patch with
finite values, and unplaced or unjoined patches staying separate.
The colour glyph cache keyed the text's colours by their 8-bit RGB look, so a fill that only looked like an earlier one (a grey and the same RGB grey, a spot colour and its process match) took that one's colour, and its separations. It now keys them exactly.
A system fallback search can add no new face because another lookup loaded it
first. Re-read the loaded faces regardless of the search's return value, so
concurrent first use does not produce a missing glyph and different advances.
Add an isolated generated-font regression with simultaneous cold lookups.
The original code missed 14 of 16 lookups; the fixed regression passed twenty
runs. Both previously failing Ruby tests pass unchanged.
Validation: Rust 1.98.1 cargo xtask ci, all 10 gates passed; workspace tests
9,635 passed, 0 failed, 14 ignored. Default Rust toolchain unchanged.
Found while testing crates/doc/src/xml_id.rs:80 (escaped)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
VectorCraft had no lists. A bullet could only be typed as a character,
and the tab after it did not stop at the left indent. The reference app
turns paragraphs into a bulleted list from the Paragraph panel, the
context menu and Type › Bullets and Numbering › Apply Bullets.
text.setFormat takes list ("none" or "bullet") and bullet ("none",
"dot", "dash", "circle" or "square"). The layout draws the bullet before
the first line of each list paragraph that has text, in the font, size
and color of its first character, and starts the text at the next tab
stop, with the left indent counted as a stop. Wrapped lines start at
the left indent, and type on a path draws the bullet too. In a
horizontal right-to-left list paragraph the indents are mirrored: the
bullet hangs at the right, and the wrapped lines start at the right
where the first line's text starts after the bullet. The bullet is not
a character of the text. A paragraph whose left and first-line indents
are both 0 gets 18 pt and -18 pt when it joins a list. Return continues
the list, paragraph styles carry it, Reset Panel turns it off, and the
native file saves it only when it is set.
The Paragraph panel has a Bullets button with a list of the five
presets, and Type › Bullets and Numbering › Apply Bullets and the
context menu run type.applyBullets. Live SVG text writes the bullet's
character, and SVG writes type on a path with bullets as outlines. DXF
writes the bullet as a TEXT entity of its own, and legacy .ai export
warns that bulleted lists are left out. The caret, selections, Touch
Type and Show Hidden Characters skip the bullet.
Live SVG text of horizontally scaled type now writes each tab in a
tspan of its own. Before, the characters before a tab shared its tspan,
and its textLength made a viewer stretch them across the tab's gap.
Docs: docs/mcp.md, docs/target-app-parity.md.
Review follow-up (CodeRabbit): each thumbnail hashed every symbol's and pattern's art before its
texture cache lookup, so a panel frame cost visible tiles × (symbols + patterns) even when every
thumbnail was cached. The Symbols panel works the key out once per frame, a symbol library panel
once per library it shows, and the dragged chip once; thumbnails take it as given.