Commit Graph
4115 Commits
Author SHA1 Message Date
Cameron Gilroy e76570c875 Snap: parent rotation is a target 2026-10-06 09:05:59 -04:00
Cameron Gilroy 343b0de552 Snap: rotation lands on the other angle 2026-10-06 09:05:59 -04:00
Cameron Gilroy 50e5355358 Snap: rotation matches another angle 2026-10-06 09:05:59 -04:00
Cameron Gilroy 5a74bf2d6e Snap: equal spacing on moves 2026-10-06 09:05:59 -04:00
Cameron Gilroy 8e7cff341f Snap: dimension guides follow the final edge
Shift plus Alt restores the aspect from the original center after the size snaps. A line keeps the runner delta on both axes. Dimension guides are redrawn on the committed side. Grouped own size decomposes the absolute transform once.
2026-10-06 09:05:59 -04:00
Cameron Gilroy 40f3b6454a Snap: dimensions on resize and create 2026-10-06 09:05:59 -04:00
Cameron Gilroy b0797f2b47 Snap: parent items use the stroked edge 2026-10-06 09:05:59 -04:00
Cameron Gilroy 7032587238 Snap: document grid and baseline passes 2026-10-06 09:05:59 -04:00
Cameron Gilroy 253f1a7a10 Snap: Alt-drag skips preview copies and Shift follows the turned view 2026-10-06 09:05:59 -04:00
Cameron Gilroy a2451243ad Snap: Shift and Command on a move 2026-10-06 09:05:59 -04:00
Cameron Gilroy 55ec575521 Snap: guides beat object alignment 2026-10-06 09:05:59 -04:00
Cameron Gilroy 4de93d1345 Snap: carry SnapView on the tool context 2026-10-06 09:05:59 -04:00
Brandon ThomasandClaude Opus 5.5 9a50b3f6c5 Fonts: scan macOS's font asset folders (PingFang, Yu Mincho, …)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:05:40 -04:00
japbcoelhoandClaude Opus 5.5 48e409e214 Fonts: installed fonts are found by name whatever ran before
FontDb cataloged the system fonts only when a character needed a fallback
(nothing else calls load_system_fonts), so a document set in an installed
family was composed in Source Sans 3, and font.list and preflight reported
the family missing (#22).

Every lookup by family name now waits for one scan of the font folders per
session, as VectorCraft does since #130: the app starts the scan in the
background at startup, other processes run it on first use. The scan reads
only each file's table directory and name table, and skips folders it has
already read, so a symlink loop can't hang it. has_family counts installed
families. FontDb::with_font_dirs and system_font_dirs let tests use their
own font folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:05:40 -04:00
japbcoelhoandClaude Opus 5.5 b8e3b2c029 Preflight: placed PDF and SVG graphics have no resolution to check
preflight.run measured every placed graphic's "pixels", which for a PDF or
SVG is only the size of its screen preview, so a vector logo placed at its
own size was reported as low resolution:

  {"kind": "lowResolution", "message": "logo.pdf: effective 72 ppi (< 150)", "severity": "warning"}

Placed PDF and SVG pages export as vectors; the effective-resolution check
now applies to raster images only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:05:28 -04:00
Brandon ThomasandClaude Opus 5.5 e7ad37517e PDF/X-4: warn when a transparency group still blends in RGB
The CMYK rewrite matches krilla's exact output; if that ever changes,
the export says so instead of silently writing RGB groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:05:03 -04:00
japbcoelhoandClaude Opus 5.5 0ce21b476c PDF: transparency groups blend in CMYK in print exports
krilla gives every transparency group it writes a DeviceRGB blending space,
including the group around a placed PDF page:

  /Group<</Type/Group/S/Transparency/I true/CS/DeviceRGB>>

A print file therefore carried DeviceRGB (forbidden by PDF/X-4 under a CMYK
output intent) and blended CMYK art through RGB: a placed logo's black,
C91 M78 Y61 K97 in its own file, printed as C72 M67 Y67 K88. When the
document's transparency blend space is CMYK (the default for print) or the
export is PDF/X-4, the groups are rewritten to /CS/DeviceCMYK in place and
at the same length (the optional /Type/Group gives the room, the rest is
padded with spaces), only inside object dictionaries found through the
cross-reference table, so every offset stays valid. Booklets get the same.
Books merged by merge_pdfs are not covered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:05:03 -04:00
japbcoelhoandClaude Opus 5.5 9fcba1e3db PDF: placed PDF 1.7 files go into PDF/X-4 exports
A PDF/X-4 export (PDF 1.6) of a page with a placed PDF 1.7 (VectorCraft's
default, and most tools') failed at the very end with

  designcraft-cli: PDF writer error: Pdf(PdfDocument(PdfDocumentRepr { .. }), VersionMismatch(Pdf17), None)

krilla refuses to embed a PDF newer than the file it writes. PDF 1.7 changed
nothing in how a page draws, so a 1.7 file now goes in as 1.6 (its header
relabelled, as qpdf --force-version=1.6 does) with a warning. A PDF 2.0
file, or one whose catalog declares 1.7, fails the export with a message
naming it. hayro-syntax moves from dev-dependencies to dependencies (krilla
already builds it) because the check names its PdfVersion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:04:49 -04:00
japbcoelhoandClaude Opus 5.5 108e34ff79 PDF: [Paper] prints as no ink in print exports
[Paper] was written as DeviceRGB white (`1 1 1 rg`) even in print documents
and PDF/X-4 files: an RGB object in a CMYK print file, which print preflight
rejects. For the Print intent and for PDF/X it is now `0 0 0 0 k` (CMYK, no
ink, still knocking out what lies below); Web and Mobile documents keep RGB
white.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:04:34 -04:00
Brandon ThomasandClaude Opus 5.5 f1767a84fd Engine: an empty ids names nothing; states.create and conveyor.collect honour id
named_targets reads ids/id as targets() does, so {ids: [], id} can't
pass the gate and then run on nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:04:22 -04:00
japbcoelhoandClaude Opus 5.5 7e69c82b89 Engine: commands given ids run without a selection
A command that documents ids (object.textFrameOptions, transform.rotate, ...)
acts on the objects a call names, yet its enabled check looked only at the
selection. With a text caret or nothing selected, {"ids": [5]} failed with:

  command `object.textFrameOptions` is not available right now: nothing selected

Execute now lets such a call through when every named object exists, and
says "no object with id N" when one doesn't. transform.set, object.arrange
and edit.stepAndRepeat, which already acted on ids, now document them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:04:22 -04:00
japbcoelhoandClaude Opus 5.5 b54fa8d9bb Text: carriage returns in typed or created text start paragraphs
frame.create's text and text.insert kept CR and CR LF as characters, so text
written with InDesign's, classic Mac or Windows line ends ("One\rTwo") became
a single paragraph, and paragraph formatting (spaceAfter) never applied
between the parts. They now become \n, the paragraph separator, as edit.paste
and text import already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:04:08 -04:00
japbcoelhoandClaude Opus 5.5 316e1ffe62 Text: text.select documents its offsets as UTF-8 bytes
anchor and focus are byte offsets into the story text, as find.find reports
them, but the params doc said only {story, anchor, focus}. A script that
counts characters (Python's len()) selects too little after the first
accented letter: with "Matemática Básica" and focus 17, type.char left the
last two letters out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:03:56 -04:00
storybored379andOpenAI Codex 6e62155108 Guides: honor layer protection in move and delete commands
Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-10-06 00:03:03 -07:00
Brandon ThomasandClaude Opus 5.5 02f187cc5d Text: story.replaceRange keeps the caret on a character boundary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:32:57 -04:00
storybored379andOpenAI Codex e47f65a595 Text: keep selections on UTF-8 character boundaries
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>
2026-10-05 17:32:57 -04:00
Brandon ThomasandClaude Opus 5.5 7d721c92eb Fonts: embed Shippori Mincho once, shared by documents and the UI
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>
2026-10-05 17:32:47 -04:00
midasdf 3ef82f850c i18n: bundle Japanese font fallback and translate vertical controls 2026-10-05 17:32:47 -04:00
Brandon Thomas afdae89d68 Merge pull request #14 from storytold/pr/never-crash-guard
Never crash: last-resort guard around every command
2026-10-04 14:20:03 -04:00
Brandon Thomas 67535cf40a Merge pull request #12 from storytold/pr/never-crash-ui
Never crash: UI (ui-egui)
2026-10-04 14:06:22 -04:00
Brandon Thomas 9ec051e526 Merge pull request #11 from storytold/pr/never-crash-geom-color
Never crash: geom and color
2026-10-04 14:05:59 -04:00
Brandon Thomas e7f7ba13cc Merge pull request #18 from storytold/pr/never-crash-engine
Never crash: engine, MCP and tools
2026-10-04 14:05:42 -04:00
Brandon Thomas 1bf8ca50a1 Merge pull request #17 from storytold/pr/never-crash-render-pdf
Never crash: render and pdf
2026-10-04 14:05:25 -04:00
Brandon Thomas 9cf3a3309b Merge pull request #16 from storytold/pr/never-crash-doc-compose
Never crash: doc and compose
2026-10-04 14:05:09 -04:00
Brandon ThomasandClaude Opus 5.5 f1662185de Never crash: last-resort guard around every command
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>
2026-10-04 12:55:07 -04:00
Brandon ThomasandClaude Opus 5.5 2c46e97a48 Never crash: UI
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>
2026-10-04 12:43:34 -04:00
Brandon ThomasandClaude Opus 5.5 d84107aaca Never crash: importers (text, IDML, fonts, format, EPUB, images)
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>
2026-10-04 12:43:34 -04:00
Brandon ThomasandClaude Opus 5.5 f05bbfeea2 Never crash: engine, MCP and tools
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>
2026-10-04 12:43:33 -04:00
Brandon ThomasandClaude Opus 5.5 82d6def80b Never crash: render and pdf
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>
2026-10-04 12:43:33 -04:00
Brandon ThomasandClaude Opus 5.5 a539c76632 Never crash: doc and compose
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>
2026-10-04 12:43:33 -04:00
Brandon ThomasandClaude Opus 5.5 89891fdfbe Never crash: geom and color return or fall back instead of panicking
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>
2026-10-04 11:39:33 -04:00
Brandon ThomasandClaude Opus 5.5 a2bbd96f30 UI: tool shortcuts work right after clicking the canvas (#1)
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>
2026-10-04 11:38:17 -04:00
Brandon ThomasandClaude Opus 5.5 8490965e71 CI: fix new Rust 1.99 clippy lints (as_chunks, ?, redundant ref)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:37:09 -04:00
storybored379andClaude Opus 5.5 8d0203a2d2 EPUB: styles whose names slug alike keep their own CSS classes
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>
2026-10-04 09:52:37 -04:00
storybored379andClaude Opus 5.5 eee64ae7c9 Layers: the active layer falls back to the top layer when it is removed
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>
2026-10-04 09:52:29 -04:00
storybored379andClaude Opus 5.5 01753f90f3 File: file.serialize returns the file's bytes (base64) — web Save wrote corrupt documents
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>
2026-10-04 09:51:34 -04:00
Brandon ThomasandClaude Opus 5.5 5294c8f677 T: ruby and kenten
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:47:55 -04:00
Brandon ThomasandClaude Opus 5.5 558e798dfc E: CMYK transparency blend space on screen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:09:26 -04:00
Brandon ThomasandClaude Opus 5.5 e856b849bb M: video and sound in interactive PDF (screen annotations, renditions)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:44:50 -04:00
Brandon ThomasandClaude Opus 5.5 5b7969901c T: right-to-left carets and selections; Binding: Right to Left
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:34:47 -04:00