Use the document model boundary helper for explicit selections and retained selections after story replacement. Cover editing and caret movement through the command API.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
The UI theme included the 8.7 MB font a second time; it now uses
designcraft_fonts::JAPANESE_FALLBACK (release binary 72 MB -> 63 MB).
The ROADMAP note joins the Interface language entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Session::execute runs each command inside catch_unwind: a panic that escapes
(any edit, import or export, from the UI, CLI, MCP or control channel)
becomes EngineError::Internal, the document and selection are restored to
before the command, and crash recovery is saved when enabled. The first
Session installs a panic hook that logs the message and location.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The canvas, Properties panel and QR dialog skip a frame instead of
expect()ing a document; the control channel caps ui.drag steps. ui-egui
denies the panicking lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An .xlsx cell reference far from the others no longer allocates the whole
range before the cell cap (billions of cells, out of memory), and long
column names can't overflow. IDML import doesn't trust zip part sizes for
up-front allocation and adds header/footer counts saturating. Tagged Text
and IDML export use let-else; the font fallback has a compiled-in last
resort. These crates deny the panicking lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Commands return errors instead of expect()/unreachable!() (book, library,
undo/redo, paste, find/replace, Pathfinder, tables, QR, select, swatches,
anchors, articles, transform again, XML import). The sample document builder
propagates errors. Commands that add a key to their parameters no longer
panic when a caller (control channel, MCP, script) passes an array or a
string, and corrupt crash-recovery metadata is ignored. engine, mcp and
tools now deny unwrap/expect/panic/unimplemented/todo/unreachable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rasters stay below vello_cpu's u16 limits (a 65535-pixel-wide render
panicked inside it), page renders larger than 2^29 pixels decline instead of
allocating, PNG encoding never panics, and the PDF heading tag can't fail.
Both crates deny the panicking lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
expect() calls become let-else / fallbacks. A parent spread without pages
(possible in a .designcraft file) no longer underflows in parent_page_for,
and check() rejects a next_id so large that allocating ids would overflow.
Both crates deny the panicking lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the unwrap/expect calls in path closing, bounds, Pathfinder output
and gradient sampling with let-else / if-let; the ASE reader uses checked
offsets (a 32-bit length could overflow on wasm32). Both crates now deny
unwrap/expect/panic/unimplemented/todo/unreachable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only a focused text field blocks global shortcuts; the canvas (or a
button) holding egui focus after a click no longer swallows V/F/… until
Esc.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paragraph and character styles become CSS classes through slug(), which
keeps only ASCII letters and digits. Different styles could therefore get
the same class: every all-non-ASCII name ("見出し", "本文", Cyrillic,
Greek…) becomes `basic`, and "Body Text" / "Body-Text" / "body text" all
become `body-text`. style.css then holds several rules for one class and
the last one wins, so e.g. a 30 pt centred Japanese heading style came
out at the body style's 10 pt, left-aligned, in the EPUB.
Map each style name to a class once per export (paragraph and character
styles separately), adding -2, -3… on a collision, and use that map for
both the stylesheet and the XHTML. A class set in Export Tagging still
takes precedence in the XHTML, as before.
Test: styles_whose_names_slug_alike_keep_their_own_classes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The active layer lives on DocState, outside the undo snapshots, and
nothing re-checked it when the document's layers changed. After New
Layer → Undo (or Delete Layer / Delete Unused Layers / Merge Layers on
the active layer) it kept pointing at a layer that no longer exists, and
every object created next (frames, lines, placed images, pasted items)
was put on that phantom layer: not drawn on the canvas, missing from PDF
export, ItemLayer referencing an undefined layer in IDML, and preflight
reported nothing.
After each command, if the active layer is gone, make the top layer
active (as on opening a document).
Test: active_layer_survives_undoing_new_layer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
file.serialize returned {json: String::from_utf8_lossy(to_bytes(doc))},
but since M6.1 the native format is a zip (deflated document.json + raw
assets), not JSON text. from_utf8_lossy turns every invalid UTF-8
sequence into U+FFFD, so the result is not the file: on the web, File ›
Save downloads exactly those bytes (download_document), and the
downloaded .designcraft can't be opened again ("invalid Zip archive").
Agents calling file.serialize got the same unusable string.
Return {base64, bytes} like the other byte-producing commands
(file.exportIdml, file.exportPdf, snippet.export) and decode it in
download_document.
Test: serialized_document_opens_again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>