Commit Graph
1054 Commits
Author SHA1 Message Date
74a81f9fb0 Harden temporary files and release inputs (#153)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:46:19 -04:00
8146737e97 Text: a column, frame or page break ends its paragraph (#285)
A break character is stored as the last character of the paragraph it
ends, followed by the usual '\n' separator. The story model, paragraph
ranges, styles, editing and every exporter keep splitting paragraphs on
'\n' only, so the change stays in the places that create, lay out or
write break characters:

- IDML import writes the break and ends the paragraph, so the range that
  follows keeps its own paragraph style. NextOddPage and NextEvenPage map
  to new odd and even page break characters; IDML export writes a
  paragraph that ends in a break as that one Br with its
  ParagraphBreakType, so export and re-import give the same story.
- Word and RTF import, Type > Insert Break Character (with odd and even
  page breaks added) and typing end the paragraph after a break. Typing
  between a break and its return goes to the next paragraph; deleting
  either deletes both.
- Layout: a break that ends a paragraph leaves no empty line; the next
  paragraph starts in the next column, frame or page, as a paragraph start
  option would. Page breaks and Next Page start options move to the next
  frame on a later page; odd and even ones to the next page with that
  page number parity (no such frame: overset). A break inside a paragraph
  from an older document still moves the rest of that paragraph on.

Storing the break as a paragraph separator of its own would have changed
every place that counts or splits paragraphs on '\n'.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:36:47 -04:00
a50d279cba test(datamerge): pin facing-page record placement (#198 no longer reproduces) (#258)
* test(datamerge): pin facing-page record placement (#198)

#198 reported that with `facingPages: true` the merged records after the
first landed at identical coordinates on one spread, leaving a later page
empty. That no longer reproduces: `data.merge` now builds a new document
rather than duplicating pages inside the template, and the three records
land on absolute pages 0, 1 and 2 with no overlap.

Nothing in the engine changed here. These two tests pin the behaviour the
issue asked for, both at the one-record-per-page plan and at the short
block that tiles, so a future refactor of `tile_records` can't quietly
bring the stacking back.

Refs #198

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Merge origin/main; the facing-pages merge tests find each frame's page with page_of_item

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

---------

Co-authored-by: Codebuff <noreply@codebuff.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:35:34 -04:00
60ad82497b Text frames: column rules on screen, in PDF and in IDML; Column Rules in Text Frame Options (#274)
* Text frames: column rules draw between columns on screen and in PDF

A frame with Column Rule on gets a bar centred in each gutter, inside the
insets, broken around paragraphs that span columns; right-to-left and
vertical frames included. object.textFrameOptions takes columnRuleWeight
and columnRuleColor (unknown swatches are an error) and an inset array
whose null sides stay; document.inspect reports the rule.

* Text Frame Options: Column Rules fields; OK changes only edited fields

The dialog shows the selected frame's options, with an inset field per
side, and sends only what the user changed, so unequal insets survive.
The rule weight is read in points.

* Column rules: tint, horizontal offset and top/bottom insets; IDML import and export

Text Frame Options gains Tint %, Top Inset, Bottom Inset and Horizontal
Offset for the rule, and object.textFrameOptions and document.inspect
take and report them. IDML reads the ColumnRule attributes of
TextFramePreference on frames and object styles and writes them when a
rule is set.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:49:00 -04:00
zydvc26s67-web 92950c0938 Merge remote-tracking branch 'origin/main' into zydvc26s67-web-paragraph-styles-save-delete
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
2026-10-09 23:32:14 -07:00
zydvc26s67-web 85e3fc879c Merge remote-tracking branch 'origin/main' into zydvc26s67-web-paragraph-styles-save-delete
# Conflicts:
#	crates/doc/src/styles.rs
#	crates/engine/src/cmd/style.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/panels/styles.rs
2026-10-09 23:28:38 -07:00
01500ceb04 Paragraph Rules: a dialog for the selected paragraphs and a section in Paragraph Style Options (#263)
* Paragraph rules: a partial ruleAbove/ruleBelow edit keeps the other rule fields; Rule Above honours Text width

* Paragraph Rules: dialog for the selected paragraphs and a Paragraph Style Options section

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:28:09 -04:00
617e487dff Styles: Character Style Options dialog (#144)
* Styles: editing a character style renames it everywhere it is used, changes its Based On and unsets attributes; [None] can't be edited

* UI: Character Style Options edits a character style's name, Based On and character formats, opened by double-clicking the style, Edit in its menu, or New Character Style…

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:14:30 -04:00
e585f9495d Preserve browser import intent and document targets (#119)
* #89: preserve browser import context and PDF options

* #91: reject PDF pages before narrowing on Wasm

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:02:57 -04:00
aiongg 4d16a8ce3d IDML: layers import and export in stacking order; font names with a format suffix (#247)
* IDML: layers import and export in stacking order

* Fonts: a family named with a format suffix such as (OTF) resolves to the installed family
2026-10-10 01:51:03 -04:00
DWAA1660andCodex fb68b7cc2d M5: rebind embedded image links when opening relocated packages (#235) (#244)
Reuse packaged-link candidates for embedded IDML images and native files.
Resolve missing original paths beside the opened document, retaining the
embedded image bytes and any original link that still exists. Bound path
lookup allocations and cover actual package relocation through commands.

Make existing data-merge test path assertions portable to Windows.

Co-authored-by: Codex <noreply@openai.com>
2026-10-10 01:50:58 -04:00
skylar taylor-barrick 452134c13b FID2: preserve decoration color resets, geometry and paint order (#230) 2026-10-10 01:50:52 -04:00
Vikram Bhaskaran 67d4539df7 Distribute selected table rows evenly and keep the edit undoable. (#224)
The rows share one fixed height that preserves their composed total. Merged rows and rows that are not laid out are refused, and the table stays unchanged.
2026-10-10 01:50:43 -04:00
zydvc26s67-web 206bc0da74 Merge remote-tracking branch 'origin/main' into zydvc26s67-web-paragraph-styles-save-delete
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
2026-10-09 22:16:46 -07:00
aiongg 54a4a8d4ae Fonts: the Document Fonts folder is found whatever its case (#287)
Packages name the folder `Document fonts`; on a case-sensitive file system, opening a document beside it loaded none of its fonts. The exact name still wins when both exist.
2026-10-10 01:14:43 -04:00
kubuntugamerandCodebuff a9326be318 fix(engine): object.opacity accepts its documented lowercase blend modes (#275)
`object.opacity` documents `blend?: normal|multiply|…`, but parsed the
value with `serde_json::from_value`, which only accepts the enum's
PascalCase variant names. Every documented lowercase form was silently
dropped: the command returned `{"changed": 1}` while the stored blend
stayed as it was, so a client following the parameter description could
save and export the previous appearance without any error (#260).

Parse with the codebase's own `BlendMode::parse`, which is already
case-, space-, underscore- and hyphen-insensitive and covered by tests,
so `multiply`, `MULTIPLY`, `Color Burn`, `soft_light` and the serialized
`Multiply` all resolve to the same mode. An unrecognized value is now an
error rather than a silent no-op, matching how sibling commands treat a
bad parameter — restoring `normal` after `Multiply` really does go back
to Normal.

Three tests in `blend_mode_tests`: the documented forms apply, the
PascalCase names the Properties panel submits still apply, and an
unknown value is rejected without changing the item.

Fixes #260

🤖 Generated with Codebuff

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-10 01:14:38 -04:00
thaynejandClaude Opus 5.5 ecadfcee3e Autoflow: add only the pages breaks ask for (#266)
A frame that a column, frame or page break ends early held only a few
characters, and autoflow took that as the rate frames fill at: placing
text that starts with a page break added a page per few characters
(33 pages for 98 characters, 271 for a one-page Word section). The
estimate now comes from full frames only; with none yet, one page is
added and the story measured again. Regression test covers a leading
break and a break between every paragraph. Fixes #262.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:14:32 -04:00
jahalaandClaude Opus 5.5 ce3deb651a Commands: huge row and column counts can't exhaust memory (#137)
Three commands took a count from their parameters and used it unchecked.
Parameters come from MCP, the control channel and scripts, so the count
is hostile input (never-crash: cap input-sized allocations, checked
arithmetic).

- layout.createGuides made one or two guides per row and column with no
  upper limit: 30 million rows took 1.2 GB, and 2^32 - 1 would exhaust
  memory, which aborts the process past the command guard. A count over
  2^32 wrapped to a small one. Both counts are now capped at 100.
- edit.stepAndRepeat multiplied rows by columns as usize. In debug builds
  the overflow panicked; in release builds 2^32 x 2^32 wraps to 0, passes
  the 1000-copy limit and starts a grid of 2^64 copies. The product is
  now checked, and a grid that large falls back to `count` copies like
  any other grid over 1000.
- file.new stored any column count in the page (4 billion, or a wrapped
  value past 2^32). Page::column_rects builds one rect per column each
  time the columns are laid out. The count is now clamped to 1..=216,
  the range layout.marginsAndColumns already uses.

Each case has a regression test that failed before the fix.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 01:13:54 -04:00
aiongg fe16e00e37 PDF/A keeps placed images; right-to-left tables and columns (#128)
* PDF: PDF/A exports keep placed PNG, JPEG, GIF and WebP images by writing them without interpolation (#71)

* Tables: new tables take their paragraph's direction, and the Table panel sets a table's direction (#100)

* Text: stories made in a right-to-left-bound document fill their columns from the right (#100)
2026-10-10 01:13:48 -04:00
skylar taylor-barrick fc2739a8cd #61: honor MCP typing, story and page contracts (#118) 2026-10-10 01:13:29 -04:00
skylar taylor-barrick 4c6aa4cd4b browser-correctness: fix print, startup flags, and document MIME (#117) 2026-10-10 01:13:23 -04:00
aiongg e1ed4ff118 Merge #288: IDML: resolve table styles and correct cell height fitting
Conflict: crates/idml/src/import.rs: #288's table style import (based_on, region flags, borders, row/column fills and strokes) and #288's table() style resolution; next's alt_fills (#83) is unused and removed (#83's defaults hold under #288's cascade)
Conflict: crates/idml/src/tests.rs: next's file with #288's appended tests
Conflict: crates/doc/src/styles.rs: tests only; kept both
2026-10-10 05:12:12 +00:00
aiongg c8bdb25910 Merge #287: Fonts: the Document Fonts folder is found whatever its case 2026-10-10 04:32:08 +00:00
aiongg e1cef6ecc1 Fonts: the Document Fonts folder is found whatever its case
Packages name the folder `Document fonts`; on a case-sensitive file system, opening a document beside it loaded none of its fonts. The exact name still wins when both exist.
2026-10-10 04:21:10 +00:00
skylartaylor 56a8150709 IDML: resolve table styles and shared cell edges 2026-10-09 21:57:31 -06:00
aiongg 9c1637bee4 Integration: autoflow counts odd and even page breaks as breaks
#266's autoflow leaves a frame a break ends early out of its frames-per-text estimate; #285 adds
odd and even page breaks, which fit() did not recognise, so a short frame ended by one counted as
full and autoflow added a page per few characters left. fit() uses is_break_char. #266's loop
already adds the skipped page an odd or even break needs (each round adds at least one page until
the story fits); the test covers both parities from page 1.
2026-10-10 03:54:09 +00:00
aiongg 2a80cfe54c Merge #285: Text: a column, frame or page break ends its paragraph; odd and even page breaks
Conflict: crates/compose/src/lib.rs: start options, the break after the previous paragraph and a break inside a paragraph go through a local `go` that sends NextColumn to next's span-aware next_column (#276) and the rest to #285's Cursor::start; the in-paragraph break takes #283's `brk` (the line's last glyph); #283's ends_para uses is_break_char; Cursor keeps next's resume() and adds #285's start/next_page (#285's old next_baseline header dropped, next's is kept); the table branch is #285's
Conflict: crates/idml/src/tests.rs: tests only; kept both
Conflict: crates/textimport/src/docx.rs: tests only; kept both
2026-10-10 03:51:42 +00:00
aiongg 69f8d0fcdd Text: a column, frame or page break ends its paragraph
A break character is stored as the last character of the paragraph it
ends, followed by the usual '\n' separator. The story model, paragraph
ranges, styles, editing and every exporter keep splitting paragraphs on
'\n' only, so the change stays in the places that create, lay out or
write break characters:

- IDML import writes the break and ends the paragraph, so the range that
  follows keeps its own paragraph style. NextOddPage and NextEvenPage map
  to new odd and even page break characters; IDML export writes a
  paragraph that ends in a break as that one Br with its
  ParagraphBreakType, so export and re-import give the same story.
- Word and RTF import, Type > Insert Break Character (with odd and even
  page breaks added) and typing end the paragraph after a break. Typing
  between a break and its return goes to the next paragraph; deleting
  either deletes both.
- Layout: a break that ends a paragraph leaves no empty line; the next
  paragraph starts in the next column, frame or page, as a paragraph start
  option would. Page breaks and Next Page start options move to the next
  frame on a later page; odd and even ones to the next page with that
  page number parity (no such frame: overset). A break inside a paragraph
  from an older document still moves the rest of that paragraph on.

Storing the break as a paragraph separator of its own would have changed
every place that counts or splits paragraphs on '\n'.
2026-10-10 03:41:12 +00:00
aiongg 0f2dbd2fcd Merge #275: fix(engine): object.opacity accepts its documented lowercase blend modes
Conflict: crates/engine/src/cmd/object.rs: tests only; kept next's column_rule_tests and #275's blend_mode_tests
2026-10-10 01:23:28 +00:00
aiongg 66d6479c09 Merge #274: Text frames: column rules on screen, in PDF and in IDML; Column Rules in Text Frame Options
Conflict: crates/idml/src/import.rs: next's text_frame_options(&[&El]) object-style chain with #274's column rule fields; #274's frame_options takes the prefs list and looks up ColumnRuleStrokeColor through Prefs; the frame site resolves the rule colour's swatch after building options from the chain (the chain borrows the importer)
Conflict: crates/idml/src/tests.rs: tests only; kept both
Conflict: crates/compose/src/tests.rs: tests only; kept both
Conflict: crates/pdf/src/tests.rs: tests only; kept both
2026-10-10 01:05:14 +00:00
aiongg 02da5681fa Merge #266: Autoflow: add only the pages breaks ask for 2026-10-10 00:59:47 +00:00
kubuntugamer a2dbe793f3 fix(engine): object.opacity accepts its documented lowercase blend modes
`object.opacity` documents `blend?: normal|multiply|…`, but parsed the
value with `serde_json::from_value`, which only accepts the enum's
PascalCase variant names. Every documented lowercase form was silently
dropped: the command returned `{"changed": 1}` while the stored blend
stayed as it was, so a client following the parameter description could
save and export the previous appearance without any error (#260).

Parse with the codebase's own `BlendMode::parse`, which is already
case-, space-, underscore- and hyphen-insensitive and covered by tests,
so `multiply`, `MULTIPLY`, `Color Burn`, `soft_light` and the serialized
`Multiply` all resolve to the same mode. An unrecognized value is now an
error rather than a silent no-op, matching how sibling commands treat a
bad parameter — restoring `normal` after `Multiply` really does go back
to Normal.

Three tests in `blend_mode_tests`: the documented forms apply, the
PascalCase names the Properties panel submits still apply, and an
unknown value is rejected without changing the item.

Fixes #260

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-10-09 19:55:57 -05:00
aiongg 7d0bdbc916 Column rules: tint, horizontal offset and top/bottom insets; IDML import and export
Text Frame Options gains Tint %, Top Inset, Bottom Inset and Horizontal
Offset for the rule, and object.textFrameOptions and document.inspect
take and report them. IDML reads the ColumnRule attributes of
TextFramePreference on frames and object styles and writes them when a
rule is set.
2026-10-10 00:33:27 +00:00
aiongg 1efbf0f894 Text frames: column rules draw between columns on screen and in PDF
A frame with Column Rule on gets a bar centred in each gutter, inside the
insets, broken around paragraphs that span columns; right-to-left and
vertical frames included. object.textFrameOptions takes columnRuleWeight
and columnRuleColor (unknown swatches are an error) and an inset array
whose null sides stay; document.inspect reports the rule.
2026-10-10 00:26:13 +00:00
Jeffrey ThayneandClaude Opus 5.5 7b3afb0b21 Autoflow: add only the pages breaks ask for
A frame that a column, frame or page break ends early held only a few
characters, and autoflow took that as the rate frames fill at: placing
text that starts with a page break added a page per few characters
(33 pages for 98 characters, 271 for a one-page Word section). The
estimate now comes from full frames only; with none yet, one page is
added and the story measured again. Regression test covers a leading
break and a break between every paragraph. Fixes #262.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLcnoDFjqsh5x3MzVbfZeb
2026-10-09 18:02:15 -06:00
aiongg e1cc16325a Merge #263: Paragraph Rules: a dialog for the selected paragraphs and a section in Paragraph Style Options
Conflict: ROADMAP.md: kept next's IDML1 line and #263's Paragraph Rules line
Conflict: crates/compose/src/lib.rs: Rule Above uses #263's first line and Column/Text width with #83's rule_color() for colour and tint
Conflict: crates/engine/src/cmd/text.rs: type.para params doc lists next's drop cap fields and #263's ruleAbove/ruleBelow; format_paras is #263's
Conflict: crates/ui-egui/src/dialogs.rs: kept both confirm arms (characterStyleOptions, paragraphRules) and both sets of tests
2026-10-09 23:59:08 +00:00
aiongg fa13bf24f4 Paragraph rules: a partial ruleAbove/ruleBelow edit keeps the other rule fields; Rule Above honours Text width 2026-10-09 23:42:11 +00:00
aiongg bb8a6a23fa INDD: open .indd files through indd-utils
InDesign documents and templates (.indd, .indt) open through File › Open,
drop, the web inbox, file.open, file.openBytes, file.openIdml and the CLI.
The engine converts them to IDML in memory with indd-utils
(github.com/aiongg/indd-utils, rev 9cf456d, MIT OR Apache-2.0, no
dependencies, builds for wasm) and imports the result through the existing
IDML path. The converter's warnings come first in the open result's
`warnings`. A file the converter can't read is an open error ("can't
convert the InDesign file: ..."), shown in the Can't Open the File alert.

Routing: a path or name ending in .indd/.indt, or bytes starting with the
INDD signature (the web inbox passes the name without its extension).
opens_as_document and the desktop and web Open dialogs list indd/indt.

Tests use synthetic bytes only: signature-bearing junk, a zero-filled
.indd and a short .indt all fail as open errors with no document added;
a dropped unreadable .indd raises the open alert.
2026-10-09 23:06:40 +00:00
aiongg 9f0b34e876 Merge #177: Memory: release retired layouts and reserve text growth exactly
Brings #175 (abf9a40, PSD fixed-size chunks).
Conflict: crates/engine/src/cmd/text.rs: tests only; kept #118's story_query_contract_tests and #177's reserve_tests (insert()'s code merged cleanly: #177's reservations around #84's typing format)
2026-10-09 22:53:16 +00:00
aiongg ee24cb949b Merge #129: Fix IDML Mojikumi spacing, kinsoku composition, and nested text preflight
#129's first commit (4f8d230) holds #124 and #126, which are already merged. Each conflicted file is a three-way merge of #129's own commits (4f8d230..pr/129) onto this branch, which takes #129's side against #126 and keeps the chain's (#81/#84) code.
Conflict: ROADMAP.md: kept both entries
Conflict: crates/compose/src/shape.rs: #129's Mojikumi metrics added; #81's pieces, the #126 composite-note piece and the #124 inline-object metrics unchanged
Conflict: crates/compose/src/tests.rs: tests only; #129's tests added, no duplicates of #124/#126's
Conflict: crates/engine/src/cmd/fonts.rs: #129's pub(super) for_each_story; #126's tests kept once
Conflict: crates/engine/src/cmd/preflight.rs: #129's nested-story paragraph rules; the used_fonts loop kept once
Conflict: crates/engine/src/tests.rs: tests only; #129's test added
Conflict: crates/fonts/src/fontdb.rs: #126 content (same in #129); no change
Conflict: crates/idml/src/import.rs: #129's Mojikumi import added
Conflict: crates/idml/src/tests.rs: tests only; #129's tests added
Conflict: crates/ui-egui/src/dialogs.rs: #126 content (same in #129); no change
Conflict: crates/ui-egui/src/lib.rs: #126 content (same in #129); no change
2026-10-09 22:52:22 +00:00
aiongg 988e45b0bb Merge #126: Fix composite font resolution, missing-glyph diagnostics, and document-wide font replacement
Conflict: crates/compose/src/shape.rs: #81 collects paint-only pieces and shapes them later, #126 shapes composite-font note labels inline. A footnote reference in a composite font becomes a Piece with `composite_note`, shaped alone in the piece pass with #126's per-entry label code (from the run's env, as in #126); other references stay ordinary pieces. #126's label fallback in shape_segment passes #81's StyleRuns.
Conflict: crates/fonts/src/fontdb.rs: #126's canonical_family lookup sits at the top of exact_style_traits, so #81's styles/traits_of/linked_style and #247's format-suffix fallback all use it; face() resolves canonical, then canonical without the format suffix, then #126's substitute_family, through #247's find_or_load; has_family is canonical_family as named or without the suffix
Conflict: crates/ui-egui/src/lib.rs: kept #84's system-clipboard copy match and added #126's Find Font on open and _fonts reset after it
Conflict: crates/ui-egui/src/dialogs.rs: OK label match has #146's Save and #126's Replace All; #80's alert-only OK kept, #126's Done label for findFont; find_font keeps #143's font_combo with #126's FALLBACK_FAMILY default
Conflict: crates/compose/src/tests.rs, crates/idml/src/tests.rs: tests only; kept both
Conflict: ROADMAP.md: kept both entries
2026-10-09 22:49:44 +00:00
aiongg 01799b4927 Merge #124: IDML: preserve anchored text frames and footnote artwork
Conflict: crates/compose/src/shape.rs: inline object on the baseline: #124's rule (its box alone sets the extents, not max with the font's) applied to every metric #83 tracks (ascent, typo_ascent, cap, xh); the above-line arm keeps #83's four fields
Conflict: crates/compose/src/tests.rs: tests only; kept both
Conflict: crates/engine/src/tests.rs: tests only; kept both
Conflict: crates/idml/src/tests.rs: tests only; kept both
2026-10-09 22:46:08 +00:00
aiongg 83e1b768e6 Merge #247: IDML: layers import and export in stacking order; font names with a format suffix
Conflict: crates/fonts/src/fontdb.rs: kept #81's typo_ascent/plausible and #247's without_format_suffix; the format-suffix fallback goes inside #81's style_traits() through exact_style_traits() (same as wip 57ebfb2)
Conflict: crates/idml/src/tests.rs: tests only; kept #83's drop-cap and object-style tests and #247's layer-order test
2026-10-09 22:44:34 +00:00
aiongg d268d998a9 Merge #230: Preserve text decoration color resets, geometry and paint order
Conflict: crates/idml/src/import.rs: kept #230's Underline/StrikeThru colour line (Text Color resets) and #83's tint() helper
Conflict: ROADMAP.md: kept the chain's Text engine line and added #230's FID2 line
2026-10-09 22:43:44 +00:00
aiongg fa8492232e Merge #106: Compose: fit horizontal text frames by the last baseline 2026-10-09 22:41:32 +00:00
aiongg 71074a987b Merge #84: Text editing, snapping and text threading (#23)
Conflict: crates/ui-egui/src/control.rs: tests only; kept #118's automation_contract_tests module and #84's tests module
Conflict: crates/ui-egui/src/lib.rs: kept #119's ImportedFile Inbox type and #84's ClipboardFn
Conflict: crates/ui-egui/src/menus.rs: tests only; kept #239's two Quick Apply tests and #84's copy/paste test
2026-10-09 22:41:29 +00:00
aiongg 4b24103b24 Merge #83: Layout: first baselines, Jump Object wrap, drop caps, IDML defaults 2026-10-09 22:40:47 +00:00
aiongg e5e48d2ed1 Merge #119: Web Place/Open/swatches remember the document and purpose they started from; multipage PDF Place shows the page dialog in the browser
Conflict: crates/ui-egui/src/lib.rs: kept #119's drain_inbox, import_file and ImportRequest (and its Services.inbox doc); import_file's and confirm_pdf_import's file.place/file.openBytes go through #80's open_file, so a failure shows the alert; import_file uses #80's opens_as_document for layout files
Conflict: crates/ui-egui/src/dialogs.rs: kept #119's pdfImport arm (async confirm, target activation); its native file.place goes through #80's open_file
2026-10-09 22:38:07 +00:00
aiongg f4f0896f85 Merge #144: Character Style Options dialog; style.character.edit renames everywhere, validates Based On, null unsets; New Character Style opens the dialog
Conflict: crates/ui-egui/src/dialogs.rs: took #144's structure (shared basic_character_formats and character_color sections); paragraph_style_options reads a clone of the document as #143 does, so the font menu can borrow the app; basic_character_formats takes &mut DesignApp and uses #143's font_combo (font_menu_rows is gone) with a × button that clears a style's family
2026-10-09 22:35:29 +00:00
aiongg c18912db69 Merge #128: PDF/A keeps placed images; right-to-left tables and columns
Conflict: crates/pdf/src/export.rs: took #223's side (RasterMode with #147's decode check) and dropped #128's Exporter interpolate field; #128's RTL story/table work and its PDF/A test are kept
2026-10-09 22:16:55 +00:00