Commit Graph
1043 Commits
Author SHA1 Message Date
dotCooCoo 95fdb96680 Text wrap: a clipped contour visits only the edges in each line band
A clipped contour now looks only at the edges of the outline and its clips
that reach a line band, and charges its work budget for those alone. An
outline with thousands of points elsewhere no longer exhausts the budget
and falls back to the projections' common part. The Text wrap row of
docs/typography-parity.md notes that an image over 40 megapixels, or one
that can't be decoded, follows the frame's path.
2026-11-12 05:19:22 -08:00
autocraft-bot[bot] 6b9c47fdb2 Apply review fixes
- crates/ui-egui/src/app/clipboard.rs
- docs/egui-037-migration-plan.md
2026-11-12 05:37:36 +00:00
dotCooCoo 544ebaf571 Text wrap: a relinked image's new outline recomposes the text around it
- each traced Alpha Channel outline has a serial number, and the composition signatures include the outlines in use, so a relink or link update that changes only the image data no longer leaves text laid out around the old image
- a wrap with the default alpha threshold is saved without it, as before thresholds were stored
- docs/typography-parity.md says Alpha Channel contours follow the image's visible pixels
2026-11-11 21:33:53 -08:00
dotCooCoo 630b5dfc43 Paste: images on the system clipboard paste as embedded images (#2204)
Paste ignored a bitmap on the system clipboard: a screenshot or an image
copied in a browser had to be saved and placed. Paste now places it.

- edit.pasteImage {base64, name?, spread?, at?} embeds the image in the
  selected empty frame, or in a new frame centered on `at` at its stored
  resolution (72 ppi when none) and at most 60% of the page width, as
  File > Place sizes it. Save and reopen and IDML export keep the pixels.
  Images over 64 million pixels and names without an image extension are
  refused.
- Ctrl/Cmd+V pastes the image egui delivers with no text, and Edit >
  Paste reads the clipboard's bitmap, or the first image file of a copied
  file list, when there is no text or the text is only an address.
- The menu read waits at most 1.5 s on its own thread, with one read in
  flight at a time.
- With a native menu, objects copied here still paste as objects until
  the pasteboard's change count shows a newer copy.
- ui.paste takes {image} for the control channel.
2026-11-11 20:50:24 -08:00
dotCooCoo 630c2e2bba Merge #1520 (object-style-paragraph-styles) onto main 2026-11-11 20:24:48 -08:00
dotCooCoo fc09aa9124 Merge main into feat/type-shortcuts 2026-11-11 20:23:58 -08:00
dotCooCoo 5d9fbb4d4e Docs: vertical justification moves grid-aligned lines by whole grid steps 2026-11-11 20:02:54 -08:00
dotCooCoo 3473b8d96e Text: Text Frame Options refuse an ids that is not a list of object ids
A call with an `ids` or `id` that is not an object id used to fall back
to the selected frame or the caret's frame. It now fails and leaves the
document unchanged.

The Properties panel's Text Frame section shows the frame Text Frame
Options change: with the Type tool, the frame the caret is drawn in.
2026-11-11 19:46:20 -08:00
dotCooCoo 27963d6d05 Justification: the dialog sets Auto Leading and Single Word Justification
Type > Justification and Paragraph Style Options > Justification now show
Auto Leading (0% to 500% of the type size) and Single Word Justification
(Align Left, Align Center, Align Right, Full Justify). Both fields show the
selected paragraphs' values, or blank where they differ, and OK writes
autoLeading and singleWordJustify only when they were edited. An Auto
Leading outside 0% to 500% keeps the dialog open with the reason.
2026-11-11 19:45:30 -08:00
dotCooCoo fd15245f4e Merge #2275 into fix/recovery-off-interface-thread 2026-11-11 19:08:22 -08:00
dotCooCoo 18be5b76f5 Text: Text Frame Options in a footnote or table cell change the frame that shows it
With the insertion point in footnote text or a table cell and no stored
frame on the caret, Text Frame Options changed the last frame of the
thread. They now change the frame the caret is drawn in: the frame that
holds the placed footnote, or the cell's own occurrence (not a repeated
header or footer), else the frame that shows the footnote reference or
the table's anchor.
2026-11-11 19:06:29 -08:00
dotCooCoo 7809f5e9da Text: frame columns, gutter, switches and indents are checked too
object.textFrameOptions refuses a gutter outside 0 to 15552 pt, a column
count outside 1 to 40 (it was clamped) and an on/off option that is not
true or false (it was skipped). The Control bar's Columns field stops at
40, as the other column fields do. The params text gives the ranges and
lists flexibleWidth.

type.para and paragraph style create and edit refuse left and right
indents outside 0 to 15552 pt and first and last line indents outside
-15552 to 15552 pt. A style made from the selection is checked with the
values it takes from the selection.

style.paragraph.apply and style.paragraph.redefine check that Minimum,
Desired and Maximum stay in order in each justification row they change,
over the overrides paragraphs keep, and refuse the change otherwise.
2026-11-11 18:42:32 -08:00
dotCooCoo 510269744a Text: Text Frame Options open from the Type tool and change the frame holding the caret
Object > Text Frame Options and Cmd+B are enabled with a text caret or a
text selection. Without ids or selected objects, object.textFrameOptions
changes the frame that shows the insertion point, or every frame that
holds part of the selected text, instead of every frame of the story.
The dialog shows that frame's options and keeps the frames it opened
with while the preview reflows the text.

An empty ids list and an id that is not in the document are refused.
Options a frame already has leave the document unchanged and add no
undo step.

Fixes #2396
2026-11-11 18:40:25 -08:00
dotCooCoo d490e0fb18 Data merge: a QR field at a text insertion point is refused instead of written as text
Insert Field with a QR column and the text cursor in a frame added a text placeholder, so a merge wrote the QR payload as plain text. It now returns "qr placeholders bind to a frame" and leaves the story unchanged.
2026-11-11 18:14:15 -08:00
autocraft-bot[bot] c93e93414c Apply review fixes
- crates/ui-egui/src/dialogs.rs
- docs/target-app-parity.md
2026-11-12 02:02:22 +00:00
dotCooCoo e90517be45 Text: paragraph spacing and justification values are checked
type.para, style.paragraph.create and style.paragraph.edit accepted any
number for paragraph spacing and justification. They now refuse Space
Before, Space After and same-style spacing outside 0 to 15552 pt, word
spacing outside 0% to 1000%, letter spacing outside -100% to 500%, glyph
scaling outside 50% to 200%, Auto Leading outside 0% to 500%, and a
Single Word Justification other than left, center, right or
fullyJustified. Each justification row needs Minimum <= Desired <=
Maximum over the values the paragraph or style resolves to after the
change, including the styles based on an edited style and the
paragraphs, table cells and notes that use them.

object.textFrameOptions refuses an inset outside 0 to 15552 pt or a list
that is not four sides, and a verticalJustification, firstBaseline,
autoSize or columnsKind that is not one of its names. These were applied
or skipped without an error before. IDML import reads these values as
before.
2026-11-11 17:51:19 -08:00
dotCooCoo e362820bc4 Data merge: an image field in text places the image, not its path
An image field inserted at a text insertion point, or typed there as
<<name>>, merged as text, so each record showed the image's path.
It now merges as an inline image at its natural size, scaled down to
fit the smallest text column of the story's frames. A missing file is
listed in missingImages and its marker is removed. An explicit image
role at a text insertion point places an inline image too.

An image path that starts with one separator (\Photos\a.jpg) is tried
from the drive's root first, then beside the data file and the
document.
2026-11-11 17:42:24 -08:00
dotCooCoo 54652717c0 Text Frame Options: a non-rectangular frame shows one Inset field
For an oval, a polygon or a frame with corner options, Inset Spacing dims
Top, Left, Bottom, Right and the link and shows one Inset field. OK sends
it as `object.textFrameOptions {inset: <number>}`, which sets all four
sides; compose keeps that inset from the frame's outline. Rectangular
frames keep the four fields.
2026-11-11 17:30:44 -08:00
dotCooCoo a7cd84849e Merge main into fix/table-story-target 2026-11-11 12:29:22 -08:00
.CooCoo 513f77b88e Tables: commands given a table id run with nothing in a table selected (#2607)
* Tables: commands given a table id run with nothing in a table selected

Table commands whose params list `story?` and `table?` (table.insertRow,
table.deleteColumn, table.options, the Convert Rows commands and the
others) were refused with "place the insertion point in a table" when the
call named the table but the selection was not in one. A call that passes
`table` now acts on that table whatever is selected. A table that is not
in the document, or not in the given `story`, is refused, and a `story`
that is not a non-negative integer is an error.

The menus are unchanged: with nothing in a table selected, the table
items stay disabled, and calls that name no table are still refused.

* Tables: a bad story is refused whether or not a table is selected

A call that names a table with a story that is not a non-negative integer (a string, a negative number, a fraction or null) was refused only when nothing in a table was selected. With a table selected, the command ignored the story and acted on the named table. Both paths now read story with one helper and refuse it the same way.

* Tables: an edit to a named table keeps a caret in footnote text

With the caret in footnote text, a table command that names a table
runs, and the edit cleared the caret's footnote address, so the next
typing went into the story. clamp_selection now leaves footnote
addresses alone, as in_cell does.
2026-11-11 19:56:37 +00:00
texjer a14c782b98 Merge commit '39d8469b' into HEAD 2026-11-10 18:54:58 -05:00
texjer 1126dfba62 Merge commit '39d8469b' into HEAD
# Conflicts:
#	crates/ui-egui/src/i18n/tests.rs
2026-11-10 18:54:56 -05:00
texjer 39d8469b1f Merge remote-tracking branch 'upstream/main' into HEAD
# Conflicts:
#	crates/engine/src/cmd/file/place.rs
2026-11-10 18:29:09 -05:00
skylar taylor-barrick c2211e0f41 Merge pull request #1161 from y-az/feat/anchored-text-frames
Anchored text frames: own stories and ids through copy, paste, duplicate, release and delete
2026-11-10 20:48:08 +01:00
95abb2b03b pt-BR: translate 996 missing interface strings and the unit names (#1539)
* pt-BR: translate the 634 interface strings added since the catalog was completed

Covers the strings the catalog lacked after #495: tool names, menu
paths (Edit > Menus), Japanese typesetting dialogs (ruby, kinsoku,
mojikumi, warichu, kenten), special characters, Load Styles, Text
Frame auto-size, Data Merge, preflight, font replacement and About.
Tools are "Ferramenta X", parent pages "página principal", stories
"artigo"; menu paths reuse the existing menu names. IT, VA and Y:
stay as they are, as in every other language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* pt-BR: one term per feature, the unit names, a catalog test and the docs

Review fixes for the new rows: Gradient Feather, stroke styles and type
on a path use the catalog's existing terms; warichu's Line Spacing no
longer reads as Leading; em/en dash, hair space, nonbreaking space and
hyphen and paragraph return take their Brazilian typographic names; the
file and word counts read correctly for one. File > New's unit names,
which reach tr at run time, are translated too.

The new test checks every pt-BR row's placeholders, padding and
ellipsis, and that each menu path is made of the menu names.

Docs: pt-BR re-measured at 2,416 of 2,419 strings (61% before) in
localization-parity.md, gaps.md (gap 21), target-app-parity.md and the
ROADMAP languages table and progress log. Readiness is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Docs: trace each localization count to its commit; list Russian

The 1,724 baseline was counted at a127c6d5 (#355); recounting that
commit by the same rule gives 1,723, with pt-BR at the same 1,057. The
2,419 strings are main at 438d7b6d. The 2,021 figure is gone: it was
the same rule at the last commit of 2026-10-10, not another scale.

Russian (ru, 1,705 of 2,419) joins the other shipped languages, which
makes six beyond the twelve; ROADMAP's two "Also shipped" lines become
one that includes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* pt-BR: the 12 strings main added since; docs re-measured at 2e36dcf9

Merging main brought window and guide commands, the InDesign shortcut-set
import and the credits' PRs column, which pt-BR didn't translate yet. Its
button label and result message reach tr as literals no other catalog
has, so they join the shared test's list of UI strings.

Re-measured at main 2e36dcf9: pt-BR translates 2,431 of 2,434 strings
(the Control panel's IT, VA and Y: stay English) and the unit names;
Russian 1,709 of 2,434. The PR adds 645 strings and 7 unit names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Docs: pt-BR at 2,574 of 2,941 strings by i18n-report; Russian listed

Re-measured on main f3bcc886 with `cargo xtask i18n-report`: pt-BR
translates 2,574 of 2,941 interface strings (1,933 before); the 367 it
lacks are mostly strings main added since 2026-10-23. pt-BR to full is
4-6 h. localization-parity.md, gaps.md (gap 21), target-app-parity.md
and the ROADMAP languages table and progress log say so, each with its
revision row; the ROADMAP entry is dated today, as the review asked.

Russian (1,855 of 2,941) joins the other shipped languages, eight beyond
the twelve, and ROADMAP's two "Also shipped" lines become one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* pt-BR: drop two rows main removed; the test lists eight live labels

`cargo xtask i18n-report` flagged ten pt-BR rows whose keys its
interface list lacks. "/1000 em" and "Import InDesign Set…" are gone
from the code, so their rows go. The other eight still reach tr
(Auto-Sizing, Color Theme, Minimum Height/Width, No Line Breaks,
Reference Point, the shortcut-import result and the unavailable-fonts
message) and join the test's list of UI strings.

The report now counts 2,939 strings; pt-BR translates 2,572 of them
(88%), Russian 1,853. The docs say so; the PR adds 632 strings and the
7 unit names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* pt-BR: the 364 strings main added since 2026-10-23

Keyboard Shortcuts sets (craft-keymap's dialog, import messages and
counts), panel docking, OpenType features, page presets, the Package
summary and the command dialog's parameter names. "Atalho", "conjunto"
and "escopo" for shortcut, set and scope; tab groups stay "grupo de
guias" and inks "de escala"/"especial", as in the catalog. Key names
stay as printed on keyboards.

`cargo xtask i18n-report` now lists only the Control panel's IT, VA and
Y: for pt-BR: 2,936 of 2,939 strings. The docs say so; pt-BR to full is
1-2 h (an in-app check and the older parent-page and story terms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* pt-BR review fixes: Affix in, Dir, End, Pathfinder and three clashes; doc scales

- "Affix in" (where footnote prefix and suffix appear) is "Afixos em".
- "Dir" is a folder in file.save, recovery save and links.copyTo and a
  direction in text.move: "Pasta/direção".
- The End key is "Fim", as Home is "Início".
- Pathfinder keeps its name ("Objeto/Pathfinder"), as "Pathfinder:"
  already did, instead of "Localizador de caminhos" beside "demarcador".
- Pattern (stroke dashes) is "Padrão de traço", apart from Default
  ("Padrão"); Skipped is "Pulados", apart from "ignoradas"; Heading is
  "Intertítulo", apart from Title and Section headings.

ROADMAP's "Also shipped" line now uses one scale (today's i18n-report of
2,939 strings); the pt-BR note names main ef6c3686, and Russian reads
1,855 of 2,939.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 13:50:31 +01:00
texjer 969eaa0aaf Merge remote-tracking branch 'upstream/main' into HEAD
# Conflicts:
#	crates/ui-egui/src/app/files.rs
#	crates/ui-egui/src/tests.rs
2026-11-09 13:51:45 -05:00
aiongg f16065eaee Merge main into feat/anchored-text-frames 2026-11-08 02:22:05 +00:00
Luis 1da6effdeb Release: refresh contributor credits for each release (#2424) (#2426)
About > Contributors still showed an old snapshot, because nothing
regenerated contributors/contributors.json before a release. Opt in to
the shared refresh from craft-common-actions: a read-only credits job
regenerates the snapshot for the release commit, and the shared release
workflow installs it before every source build. The snapshot is not
committed back.
2026-11-08 01:32:13 +01:00
Zinadin Zidan 085a120606 Preflight: white or [Paper] set to overprint is reported (#2401)
A fill, stroke or stroke gap set to overprint in [Paper] or in a process
colour that lays down no ink (C=0 M=0 Y=0 K=0, gray 0, RGB white, a 0%
tint) disappears on press. Preflight now reports it as whiteOverprint, and
ruby and kenten set to overprint in such a colour too. A spot ink (a white
ink or a varnish) prints whatever colour stands in for it, so it isn't
reported. Text has no overprint of its own, so white text knocks out and
prints, and isn't reported. The old Preflight note that PDF export drops
ruby and kenten overprint is gone: export writes it.
2026-11-07 00:27:10 +06:00
texjer d701ff01c9 Merge branch 'feat/place-cursor-queue' into feat/place-contact-sheet
# Conflicts:
#	crates/ui-egui/src/app/files.rs
#	docs/multi-image-placement.md
2026-11-05 18:41:09 -05:00
texjer 77116f3621 Merge branch 'feat/place-cursor-queue' into feat/drop-loads-place-cursor
# Conflicts:
#	crates/ui-egui/src/app/files.rs
#	docs/multi-image-placement.md
2026-11-05 18:41:09 -05:00
texjer d5bb8a378c Place: a browser batch waits for a PDF's options; a PDF first joins the cursor
Files delivered after a multi-page PDF were dropped when its Import Options opened, with a
status asking to choose them again, and choosing them again replaced the cursor. They now
wait with the PDF and carry on when it is confirmed; Cancel ends the batch, as a native
one's does.

A batch starting with a single-page PDF, or in a browser with one image before a PDF, filled
the selected frame instead of entering the place cursor. A browser delivers one Place's files
together, so several in one delivery now count as a batch. One file alone still fills the frame.
2026-11-05 18:40:41 -05:00
texjer dd311b8803 Place: the files after a multi-page PDF in a batch wait for its options
They were left behind in place_paths when the PDF's Import Options opened, so the
PDF was placed alone and the rest of what was chosen never loaded. They now wait in
the dialog and carry on when it is confirmed; Cancel ends the batch.
2026-11-05 15:45:45 -05:00
texjer 5e1cb976f9 Place: the 1000-image cap holds for the whole place cursor when appending
Appending only checked each batch against the cap, and a single image or a PDF
added with append wasn't checked at all, so repeated appends could grow the
cursor without limit. Refused before decoding a batch or adding an asset.
2026-11-05 15:45:21 -05:00
texjer 3111d4086a Place: the files after a multi-page PDF in a batch wait for its options
They were left behind in place_paths when the PDF's Import Options opened, so the
PDF was placed alone and the rest of what was chosen never loaded. They now wait in
the dialog and carry on when it is confirmed; Cancel ends the batch.
2026-11-05 15:45:06 -05:00
texjer ba2237f8d5 Place: the 1000-image cap holds for the whole place cursor when appending
Appending only checked each batch against the cap, and a single image or a PDF
added with append wasn't checked at all, so repeated appends could grow the
cursor without limit. Refused before decoding a batch or adding an asset.
2026-11-05 15:45:06 -05:00
francesco fantoni 68a2030614 fix(engine): write automatic recovery off the interface thread
The 30 s timer ran file.recovery.save on the UI thread, and
designcraft_format::save() deflated the entire package — every linked placed
file included — into a Vec in RAM: 3.6 GB and 30-100 s of zlib inside
DesignApp::ui() per autosave on a 190-page catalogue with 4.2 GB of links.

- file.recovery.save {background: true} snapshots the dirty documents (Arc
  clones; no bytes copied) and queues them on one "designcraft-recovery"
  thread, skipping a document whose previous write is still in flight.
  file.recovery.progress reports {written, busy, errors}; the app's timer uses
  it and polls from ui_frame_end while a write is outstanding. The synchronous
  command is unchanged for MCP, scripts and tests, and the web build still
  writes inline (it has one thread).
- designcraft_format gains write(), streaming the package to any Write + Seek,
  so a large save needs no second copy of the archive.
- A snapshot still queued or running when its document is saved, reverted or
  closed is cancelled before its atomic rename; a write that lands after the
  poll is discarded, so a crash cannot offer back a copy older than the saved
  file. A panicking write is the job's error instead of ending the thread and
  leaving the document stuck as running.

Tests: the_recovery_timer_writes_off_the_interface_thread;
recovery::a_background_save_writes_recovery_data_the_app_can_recover,
a_saved_document_is_not_written_in_the_background,
a_snapshot_whose_metadata_failed_is_still_the_documents_to_remove,
a_background_snapshot_whose_metadata_failed_is_still_the_documents_to_remove,
a_write_cancelled_midway_publishes_nothing, only_a_queued_write_is_cancelled,
a_panicking_recovery_write_is_an_error_and_the_writer_keeps_going;
format::writing_to_a_stream_makes_the_same_file_as_saving_in_memory.

Gates: cargo fmt --all --check; clippy -p designcraft-engine -p
designcraft-format -p designcraft-ui-egui --all-targets -D warnings;
cargo test -p designcraft-engine -p designcraft-format.

Part of #1923, split for review: leaving linked placed files out of snapshots
is a separate pull request on top of this one.
2026-11-05 21:21:58 +01:00
texjer 15eeacc845 Place: the files after a multi-page PDF in a batch wait for its options
They were left behind in place_paths when the PDF's Import Options opened, so the
PDF was placed alone and the rest of what was chosen never loaded. They now wait in
the dialog and carry on when it is confirmed; Cancel ends the batch.
2026-11-05 15:00:33 -05:00
texjer 5868b60365 Place: the 1000-image cap holds for the whole place cursor when appending
Appending only checked each batch against the cap, and a single image or a PDF
added with append wasn't checked at all, so repeated appends could grow the
cursor without limit. Refused before decoding a batch or adding an asset.
2026-11-05 14:54:26 -05:00
texjer 9c96afe383 Merge remote-tracking branch 'upstream/main' into feat/place-contact-sheet
# Conflicts:
#	crates/ui-egui/src/app/file_dialogs.rs
2026-11-05 11:59:37 -05:00
texjer 37cbfceaaa Merge remote-tracking branch 'upstream/main' into feat/drop-loads-place-cursor
# Conflicts:
#	crates/ui-egui/src/app/file_dialogs.rs
2026-11-05 11:59:37 -05:00
texjer aca5c8b84a Merge remote-tracking branch 'upstream/main' into feat/place-cursor-queue
# Conflicts:
#	crates/ui-egui/src/app/file_dialogs.rs
2026-11-05 11:59:37 -05:00
dotCooCoo 1191a1df52 Reject ambiguous table targets 2026-11-05 05:40:07 -08:00
AxelandClaude Opus 5.5 0e85e9e5ec Esperanto as a text language (#1142) (#1204)
Esperanto in Character › Language (panel, Properties, style options),
mapped to the BCP 47 tag eo, with English-style typographer's quotes.
IDML keeps the name as written ($ID/Esperanto). Not hyphenated yet:
morpheme-based hyphenation follows in #1201.


Claude-Session: https://claude.ai/code/session_01Y9j7XJDCmPc8UZLs9cTxtu

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 13:16:42 +01:00
4f3bb5340e Pages: parents and alternate layouts; export and print layouts and versions (#1342)
* Pages: more parents and alternate layouts as columns in the Pages panel

Parents
- layout.parents.duplicate copies a parent spread (pages and items) under the next free prefix.
- layout.parents.options also renames the prefix (1-4 characters, no "-") and the name; basedOn is now optional.
- layout.parents.new validates prefix and name and takes basedOn.
- Pages panel: a "+ New Parent" row adds B, C… in one click. The parents' context menu and the panel menu have New Parent…, Duplicate Parent Spread, Parent Options… and Delete Parent, named after the selected parent, or a submenu of the parents when none is selected. A new Parent Options dialog has Prefix, Name and Based on Parent.

Alternate layouts
- Section.layout marks the section that starts a layout; Document::layouts() lists them.
- layout.createAlternate copies one layout (from), not every page; new layout.alternate.duplicate, .delete and .options (rename, resize).
- Each layout starts a spread of its own; sections now follow their pages when pages are inserted or deleted.
- Pages panel: with more than one layout, each is a column side by side with a header (name, size, ▾ menu: create, duplicate, options, delete) and a + column to add one.

Tests cover the commands' happy and error paths, undo, serde, and the panel's menus and columns.

* Pages: address review of parents and alternate layouts

- Deleting a layout's first page keeps the layout's name when another section lands on the same page.
- A parent's full label (prefix-name) must be unique; parents may still share a prefix.
- Alternate layout names round-trip through IDML (Section AlternateLayout / AlternateLayoutLength).
- The Pages panel's selected parent is per document and no longer saved in ui.json.
- Layout ▸ Parent Options… and Alternate Layout Options… open the Pages panel's dialogs.
- The Pages panel computes the layouts once per frame.

* Export: layouts and versions in one export, with presets; print chosen layouts

- file.exportBatch writes the chosen alternate layouts (a file per layout, or all in one PDF) in several versions at once: PDF with any marks, bleed and standard, PNG or JPEG at a resolution. Each version goes in a folder of its own, and file names come from a template ({document}, {layout}, {version}, {date}). Names are made safe and unique. dryRun lists the files; without a dir the files come back as base64 (web).
- Export presets: file.exportPresets, file.saveExportPreset, file.deleteExportPreset, kept in prefs.json. Built-ins: Print + Screen, Press PDF per Layout, Images per Layout, All Layouts in One PDF.
- file.print takes layouts and separateJobs (a job per layout, titled after it).
- File ▸ Export Layouts and Versions… dialog: preset, layouts, versions (add from templates, duplicate, remove, quick PDF choices or custom marks), name template, and a live list of the files it will write. It opens from a layout column's ▾ menu as "Export Layout…". The desktop asks for a folder; the web downloads each file.
- The Print dialog prints chosen layouts, together or separately; the Export PDF dialog fills the page range from a layout.
- crate::io::create_dir_all keeps folder creation inside the automation write root.

* Export: address review of layouts and versions

- An exported image is capped at 80 million pixels, the limit EPUB page images already use.
- The Print dialog stays open and says why when no layout is chosen.
- Clicking a layout column's header leaves Edit Parents before going to the layout's first page.

* Pages, Export: address maintainer review of layouts and versions

- Sections (and the layouts they start) follow a moved page, as they do inserted and deleted
  ones: moving a page across a layout boundary no longer moves the other pages between layouts.
  Delete and move share `settle_sections`.
- Alternate Layout Options resizes only pages whose size changes: renaming a layout no longer
  forces every page to the first page's size.
- Export file and folder names that are Windows device names (CON, NUL, COM1…) get a leading `_`.
- An export without `dir` refuses past 512 MB of files instead of building them all in memory.
- A file that fails partway through an export to a folder says how many were already written.
- Parent Options and Print keep the dialog open, with what was typed and why, when the engine
  refuses.
- A duplicated layout's default name stays within the 100-character limit.
- rustfmt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pages: Alternate Layout Options sends the size only when it changed

The dialog starts at the layout's first page's size, so the engine couldn't tell a rename from a
resize to that size. The dialog now leaves out a width or height still at its starting value, and
`layout.alternate.options` resizes every page of the layout when a size is given, as before.
The test moves to the UI, where the size was coming from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 06:02:27 +01:00
texjer 2597e2d7f4 Merge branch 'feat/place-cursor-queue' into feat/place-contact-sheet
# Conflicts:
#	ROADMAP.md
#	docs/multi-image-placement.md
2026-11-04 19:57:10 -05:00
texjer b458bec4c5 Merge branch 'feat/place-cursor-queue' into feat/drop-loads-place-cursor
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/app/files.rs
2026-11-04 19:56:22 -05:00
texjer 92d3e74a39 Place: a multi-page PDF in a batch keeps the images loaded before it
Confirming the PDF's Import Options placed it without append, so in a batch such as [a.png, b.png, multi.pdf] it replaced the images already in the cursor. The dialog now carries append, on both the native and the delivered-file paths. A single multi-page PDF still replaces what the cursor holds.
2026-11-04 19:36:38 -05:00
texjer e4f82e243e Place: switching, opening or closing a document leaves the place cursor's tool
Review fix for #2309: set_active, add_document and close_document emptied the place cursor but
kept the placeGun tool, so the next canvas click failed with 'nothing loaded in the place
cursor'. One helper, Session::unload_place_cursor, empties the cursor and its queue and goes back
to the Selection tool when the place cursor's tool is active; validate_place_cursor uses it too.
Test: changing_documents_unloads_the_place_cursor_and_its_tool.
2026-11-04 19:03:59 -05:00
texjer ad611c1b6f Merge remote-tracking branch 'upstream/main' into feat/place-cursor-queue
# Conflicts:
#	ROADMAP.md
2026-11-04 19:03:47 -05:00