Commit Graph
2702 Commits
Author SHA1 Message Date
aiongg 64cc973df8 Tabs: style tab lists go through the same check as paragraph tab lists
The style path of the Tabs commands writes its list through
ParaAttrs::set_json_over, the same call format_paras makes for
paragraphs, so TabStop::checked_list sees every list a Tabs command
writes.
2026-10-10 16:15:02 +00:00
aiongg a2ff7d03f8 Drop caps: nested-style lists in table cells and notes clear an inherited drop cap style too
The IDML import pass walks each story's table cells, footnotes and
endnotes (nested, depth-bounded) as well as its own paragraphs.
2026-10-10 16:11:43 +00:00
aiongg 55fa985e38 Bullets and numbering: the tab position survives IDML
IDML has no attribute for the list Tab Position. Export writes it as the
left stop the composer adds for it, in the TabList of each style or
paragraph that sets the list type, the tab position or the tabs; a level
that ends an inherited list writes its tabs without the stop. Import
reads it back as an ordinary tab stop, so the composed text is the same.
2026-10-10 16:10:26 +00:00
aiongg fac18790bb Merge feat/cjk-typography into feat/cjk-typography-ui 2026-10-10 16:09:30 +00:00
aiongg 9c935ce50c CJK: leading model space below measures from the em box top, space above from the bottom 2026-10-10 16:09:30 +00:00
aiongg e20183033f Rulers: the two guides from the corner are one undo step
guide.add takes orientation: both with point: [x, y] and adds a
horizontal guide through y and a vertical guide through x in one edit.
Cmd-drag from the ruler corner uses it.
2026-10-10 16:05:50 +00:00
aiongg 6dda095396 Engine: a cancelled gesture puts back the revision it began at
Cancelling a drag, a preview or an IME composition restores the
document, its selection and now its revision. Revisions come from one
counter, so the numbers a cancelled preview used are never handed out
again and caches keyed by (uid, revision) never show a cancelled
preview.
2026-10-10 16:05:46 +00:00
aiongg 1276181cde Text: IME composition in place, commit as one step (#322)
The Type tool shows an input method's marked text in the story as a
preview of text.insert, underlined (the converting clause twice), with
the candidate window at the caret. The commit is one text.insert and one
undo step; a cancelled composition leaves the document as it was. Keys,
shortcuts, the clipboard and Undo wait while the IME composes; a click,
a tool or document switch, or any journaled command keeps the marked
text as typed. On macOS the app tells the input context to drop marked
text the tool kept.

With a split window, only the pane last clicked takes keyboard events
(both panes saw them, so typing went in twice).

The web build drops only real key names from text events, and nothing
while its text input has the keyboard, so autocompleted words survive.

The control channel's ui.ime, the headless MCP backend's ui.ime and the
MCP ime tool send IME events.
2026-10-10 16:05:36 +00:00
aiongg c846b92388 Merge feat/cjk-typography into feat/cjk-typography-ui 2026-10-10 16:03:53 +00:00
aiongg 30778c14a5 CJK: document kenten, shatai, grid, ruby and ICF status 2026-10-10 16:03:35 +00:00
aiongg 29a75b3607 UI: every paragraph border, shading, span and list setting in the dialogs
Paragraph Style Options › Paragraph Border and Paragraph Shading,
Span Columns and Bullets and Numbering show every attribute of the
model. The same sections open for the selected paragraphs from the
Paragraph panel menu and the Type menu: Paragraph Borders and
Shading…, Span Columns… and Bullets and Numbering…. The bullet
character can be picked from the font's glyphs in the Glyphs panel's
grid. New strings are translated.
2026-10-10 16:03:11 +00:00
aiongg 8f93548572 Paragraph borders, shading, span space and list levels: model, layout and IDML
Borders gain a stroke type (built-in or a document stroke style), cap,
join, gap colour and tint, a shape and size per corner, column or text
width, top and bottom edges, closed boxes in each part of a split
paragraph, and one box for consecutive paragraphs with the same
settings. Shading gains the same corners, edges and width, Clip to
Frame, and Do not Print or Export. Boxes are drawn once the lines are in
place, as filled paths where they aren't plain rectangles.

Span and split paragraphs keep at least their space before and after
from the column text. Lists have levels that number on their own and
restart after a higher level, ^1 to ^9 in the number text, a character
style for the label, a bullet font, left, centre or right alignment at
the line start, and a tab position for the text after the label.

All attributes load from older files with their defaults and
round-trip through IDML, apart from the tab position.
2026-10-10 16:03:11 +00:00
aiongg 4c4dedaa93 Ruby: clears kenten on the same side, stays with the kana it overhangs, overprints in Overprint Preview 2026-10-10 16:02:52 +00:00
aiongg b720ff6147 CJK: ICF top and bottom from the font's BASE table for grid and character alignment 2026-10-10 16:02:52 +00:00
aiongg 5365d0f7ca Drop caps: tabs in right-to-left drop caps advance to the tab stops
Measured from the column's right edge, past the right indent.
2026-10-10 15:57:10 +00:00
aiongg f9fb640229 Drop caps: a cleared drop cap style round-trips through IDML
A drop cap style set at a level ([None] included) restyles the leading Dropcap entry of the nested style list, in export and when composing. A paragraph or style without nested styles of its own that clears an inherited drop cap style is written with the inherited list, whose Dropcap entry it sets to [None], or with an empty list.
2026-10-10 15:57:10 +00:00
aiongg 276b6f3170 Drop caps: an IDML nested style list of its own clears an inherited drop cap style
A style or paragraph whose nested style list has no leading Dropcap entry gets no drop cap style, since its list replaces the inherited one, Dropcap entry included.
2026-10-10 15:57:07 +00:00
aiongg d9bc53d7f8 Ruby: placement, spacing, font, size, overhang, scaling and colour settings with JLReq layout and IDML 2026-10-10 15:56:16 +00:00
aiongg 289c4a6aa4 IDML: a placed PDF keeps its crop choice
The placed PDF's asset stores its Crop to choice (crop, art, trim, bleed,
media, or the content bounds with visible or all layers) and where that
box sits on the page. The graphic spans the box: the screen, PDF export,
rasterised exports and clipping-path tracing draw only that part of the
page, so a larger frame doesn't reveal the bleed.

IDML export writes the choice as PDFCrop, with GraphicBounds spanning the
box. Import reads PDFCrop back; the engine finds the box on the PDF page
after import (and after relink or update) and maps it onto GraphicBounds,
or gives a graphic without bounds the box's size. Documents saved without
a choice load with the crop box. The Place PDF dialog offers the content
bounds too, and links.list reports the choice.
2026-10-10 15:52:40 +00:00
aiongg 31a58a8501 CJK: kenten settings, shatai, auto tate-chu-yoko, rotate roman, roman word break and grid references 2026-10-10 15:50:32 +00:00
aiongg 70e41a297d Drop caps: nested styles start after the drop cap style
Test that the drop cap style counts as a leading "through 1 Dropcap" nested style, and describe the IDML form in ROADMAP. The previous drop cap commit also makes the cap reach down the text's leading rather than to where line N is set.
2026-10-10 15:50:14 +00:00
aiongg bcdbb68eee Drop caps: one-line caps keep a smaller size and set their tabs
A one-line drop cap keeps its characters' own point size when it is smaller than the text (verse numbers), and tabs inside a left-to-right drop cap advance to the paragraph's tab stops.
2026-10-10 15:50:05 +00:00
aiongg 21863563f3 Drop caps: a space after the cap doesn't widen the indent
Spaces right after the drop cap are set at its size on its baseline and leave the lines beside it starting where its characters end.
2026-10-10 15:49:48 +00:00
aiongg 7636af73e4 Drop caps: the cap style goes through IDML as a Dropcap nested style
IDML has no drop cap style attribute. Import reads the nested style list's leading "through 1 Dropcap" entry as the drop cap style; export writes the drop cap style as that entry. The drop cap style counts as that entry when composing, so nested styles after it start after the drop cap.
2026-10-10 15:48:58 +00:00
aiongg ade9eb6e69 Rulers: Spread, Page or Spine origin; Cmd-drag the corner for guides
Preferences > Units & Increments > Origin sets DocSettings.rulerOrigin
(document.preferences {rulerOrigin: spread|page|spine}, IDML
RulerOrigin). Rulers and the X/Y fields (Control, Properties, Info,
transform.set) measure from that origin plus the zero point; with a page
origin the horizontal ruler restarts on each page and an object measures
from the page it is on.

Cmd-drag (Ctrl) from the ruler corner adds a horizontal and a vertical
spread guide through the drop point. The live preview while dragging the
zero point is kept on the pasteboard like the committed value.
Translations for Origin, Spine and Lock/Unlock Zero Point.
2026-10-10 15:41:54 +00:00
Michael TownshendandGitHub Copilot f05e8f5d98 #201: route macOS text clipboard commands through egui
Forward native Copy/Cut/Paste for active canvas text through egui so the desktop integration can use the system clipboard. Add regression coverage for the native clipboard event path.

Co-authored-by: GitHub Copilot <198982749+Copilot@users.noreply.github.com>
2026-10-10 17:41:14 +02:00
aiongg a82174d53e Merge upstream main into fix/29-kerning-across-runs
Every conflicted file resolves as in the merge of main into feat/text-editing-and-frames (this branch carries #83 and #84 up to "Merge #83 into feat/text-editing-and-frames").
Conflict: ROADMAP.md, crates/compose/src/lib.rs, crates/engine/src/cmd/text.rs, crates/fonts/src/fontdb.rs, crates/idml/src/import.rs, crates/idml/src/tests.rs, crates/ui-egui/src/canvas.rs, crates/ui-egui/src/control.rs, crates/ui-egui/src/lib.rs: as feat/text-editing-and-frames's merge, without that branch's later copy and paste keys commit
Conflict: crates/compose/src/tests.rs: both sides' tests (this branch's kerning and kenten tests, main's Spanish hyphenation test)
2026-10-10 15:37:42 +00:00
aiongg 5551cf9a25 Merge upstream main into feat/text-editing-and-frames
The layout-parity files (ROADMAP, compose lib/tests, engine text, fonts fontdb, idml import/tests, canvas) resolve as in the merge of main into feat/layout-parity.
Conflict: ROADMAP.md: as feat/layout-parity's merge
Conflict: crates/compose/src/lib.rs: as feat/layout-parity's merge; this branch's trimmed trailing spaces follow main's warichu::place and come before the drop cap splice
Conflict: crates/compose/src/tests.rs: both sides' tests
Conflict: crates/engine/src/cmd/text.rs: as feat/layout-parity's merge; this branch's TypingFormat/typing_format and main's warichu_cmd
Conflict: crates/fonts/src/fontdb.rs: this branch's styles/traits_of/linked_style over style_traits; style_traits falls back to the family without main's format suffix (exact_style_traits)
Conflict: crates/idml/src/import.rs, crates/idml/src/tests.rs, crates/ui-egui/src/canvas.rs: as feat/layout-parity's merge
Conflict: crates/ui-egui/src/control.rs: main's page_index and automation_contract_tests, then this branch's tests module
Conflict: crates/ui-egui/src/lib.rs: this branch's ClipboardFn and main's Inbox of ImportedFile
Conflict: crates/ui-egui/src/menus.rs: main's Quick Apply tests, then this branch's copy and paste keys test
2026-10-10 15:36:43 +00:00
aiongg cd23a5fbbf Tabs: tab stops set through paragraph attributes are checked like the Tabs commands 2026-10-10 15:35:05 +00:00
aiongg 7f23a5e05b Merge upstream main into feat/layout-parity
Conflict: ROADMAP.md: this branch's Text engine, IDML interchange and Right-to-left lines; main's FID2, PDF export and Interface language lines
Conflict: crates/compose/src/lib.rs: list labels as main's number_label/bullet_label/empty-paragraph arms, kept in this branch's list_label form that marks label glyphs; main's layout_line call (ends_para, forced_mid) and warichu::place, then the drop cap splice; rule above uses main's first-line column/end_x span with this branch's rule_color; caret(): drop-cap branch, then main's stacked-row metrics
Conflict: crates/compose/src/tests.rs: both sides' tests
Conflict: crates/engine/src/cmd/text.rs: type.para doc lists the drop cap fields and main's rule fields
Conflict: crates/fonts/src/fontdb.rs: both (plausible/measured_top/typo_ascent and without_format_suffix)
Conflict: crates/idml/src/import.rs: main's table style regions, borders and alternating fill/stroke attrs (this branch's alt_fills helper dropped, unused); underline/strikethrough colour from main with this branch's tint(); frame_options takes the object-style chain's TextFramePreference elements, the frame's column rule colour resolved after it
Conflict: crates/idml/src/tests.rs: main's tests, plus this branch's added tests and helpers
Conflict: crates/ui-egui/src/canvas.rs: main's compose::highlight_quads, then this branch's drop-cap highlight
2026-10-10 15:34:23 +00:00
aiongg 209f758727 Rulers: a document zero point you can drag, reset and lock
The zero point is document state (DocSettings.zeroPoint, zeroPointLocked):
an offset from the top-left corner of each spread's pages, so every
spread measures from the same relative place and split views share it.
view.zeroPoint {at, reset} and view.lockZeroPoint {on} set it; moving it
is not an undo step and undo/redo keep it. Values are kept on the
pasteboard, from commands, files and IDML.

Rulers and the X/Y fields (Control, Properties, Info, transform.set)
measure from it. IDML reads and writes Document ZeroPoint.

Dragging from the ruler corner places it where released, with crosshair
lines while dragging; a click that stays in the corner does nothing; a
double-click resets it; the corner's context menu locks it.
2026-10-10 15:34:12 +00:00
aiongg 3ca0a4374e UI: CJK sections in Paragraph and Character Style Options 2026-10-10 15:32:32 +00:00
aiongg 7ed9f37d43 UI: Paragraph Style Options gains the remaining sections
Advanced Character Formats, OpenType Features, Underline Options and
Strikethrough Options reuse the Character Style Options sections. New
sections: Paragraph Border, Paragraph Shading, Keep Options, Span Columns
and Bullets and Numbering. The swatch menu is shared with Paragraph Rules.
The Span Columns layout option has its own Japanese caption.
2026-10-10 15:32:23 +00:00
aiongg 4f1be4b454 Styles: startAt null in a paragraph style continues numbering
style.paragraph.create and style.paragraph.edit store an explicit
startAt: null as Continue from Previous Number, so it overrides a Start At
the style would otherwise inherit.
2026-10-10 15:32:23 +00:00
aiongg a80d21161f IDML: a spread missing from the package is reported, and part names match leniently 2026-10-10 15:32:20 +00:00
aiongg d03dd226c5 Text: the paragraph mark's size counts in its line's auto leading
The paragraph mark is a character of its paragraph's last line, so its leading (auto: its size times the auto leading percentage) takes part in the line's largest-leading rule. It doesn't change the line's ascent or descent. Earlier lines of the paragraph are unaffected.
2026-10-10 15:30:32 +00:00
aiongg 53710aca6e Merge #336 (feat/cjk-mode) for the CJK features preference 2026-10-10 15:20:01 +00:00
aiongg 7d5af8961c Package: the packaged IDML links its images instead of embedding them
The images are already copied to the package's Links folder, and the
IDML links point there; it no longer also embeds a copy of each one.
2026-10-10 15:18:18 +00:00
aiongg 0f11a4599f IDML: placed PDFs keep their page and crop
Export writes a PDFAttribute with the page number and PDFCrop="CropPDF":
the graphic bounds already span the page's crop box, so the frame clips
to the chosen trim, bleed or media box as before. Import reads the page
number back instead of always showing page 1.

A placed PDF page that the PDF does not have (a hostile PageNumber, or
any document holding one) is left out of PDF export with a warning
instead of failing the whole export.
2026-10-10 15:18:18 +00:00
aiongg 580db95c71 IDML: font names with the $ID/ prefix resolve
AppliedFont values like "$ID/Arial" import as the plain family, in
paragraph and character styles, text ranges and composite font entries.
The font database also looks a family up without the prefix, so
documents that already hold one draw in the named font.
2026-10-10 15:18:18 +00:00
aiongg c205b53b10 Integration: German and Japanese entries for the Numbering & Section Options strings
#342 adds twelve Layout › Numbering & Section Options strings to the shared table; next's de.rs
(#311) and ja.rs (#272) keep a row for every shared key. The rows carry over the shared table's
German and Japanese columns, so lookups are unchanged.
2026-10-10 14:56:33 +00:00
aiongg ba8e5e90ef Merge #344: fix(ui): Highlight swatch list rows and keep panels clear of scroll bars 2026-10-10 14:56:15 +00:00
aiongg 816ce1b756 Merge #342: feat(ui): Lay out Numbering & Section Options like InDesign's
Conflict: ROADMAP.md: kept both entries (New Document, then Numbering & Section Options).
Conflict: crates/ui-egui/src/dialogs.rs: width match keeps "newDocument" => 960.0 and adds "numberingSection" => 560.0, dropping the PR's "newDocument" if is_rtl => 380.0; tests keep next's helper block and add only the PR's numbering_and_section_options_keep_their_actions_in_view.
Conflict: crates/ui-egui/src/i18n.rs: kept both TABLE tail blocks (File › New › Document, then Layout › Numbering & Section Options).
Conflict: crates/ui-egui/src/i18n/it.rs: kept both tail blocks.
2026-10-10 14:56:15 +00:00
aiongg 54504fa341 Merge #341: feat(engine): Document chapter numbering and section prefix rules
Conflict: ROADMAP.md: kept both entries (New Document, then Document chapter numbering).
Conflict: crates/engine/src/cmd/book.rs: book.paginate keeps #341's chapter resolution with fix/section-start-bound's saturating page count (u32::try_from) and saturating "pages" result.
Conflict: crates/engine/src/cmd/layout.rs: page_numbering_view_tests keeps both tests (section_start_numbers_are_bounded, section_prefixes_are_short_and_avoid_plus_and_comma).
2026-10-10 14:55:15 +00:00
aiongg 39f72c5cd4 Merge fix/section-start-bound: Layout: section start numbers are bounded and page numbering can't overflow 2026-10-10 14:54:46 +00:00
aiongg 1ec2136c84 Layout: section start numbers are bounded and page numbering can't overflow
layout.section refuses a startNumber outside 1..=99 999, and so does layout.documentSetup for startPage (both go through one start_number check against designcraft_doc::MAX_PAGE_NUMBER). Document::page_number walks continuing sections in a loop with saturating arithmetic, so start numbers loaded from a file can't overflow it, and book pagination saturates its running page count.
2026-10-10 14:50:48 +00:00
moppeeerandClaude Opus 5.5 e78407027e fix(ui): Keep Enter and Escape in the Save Document Preset popup; saved presets in the engine
Review follow-up. Enter in the Save Document Preset popup created the
document and Escape closed the whole dialog: the dialog footer took
both keys while a popup was open. The dialog now leaves Enter and
Escape to an open popup (read before it draws, as egui closes a popup
on Escape that frame): Enter in the preset name saves the preset,
Escape closes only the popup. Clicking the name field no longer closes
the popup either (menus close on any click; this one only outside), and
the field takes focus when the popup opens.

Saved presets lived only in the UI state, so `file.new {preset: name}`
refused them. They are now engine preferences (saved with prefs.json):
`file.savePreset {name, …the file.new settings}` replaces a preset of
that name and refuses built-in names, `file.deletePreset {name}` removes
one, `file.presets` lists them after the built-in ones (`saved: true`),
and `file.new {preset}` makes documents from them by name. The Saved tab
reads and writes them through those commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:37:21 +02:00
aiongg e0019d7005 Integration: Japanese entries for the New Document strings
#325 adds fifteen File › New › Document strings to the shared table; next's ja.rs (#272) keeps a
row for every shared key. The rows carry over the shared table's Japanese column, so lookups are
unchanged.
2026-10-10 14:23:41 +00:00
aiongg aee7e2e38d Integration: German entries for the New Document strings
#325 adds fifteen File › New › Document strings to the shared table; next's de.rs (#311) keeps a
row for every shared key. The rows carry over the shared table's German column, so lookups are
unchanged.
2026-10-10 14:23:41 +00:00
aiongg 0b5c4fefd4 Merge #325: feat(ui): Lay out the New Document window like InDesign's
Conflict: crates/ui-egui/src/lib.rs: both modules kept, mod new_document before mod object_control
Conflict: crates/ui-egui/src/dialogs.rs: the PR's translated ok_label plus next's findFont arm, translated the same way ("Replace All"); cancel_label is a match: newDocument "Close", findFont "Done", else "Cancel"
Conflict: crates/ui-egui/src/i18n/it.rs: both blocks kept (next's machine-translated and Tabs rows, then the PR's File › New › Document rows)
2026-10-10 14:23:26 +00:00