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>
winit 0.30 has no file drag-and-drop on Wayland. DESIGNCRAFT_WAYLAND=1
keeps the native Wayland backend.
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>
- LICENSE-MIT: copyright "ArtCraft Team and the DesignCraft contributors"
(it wrongly named the PrintCraft contributors); LICENSE-APACHE: the
standard text (adds the trailing newline).
- docs/brand/LICENSE-brand.txt: the ArtCraft brand terms.
- NOTICE: standard header and brand carve-out; third-party list kept.
- ASSETS.md: docs/brand/ rows point at LICENSE-brand.txt; brand files
noted as the one non-open-source exception.
- README: standard "License and credits" section, nav anchor, badge alt.
- AGENTS.md / CLAUDE.md: asset rule notes the docs/brand/ exception.
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>