In InDesign, holding Cmd (Ctrl) with the Type or a drawing tool makes a
press select, move or resize as the Selection tool would, without
switching tools; the Pen family gets Direct Selection. The engine now
borrows that tool from a Cmd-held press to the release, keeps the chosen
tool, and shows the borrowed tool's cursor while Cmd is held.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011K1peyrh5MdZ6ttHkVq8Gh
The DOCX and IDML element-tree builders accepted any depth, and the
tree is then walked and dropped recursively; a DTD content model's
groups recursed without a budget. Each parser now refuses nesting past
a fixed budget with an error, before any recursive consumer runs.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011K1peyrh5MdZ6ttHkVq8Gh
Double-clicking selected a word, but holding the button and moving did
nothing. `text.extendTo` now takes `unit`: after a `text.selectAt`, the
selection grows by whole units from the unit first selected, in either
direction, as InDesign does. The Type tool sends the unit its clicks
chose with every drag.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011K1peyrh5MdZ6ttHkVq8Gh
Conflicts:
- crates/doc/src/build.rs: kept parent_page_of/parent_page/parent_guides,
sync_parent_margins and mark_differing_margins_local; inherited_guides(abs)
stays as a wrapper over parent_guides for the guide commands and dialogs.
Kept main's single_sided_page_shows_first_parent_page test next to
differing_margins_from_older_files_stay_local.
- crates/doc/src/page.rs (auto-merged): Spread::parent_page_index takes main's
rule (first parent page for left and single-sided pages).
- crates/engine/src/cmd/layout.rs: the parent block uses sync_parent_margins
instead of main's old-margins equality check; parent accepts prefix, label or
index; document pages set here become local. Main's
parent_margins_change_the_parent_and_follow_unoverridden_pages stays; added
applying_a_parent_gives_its_margins_and_local_margins_stay_local.
- crates/tools/src/snap.rs, crates/ui-egui/src/canvas.rs: guides read through
parent_guides (no per-page Vec); main's current_spread_page and
current_parent_page kept.
- crates/ui-egui/src/panels/properties.rs: main's parent-target margin fields.
- crates/format/src/lib.rs: upgrade and merge_duplicate_assets, then
mark_differing_margins_local.
- crates/engine/src/tests_idml.rs: both sides' tests.
- ROADMAP.md: main's file plus this PR's progress-log line.
Conflicts and how each was resolved:
- crates/idml/src/import.rs: main's linked_assets and insert_asset kept; the
crop is read beside main's page (PDFAttribute as a child or a property), and
linked_assets is keyed by (path, page, PdfCrop) so two crops of one page are
two assets.
- crates/idml/src/export.rs: PDFAttribute with PageNumber, PDFCrop and
TransparentBackground is written for every placed PDF, replacing main's
page > 0 block.
- crates/idml/src/tests.rs: main's file plus the `$ID/` font tests.
- crates/engine/src/cmd/file.rs: main's exact PDF page size, place cursor and
add_asset kept; Crop to stores the choice on the asset (the graphic spans
the box), so the place cursor's frame crop box is gone.
- crates/engine/src/cmd/{book,interchange}.rs: main's confined and reporting
imports, with the PDF crops resolved after them.
- crates/engine/src/cmd/print.rs: main's print_with, exporting with the
measured PDF boxes.
- crates/engine/src/cmd/links.rs: main's text links plus pdfCrop.
- crates/engine/src/cmd/{datamerge/fill,mathexpr,media,table}.rs,
crates/format/src/lib.rs: main's add_asset with the asset defaults.
- crates/engine/src/cmd/package.rs, crates/engine/src/tests_idml.rs: both
sides' tests.
- crates/fonts/src/fontdb.rs: main's style traits, looked up through
plain_family.
- crates/render/src/lib.rs: pdf_crop_box replaces pdf_page_box; main's
UserUnit test checks pdf_crop_box.
- crates/ui-egui/src/dialogs.rs: main's dialog with the content-bounds crops.
Also: Asset::same_file compares the PDF crop, so placing or saving no longer
merges two crops of one page; Asset literals added on main take the defaults;
main's PageNumber test checks the crop and that page 1 is written; new tests
a_pdf_page_placed_with_two_crops_imports_as_two_assets and
pdf_assets_with_different_crops_stay_apart.
#84 landed on main squashed (0f4f382b0), so the merge was resolved against #84's
tip as the base: main's version of #84 stands and only this branch's IME changes
are applied on top.
Conflicts and how each was resolved:
- crates/engine/src/cmd/text.rs: removed main's text.compose command, compose_text
and its compose_tests; text.insert keeps main's reset of the typing format after a
successful edit, skipped while an IME preview is open; reselection bumps the
revision.
- crates/engine/src/lib.rs: removed Session.composition and the hook that ended it
in execute_unguarded; set_active/add_document end the composition and reset the
Find scope; both test modules kept.
- crates/engine/src/tooling.rs: removed main's composition() and its
end_composition (the opposite semantics); begin_interaction no longer ends a
composition.
- crates/engine/src/cmd/edit.rs, file.rs, guard.rs, recovery.rs: main's code with
bump_revision in place of revision arithmetic; Revert takes the fresh state's
revision.
- crates/ui-egui/src/canvas.rs: removed main's IME block, composition highlight and
remembered caret rect; this branch's ime_event/ime_output stay. Main's dead-key
test is ported to these events (the accent stays as typed when the caret moves).
- crates/ui-egui/src/lib.rs, menus.rs: both sides' fields and checks.
- apps/designcraft/src/main.rs, apps/designcraft-web/src/web.rs: both sides.
- docs/control-protocol.md: ui.ime row plus main's ui.click/ui.drag params.
Also: the remaining `revision += 1` sites on main use bump_revision, and two tests
compare revisions with `>`; the IME candidate window follows the caret on type on a
path; IME status lines in ROADMAP.md, gaps, ui-, localization- and target-app-parity
docs and roadmap.md.
Conflicts:
- crates/compose/src/lib.rs: kept main's line placement (line_metrics with first
baseline tops, baseline grid snap) and this branch's leading model and Aki Below
advance. The Hyphenation Zone check uses Composer::single_line(), so the Japanese
single-line composer gets the zone too.
- crates/idml/src/tests.rs: kept both tests at the conflict (composer kinds, text
stroke alignment).
Also resolved:
- crates/idml/src/import.rs: removed the package scan for a Japanese composer and the
drop of [No paragraph style]'s leading model; the import keeps the file's leading
model and the composer kind, and compose applies the model per paragraph.
- crates/idml/src/tests.rs: the default-leading-model test lays out the document and
checks Roman spacing under the non-Japanese composers and the Aki Below spacing
under the Japanese ones; a new test round-trips a Japanese composer document.
The Typography panel's Other Features section lists the otf::TOGGLES tags no other section shows. Choosing a figure style or width alone also sets the other axis (the one all of the selection has, else Proportional or Lining), so IDML export writes a figure style instead of Default; turning one off clears both axes. Standard Ligatures reads and sets the ligatures attribute through type.char, and type.openTypeFeatures reports liga from it.
Conflicts: crates/tools/src/lib.rs, crates/tools/src/pen.rs and
crates/ui-egui/src/cursors.rs (add/add). The cursor work already in main is
kept as is (canvas.rs, ui-egui/src/lib.rs, tools tests.rs, ROADMAP.md take
main's version).
- pen.rs: the end-point check moves into hover_at as Hover::Continue, ahead
of Delete and Add; Down has one Continue arm and cursor() derives from
hover_at alone, so over a selected open path's end point the click
continues the path instead of deleting the anchor. Continuing from either
end (continue_from) is main's. The module doc is complete again.
- cursors.rs: Badge::Continue, the PenContinue glyph, and PenContinue in the
glyph test list.
- tests.rs: pen_over_a_selected_open_paths_end_point_continues_it.
load_swatches matched each name in a swatch exchange file against the
document, which by then held the file's earlier colors. A color whose
name the file had already used was dropped, or with replace it
overwrote the earlier color. A color with no name was added with an
empty name, and every later unnamed color was treated as a repeat of
it. A file of seven colors with a repeated name and three unnamed
colors added three of its six new colors.
load_swatches now names an unnamed color by its value, as object.color
names an unnamed color ("C=0 M=0 Y=0 K=55"), and sets named to false.
A name the file has already given gets the first free number ("Red 2",
"Red 3"), as Styles::unique_name numbers names. Names already in the
document follow the same rule as before: they are kept unless replace
is set, and a locked swatch is left unchanged. The swatch.load
description now states both rules.
Found while testing crates/engine/src/cmd/variables.rs:49
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>