The engine's atomic module keeps its functions and signatures and delegates to craft-libs/craft-atomic (pinned to e9546e19), so every save and export caller is unchanged. write_atomic_with also turns a panicking writer into an error, so its temporary file is removed. Recovery checkpoints and package output keep their own writers, which replace links instead of following them.
On Unix, new files follow the umask and replaced files keep their permissions.
A frame holding items pasted into it (Edit › Paste Into) was drawn by
its own branch in draw_item that skipped every effect: Basic Feather,
Inner Glow, shadows and the rest were stored but never drawn. LYH Studio
episode 14 (11:51) gives the outlined "GET MORE." title, with a photo
pasted into it, an Inner Glow and a Basic Feather.
Such a frame now goes the way of any other object: draw_body draws its
fill, its items clipped to the frame and its stroke, so draw_item_fx
wraps all of it. Its silhouette counts as solid for shadow spread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A frame holding items pasted into it isn't a group. Ungroup took it
apart like one, leaving its content loose and losing the clipping shape,
and the Properties panel called it "Group" with an Ungroup quick action.
As in InDesign, Ungroup now only takes real groups apart; other objects
stay as they are and stay selected (Ungroup used to clear them from the
selection). Such frames are labelled by their shape (<ellipse>, ...), so
Properties shows "Ellipse" with Convert Shape.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backspace at the start of the second of two consecutive tables merged their
anchor paragraphs: Story::delete kept one table id, the other table was
dropped and the paragraph held two anchors. Pasting a table next to an anchor
replaced the existing table the same way, and pasting several paragraphs
right before an anchor left its table unreferenced. Saving succeeded;
reopening failed with "paragraph must hold exactly one anchor".
fix_tables now splits a table paragraph that holds further anchors, so every
anchor gets its own paragraph again. delete and insert_story tell it which
table each surviving anchor belongs to (fix_tables_with), and insert_story
fixes tables up once after the whole paste instead of after every run.
Regression tests in doc (Backspace between tables, deleting the middle of
three, paste before/after an anchor, paragraphs pasted before an anchor) and
engine (text.delete and edit.paste through commands, then a file round-trip);
all failed before the fix.
Claude-Session: https://claude.ai/code/session_01La6SDgsmdZfgfDDmx3wiGU
With a placed image selected via Direct Selection (InDesign's content
selection), Copy now copies the graphic itself: a frame of the whole
image's bounds (uncropped) at its spread position, without the frame's
fill, stroke or effects. Paste Into then puts the full picture in place
inside the target shape, as InDesign does (LYH Studio episode 14, 5:44).
Cut and Delete with the graphic selected remove only the graphic; the
frame stays, empty and selected.
Nested items copy in spread coordinates (parent transform applied).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The .docx/.xlsx text import iterates attributes with quick-xml's default duplicate check, so one crafted
start tag with N attributes cost O(N^2) (RUSTSEC-2026-0194, fixed in quick-xml 0.41). Bumping also clears
RUSTSEC-2026-0195 (NsReader namespace allocation), which this workspace does not reach (no NsReader).
unescape_value() is deprecated in 0.41 (clippy -D warnings); normalized_value(Implicit1_0) is what the
deprecated method now does. Regression test: an element with 100,000 attributes parses in < 5 s.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>