Commit Graph
4827 Commits
Author SHA1 Message Date
moppeeer f93bc980d6 Merge remote-tracking branch 'origin/main' into codex/toc-dialog-title-width
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
2026-10-29 11:44:18 +01:00
moppeeer 5bd339d28f Merge remote-tracking branch 'origin/main' into codex/toc-more-options
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
2026-10-29 11:44:15 +01:00
moppeeer 6f6ddb3272 Merge remote-tracking branch 'origin/main' into codex/pages-section-marker-dialog
# Conflicts:
#	crates/ui-egui/src/panels/pages.rs
2026-10-29 11:44:10 +01:00
moppeeer 7beeff1b2e Merge remote-tracking branch 'origin/main' into properties-opacity-slider
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:44:03 +01:00
moppeeer 271d20ba7f Merge remote-tracking branch 'origin/main' into properties-wrap-offsets
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:43:59 +01:00
moppeeer 8b7f17e60c Merge remote-tracking branch 'origin/main' into loaded-cursor-thumbnail
# Conflicts:
#	crates/engine/src/cmd/file.rs
#	crates/tools/src/snap.rs
#	crates/ui-egui/src/canvas.rs
2026-10-29 11:43:56 +01:00
moppeeer 7c029c4067 Merge remote-tracking branch 'origin/main' into properties-auto-fit
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:43:50 +01:00
moppeeer 740227bd18 Merge remote-tracking branch 'origin/main' into snap-page-centre
# Conflicts:
#	crates/tools/src/snap.rs
2026-10-29 11:43:39 +01:00
moppeeer b26ae037de Merge remote-tracking branch 'origin/main' into corner-options-dialog
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:43:35 +01:00
moppeeer 204da7bd75 Merge remote-tracking branch 'origin/main' into create-guides-spread
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
2026-10-29 11:43:32 +01:00
moppeeer c393c7fbd8 Merge remote-tracking branch 'origin/main' into object-menu-order
# Conflicts:
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/menus.rs
2026-10-29 11:43:29 +01:00
moppeeer 00a64e5943 Merge remote-tracking branch 'origin/main' into direct-select-anchor-points
# Conflicts:
#	crates/tools/src/select.rs
#	crates/ui-egui/src/canvas.rs
2026-10-29 11:43:21 +01:00
moppeeer 542ee6d5bc Merge remote-tracking branch 'origin/main' into graphic-frame-x-corners
# Conflicts:
#	crates/ui-egui/src/canvas.rs
2026-10-29 11:43:03 +01:00
moppeeer b8edea5692 Merge remote-tracking branch 'origin/main' into drawing-feedback
# Conflicts:
#	crates/ui-egui/src/canvas.rs
2026-10-29 11:42:59 +01:00
moppeeer 5a4012bab8 Merge remote-tracking branch 'origin/main' into properties-anchor-points
# Conflicts:
#	crates/tools/src/select.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/panels/properties.rs
#	docs/target-app-parity.md
2026-10-29 11:42:56 +01:00
moppeeer e094562ef6 Merge remote-tracking branch 'origin/main' into clear-fitting-options
# Conflicts:
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/menus.rs
2026-10-29 11:42:45 +01:00
moppeeer b6fec896f9 Merge remote-tracking branch 'origin/main' into effect-blend-modes
# Conflicts:
#	crates/doc/src/item.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/effects_dialog.rs
2026-10-29 11:42:14 +01:00
moppeeer 4145fcbc44 Merge remote-tracking branch 'origin/main' into text-frame-options-layout
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	docs/target-app-parity.md
2026-10-29 11:41:36 +01:00
moppeeer d5b9771937 Merge remote-tracking branch 'origin/main' into properties-bullets-options
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:41:11 +01:00
moppeeer 1fee4511af Merge remote-tracking branch 'origin/main' into properties-fill-color-picker
# Conflicts:
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:40:27 +01:00
moppeeer d25577fefe Merge remote-tracking branch 'origin/main' into swatch-inline-rename
# Conflicts:
#	crates/ui-egui/src/panels/swatches.rs
2026-10-29 11:40:25 +01:00
moppeeer e7cb2ba0c4 Merge remote-tracking branch 'origin/main' into color-panel-gradient-stop
# Conflicts:
#	crates/ui-egui/src/panels/swatches.rs
2026-10-29 11:40:16 +01:00
moppeeer c73f68bccf Merge remote-tracking branch 'origin/main' into status-bar-parent-page-box
# Conflicts:
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/chrome.rs
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:40:10 +01:00
moppeeer 6b57bebd6b Merge remote-tracking branch 'origin/main' into insert-pages-dialog
# Conflicts:
#	crates/engine/src/cmd/layout.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/pages.rs
2026-10-29 11:40:06 +01:00
moppeeer c5ac590cbf Merge remote-tracking branch 'origin/main' into recent-items-whole-number
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
2026-10-29 11:39:55 +01:00
moppeeer 88010aa328 Merge remote-tracking branch 'origin/main' into paragraph-border-links
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
2026-10-29 11:39:27 +01:00
moppeeer fffafb7122 Merge remote-tracking branch 'origin/main' into global-units-preferences
# Conflicts:
#	crates/engine/src/cmd/file.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/menus.rs
2026-10-29 11:39:22 +01:00
moppeeer 45751393ef Merge remote-tracking branch 'origin/main' into hyphenation-french-italian 2026-10-29 11:39:17 +01:00
moppeeer f0b580074d Merge remote-tracking branch 'origin/main' into direct-select-handles
# Conflicts:
#	crates/tools/src/select.rs
#	crates/ui-egui/src/canvas.rs
2026-10-29 11:38:53 +01:00
moppeeer 0d9b7c74ac Merge remote-tracking branch 'origin/main' into table-cell-keys
# Conflicts:
#	crates/engine/src/cmd/table.rs
#	crates/engine/src/cmd/text.rs
2026-10-29 11:38:49 +01:00
moppeeer c0af54eabd Merge remote-tracking branch 'origin/main' into table-hidden-chars
# Conflicts:
#	crates/ui-egui/src/canvas.rs
2026-10-29 11:37:40 +01:00
Hamza 5a53a3807c Check a parent page's frame on the pages that show it
The warnings composed every story once with no page. For a frame on a parent
page that is wrong twice over, and I measured both:

    exportPdf {"pages": [2]}
      ["the story in frame 9: 76 character(s) do not fit and are not in the
        export"]

Page 2 used no parent at all, so nothing of that frame was in its export to be
missing -- and the warning named no page, because a parent frame has no
document page of its own. It now resolves the pages that draw the parent page
and reports only those:

    {"pages": [1]}  ["page 1: 76 character(s) ... frame 9 ..."]
    {"pages": [2]}  []

The second half is page-dependent text. A parent frame 112 pt wide holding
"Continued on page <marker>" fits while the number is one digit and does not
once it is two, so composing it once could not tell:

    {"pages": [1]}   []
    {"pages": [9]}   []
    {"pages": [10]}  ["page 10: 1 character(s) ..."]
    {}               ["pages 10, 11, 12: 1 character(s) ..."]

Each page that shows the parent is composed as that page, and the pages are
grouped by how much they lose, so a story still gets one line.

Also from review: `story.get` now says `overset` is a UTF-8 byte offset into
`text`, not a character index. A test measures the difference on French text,
where the offset runs ahead of the character count.
2026-10-29 14:17:21 +05:00
Hamza f11f5f3b11 cli: a missing value is found before anything runs
Review caught a consequence of the previous commit. Standing the order check
aside so a missing value could be reported meant the value was reported
later -- after the exports before it had already been written. `run --export
first.png --scale --export second.png` wrote first.png and only then failed,
so an invocation that fails could overwrite a file that was already there.

`values_present` checks every option that takes a value before the order
check and before any export, so the line is refused as a whole. The test
writes a file at the export path first and asserts its bytes are unchanged.
2026-10-29 13:51:49 +05:00
NetterSilverandClaude Opus 5.5 4d88c0899e Keep stroke styles from joining the subpaths of a compound path
Found while testing crates/geom/src/stroke_style.rs:24-31 (offset_path: `(MoveTo(p), 0, true) => out.line_to(p)`; also samples/wavy at :58-98)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 09:21:20 +01:00
KnottyDyesandClaude Opus 5.5 d7bc6633e7 Compose: honor discretionary hyphens in single-line composition
Recognize U+00AD independently of automatic hyphenation in the greedy breaker, reusing the existing visible hyphen output. Cover both composers, zero-width unbroken soft hyphens, pattern suppression, and tagged and untagged PDF text extraction.

Fixes #1955

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:14:44 -06:00
Zaxaerith 53d4d9862a docs: correct sample document menu path (#1978) 2026-10-29 11:12:23 +08:00
Zaxaerith 5aba8cb017 docs: complete README architecture layer table (#1979) 2026-10-29 11:09:07 +08:00
VoiceLessQ 033fc2e17e PDF: count recompress decodes in the oversized-image test (#1586)
The test for a raster past the RGBA budget showed that recompress
skips the decode only through a one-second limit, which a slow CI
runner could fail and a fast machine could pass without the fix.
recompress now counts its decodes in a test-only per-thread counter,
and a unit test checks that the 16,385 px PNG doesn't reach the
decode while a 9 px one does. The export test keeps its output
checks and drops the time limit.
2026-10-29 00:12:33 -02:00
VoiceLessQ 30a0f08698 Story: copy a paragraph's format for all new returns at once (#1588)
insert_with_raw gave each return in the inserted text its own
Vec::insert of the paragraph's format, and each insert moves every
format after it. A ParaFormat is 2,888 bytes, so the time grew with
the square of the returns: Story::with_text took 2.2 s for 10,000
returns, 15.8 s for 20,000 and 90 s for 40,000. Placing a plain-text
file goes through it. The copies now go in with one splice.
2026-10-29 00:08:22 -02:00
VoiceLessQ d5f07d30eb PDF: check the RGBA budget before recompressing an image (#1586)
With compressImages on, PDF export re-encodes rasters as JPEG in
recompress, which decoded with the image crate directly. A gray PNG
that decode_rgba refuses still decoded there, was copied and encoded
as JPEG, and only then dropped: krilla refuses a JPEG wider or taller
than 16,384 px, and every raster past the budget is. Exporting the
23,000 px PNG that way took 4.2 s and 1 GB more memory, for a PDF
without the image.

recompress now asks too_large_to_decode first, the check decode_rgba
uses, so the export skips the image with the same warning at once.
2026-10-28 23:28:11 -02:00
VoiceLessQ 89ccfbb18c Autoflow: count each forced move once, and only visible breaks (#1209)
fit() counted every break character in the overset text and every
Start Paragraph option. A break followed by a paragraph whose option it
already meets moved the text once but asked for two frames, and breaks
that tracked changes delete or a hidden condition hides asked for
frames that composing never uses. Autoflow added those pages and
removed them again: 200 chapters that end in page breaks and start on
the next page took 61095 ids and 430 ms, now 798 ids and 16 ms.

Each paragraph composing places now counts once, when a visible break
ends the paragraph before it or its start option sends it on; after a
break it is at the top of a frame, where the option only fixes odd or
even page parity. A visible break inside a paragraph counts once more.
previous_visible_break and the new visible_break in designcraft-compose
are public, so the estimate and the composer use one rule.
2026-10-28 23:08:41 -02:00
Henrique CabralandClaude Opus 5.5 50b5936b78 IDML export: write the document's default StoryPreference
InDesign always writes a document-level StoryPreference in
Resources/Preferences.xml; DesignCraft only wrote one per story. IDML readers
that take the spread reading order from the document's preference, such as
Okapi's IDML filter, failed on DesignCraft's files without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:07:42 -04:00
VoiceLessQ 5eddc821ec Images: refuse a raster that would decode past 1 GiB of RGBA (fixes #1586)
decode_rgba relied on the image crate's 512 MiB limit, which bounds
only the decoder's own output. A gray or 1-bit image just under it
expanded four times to RGBA, and render made a premultiplied copy on
top: a 288 KB PNG of 23,000 x 23,000 pixels froze the first draw for
9 s and took the app to 4.5 GB. The size is now read from the header
and anything past 256 Mpx is refused before decoding. It draws as an
empty frame, and PDF export skips it with a warning.

A 300 ppi scan of an A0 sheet (about 139 Mpx) still opens.
2026-10-28 22:50:39 -02:00
KnottyDyesandClaude Opus 5.5 ed88f3e4b5 Compose: tables clear the footnote area
Reserve column and spanning footnotes when fitting inline table row groups. Continue rows in the next column or frame when the remaining space is too small, and include cell-local footnotes in measured row height.

Fixes #1901

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:44:34 -06:00
VoiceLessQ ca85be89d2 Autoflow: skip deleted paragraphs and ask for every forced frame at once (#1209)
The start option lookup now reads the next paragraph composing places,
past tracked-deleted ones. Frames that start options and breaks force
are counted and asked for in one round, so a story with a paragraph per
page no longer gets one frame per round.
2026-10-28 22:32:07 -02:00
VoiceLessQ e248b63165 Autoflow: a frame a Start Paragraph option ends early sets no fill rate (fixes #1209)
fit() counted the frame before a paragraph that starts on the next
column, frame, page or odd/even page as a full frame. With a short
title before such a paragraph, autoflow estimated a page per few bytes
left and added up to 2000 pages before removing them again: placing
2000 words took 35.6 s in the app, now 0.36 s, with the same pages.

The option is resolved through the paragraph style, so an imported
Word style counts the same as a local override.
2026-10-28 21:46:56 -02:00
VoiceLessQ ba1ee2329b Give anchored objects fresh ids when copying text frames (fixes #1522)
Copying a text frame cloned its story but left the objects anchored in
it with the original's item ids. Save does not validate, load runs
Document::check and rejects duplicate ids, so a duplicated or pasted
frame with an inline graphic produced a file that failed to open with
"not a DesignCraft document: duplicate item id".

renumber (Duplicate, Alt-drag copy, Paste, Paste into text, parent item
overrides) and the data merge copy now give every anchored item in the
copied story, its table cells, footnotes and endnotes a fresh id.
Anchored text frames from IDML get their own story copy. Assets are not
adopted again: the story copy already remapped them.

The IDML export tests that relied on duplication to create shared ids
now set the shared id explicitly, so the export guard stays covered.
Files already saved with duplicate ids still do not open.
2026-10-28 21:10:50 -02:00
aiongg 855d606325 Show file and message dialogs without blocking the frame (fixes macOS crash) (#1961)
* Show file and message dialogs without blocking the frame

A blocking rfd dialog inside the egui frame runs AppKit's modal loop; a
file dragged from Finder and held over the window then re-enters winit's
handler and aborts the app. Dialogs now go through craft-dialogs: a
command without a path asks with a continuation and returns, the host
shows the dialog at the end of the frame, and the continuation runs with
the answer on a later frame, in the document it was asked for. Commands
given a path stay synchronous. The replace-file question is a message
box from the same queue. Quit and Close wait for a Save As dialog's
answer. clippy.toml disallows rfd::FileDialog and rfd::MessageDialog.

* Raise the open file panel when File > Open is chosen again

* Repin craft-dialogs to the craft-libs PR head

* Queue dialogs asked by different code or documents; hold answers behind newer dialogs

Two asks for the same file dialog were treated as one, so Place for one
document and then Choose Image, or Place again for another document, ran
the first ask's action. An ask is now skipped (and the open panel
raised) only when the same code asked for the same dialog for the same
document. An answer that would replace an egui dialog opened since the
asking (an unsaved-changes question, Preferences) waits until it closes;
Quit counts it as pending.

* Repin craft-dialogs to the craft-libs PR head

* Allow the file dialogs' existence checks in the file guard

* Repin craft-dialogs to storytold/craft-libs main
2026-10-28 17:13:45 -04:00
Dylan M. Taylor 3a1edcd76d Honor CARGO_BUILD_TARGET in FreeBSD packaging for aarch64 release builds (#1959)
* Honor CARGO_BUILD_TARGET in FreeBSD packaging for aarch64 release builds

The shared release workflow cross-compiles FreeBSD aarch64 in an x86_64 VM and sets
CARGO_BUILD_TARGET. package.sh ignored it: it built for the host, read binaries from
target/release, took the arch from uname -m (amd64 in the VM), and ran the cross-built
designcraft-cli --version on the host.

Take the arch from the target, build and read binaries from the target's directory, and skip
the host-side --version run when cross-compiling. Unset CARGO_BUILD_TARGET keeps the old behavior.

* Pick FreeBSD archive arch from CARGO_BUILD_TARGET with a release_arch helper

Follow soundcraft's pattern: native builds use uname -m, aarch64-unknown-freebsd and
x86_64-unknown-freebsd map to their arch, and any other target exits 2. The --version check
runs only when the archive arch matches the host arch.
2026-10-28 15:35:48 -04:00
Claude 97b42e3476 Cross-references: the edited text's IDML export moves into its own method
Keeps story_para under clippy's cognitive-complexity limit, which upstream now enforces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-28 19:03:09 +00:00