Commit Graph
2702 Commits
Author SHA1 Message Date
aiongg b4fa77ce65 Merge #257: Word import: keep tracked moved text (w:moveTo)
Conflict: contributors/people.toml: kept both entries
2026-10-10 00:59:37 +00:00
aiongg 1443c3a8a8 Span columns: text after a spanning paragraph flows below it
A spanning paragraph closes the band of columns above it. Those columns
are balanced first and the paragraph sits below the deepest of them; the
columns after it start below the paragraph, so text that moves to the
next column no longer restarts at the frame top over the heading. A
spanning paragraph that doesn't fit moves to the next frame. Span counts
below the column count set the paragraph across the first columns only.
2026-10-10 00:58:10 +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 36d1338fa7 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.
2026-10-10 00:26:13 +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
Ikki NishiharaandClaude Sonnet 5.5 70d9fafa0b i18n: translate the whole interface into Japanese
Japanese covered menu titles, about 140 menu items and panel names. Of the
1,391 rows of the shared table, about 1,240 carried the English text in
their Japanese column, so every dialog, panel field, tooltip and status
line was still English.

Add i18n/ja.rs, a full catalog in the shape of ar.rs and pt_br.rs. It has a
row for every key of the shared table and of the Arabic and Brazilian
Portuguese tables (1,430 strings), keeps the existing Japanese menu words
unchanged, and adds 213 rows for labels the code passes to tr() but no table
had: command labels without their trailing ellipsis (the shortcut list), menu
paths (the menu list), special characters, colour-profile names and a few
field labels.

tr("ja", ..) looks in ja::TABLE first and falls back to the shared table's
Japanese column. The shared table is left as it is.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 09:12:16 +09:00
SawizzleandClaude Opus 5.5 778822702b docs: register the MCP server from an installed release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:05:40 -07: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 30264ca004 Integration: Italian and Ukrainian entries for #263's strings
#218's test needs it.rs to cover every key of TABLE and ar::TABLE, and
#236's test needs the uk.rs keys to equal TABLE, ar and pt_br together.
#263 (Paragraph Rules) adds three keys neither catalog has: Paragraph
Rules…, Paragraph Rules and Rule Below. Its other new keys (Tint:,
Column) were already covered.

The Italian and Ukrainian entries are machine-translated and sit in each
catalog's machine-translated block; a native speaker should review them.
2026-10-10 00:01:03 +00:00
aiongg 35d011fccd Integration: one "Tint:" entry for #144 and #263
#144 (Character Style Options) and #263 (Paragraph Rules) each add a
"Tint:" row to the shared table and to the Arabic and Portuguese
tables, which the i18n tests reject as duplicates. One row stays in
each table, at #144's position, with #263's German, French, Spanish
and Japanese translations (#144's row kept those as English).
2026-10-10 00:00:40 +00:00
aiongg 24ab4a1f6b Merge #264: UI: the shortcut recorder waits for the key; Interactive for PDF and Digital Publishing open their panels
Conflict: crates/ui-egui/src/dialogs.rs: tests only; kept next's character style tests and #264's shortcut recorder test
2026-10-09 23:59:54 +00: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 0d491dbdd9 Workspaces: Interactive for PDF and Digital Publishing open their own panels 2026-10-09 23:53:55 +00:00
aiongg baa0fa3494 Keyboard Shortcuts: the recorder waits for the key pressed with the modifiers 2026-10-09 23:52:38 +00:00
aiongg cbe960ce55 Paragraph Rules: dialog for the selected paragraphs and a Paragraph Style Options section 2026-10-09 23:42:11 +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
Jeffrey ThayneandClaude Opus 5.5 c812c83d95 Word import: keep tracked moved text (w:moveTo)
Text inside a tracked move destination was dropped on .docx import, so
moved passages vanished from both places. Treat moveTo like ins (both
kept as accepted); moveFrom stays skipped like del. Regression test
covers ins, del, moveFrom and moveTo together. Fixes #256.

Also adds my contributor credit entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:04:18 -06:00
aiongg 92b1a96829 Integration: Italian and Ukrainian entries for strings added by the merged PRs
#218's test needs it.rs to cover every key of TABLE and ar::TABLE, and
#236's test needs the uk.rs keys to equal TABLE, ar and pt_br together.
Other PRs on next add 34 keys to those tables that neither catalog has:
- #80: Can't Open the File, Can't Place the File
- #83 (also carried by #81, #84, #96, #106): drop cap labels (Drop Caps,
  Drop Lines, Drop Chars, Lines:, Characters:, Align Left Edge, Scale for
  Descenders, dropcap)
- #84: Select an object or text to color its fill or stroke.
- #144: Character Style Options and its Basic/Advanced Character Formats
  and OpenType sections (Case:, Tint:, Skew:, Kerning:, Ligatures, ...)

The Italian and Ukrainian entries are machine-translated and marked with
a comment in each catalog; a native speaker should review them. The
ROADMAP's Ukrainian line drops its fixed key count (1,430), which no
longer holds.
2026-10-09 23:01:00 +00:00
aiongg 9b2304e996 Merge #236: Add Ukrainian interface translation
Conflict: ROADMAP.md: kept next's lines (Italian in the language list) and appended #236's Ukrainian sentence to the Interface language line
Conflict: crates/ui-egui/src/i18n.rs: kept both languages (it then uk in LANGUAGES, both tr() branches, both test loops; the unknown-label test checks every language)
Conflict: crates/ui-egui/src/menus.rs: kept both (app.language params doc lists it|uk, both menu entries, both language tests; the geometry test switches through uk and it)
2026-10-09 22:58:33 +00:00
aiongg 772489ace4 Merge #218: I18n italian
Conflict: ROADMAP.md: kept next's Right-to-left line (with IDML Kashidas) and next's Interface language line with #218's "Italian" added
Conflict: contributors/people.toml: kept both entries (moppeeer from #248, then emanuelebonaventura)
2026-10-09 22:57:53 +00:00
aiongg 76746924fd Integration: rustfmt the composite note label call in shape_para
The #126 merge resolution moved #126's composite-font note label shaping into #81's
piece pass and passed #81's StyleRuns; the resulting call is longer than one line.
2026-10-09 22:54:32 +00:00
aiongg 66bf8585f9 Integration: #177's glyph-cache test reads #230's underline list
#230 splits LineGlyphs::decos into underlines and strikes (underlines paint before the
glyphs, strikethroughs after). #177's in-flight owner test still read `decos`, so the
render tests did not compile once both were merged. The test's composition has an
underline, so it checks `underlines`.
2026-10-09 22:54:32 +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
moppeeer 88dab50978 Compact the Advanced object Control panel into aligned rows 2026-10-10 00:43:19 +02:00
aiongg 277c442814 Merge #96: Compose: an empty paragraph takes its mark's character format 2026-10-09 22:43:00 +00:00
aiongg fa8492232e Merge #106: Compose: fit horizontal text frames by the last baseline 2026-10-09 22:41:32 +00:00
aiongg caa01d063e Merge #81: Text: kerning survives a colour change (#29) 2026-10-09 22:41:29 +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 664408e9ac Merge #80: Alert dialog when Open/Place/drop/CLI/app.open fails; one opens_as_document rule for drops
Conflict: crates/ui-egui/src/dialogs.rs: kept both: #80's max-width match gains #146's closeDocument (380), and #146's closeDocument confirm arm stays before #80's importOptions through open_file
2026-10-09 22:36:43 +00:00
aiongg 09284f721c Merge #239: A shortcut that opens Quick Apply consumes its key; Quick Apply rows are left-aligned 2026-10-09 22:36:25 +00:00
aiongg 97e0109ca6 Merge #199: Swatch menus: the whole row (chip included) is clickable; the Contextual Task Bar's fill and stroke menus show chips
Conflict: crates/ui-egui/src/panels/properties.rs: tests only; joined #145's and #199's tests into one mod tests
2026-10-09 22:36:23 +00:00
aiongg d78b562fab Merge #145: Right-click a ruler to pick its units; the vertical ruler uses the vertical units; Language in Properties › Character 2026-10-09 22:36:01 +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 97edaf461a Merge #143: One searchable font menu for every font field; hidden .Name fonts left out; Contextual Task Bar draggable, with Pin and Reset
Conflict: crates/ui-egui/src/lib.rs: one test_window module with #112's wheel and panel_rect and #143's click_at
2026-10-09 22:34:30 +00:00
aiongg e9fa6a72e3 Merge #111: macOS menu Select All/Copy/Cut/Paste/Undo/Redo go to the focused text field 2026-10-09 22:34:17 +00:00
aiongg e80e4a656a Merge #146: File › Close, ⌘W and the tab × ask Save / Don't Save / Cancel for unsaved documents
Conflict: crates/ui-egui/src/lib.rs: kept both sides' tests in mod tests (#142/#138 and #146)
2026-10-09 22:33:55 +00:00
aiongg 9ca7d3f663 Merge #112: Tools panel uses two columns or scrolls when short; Control bar and app bar scroll sideways; tool button labels
Conflict: crates/ui-egui/src/chrome.rs: kept #112's horizontal ScrollArea in control_bar and put #142's horizontal_top inside it
2026-10-09 22:32:57 +00:00
aiongg 7ff03ade34 Merge #142: Control bar row is horizontal_top, so the second row of two-row groups shows 2026-10-09 22:32:43 +00:00
aiongg 3751d0346c UI: Quick Apply runs its first entry only on a fresh Return
Pressing Cmd+Return again while Quick Apply is open, or holding it until
it repeats, reached the palette as Enter and ran the first entry. The
palette now counts only a Return press that isn't a repeat and has no
Cmd.
2026-10-09 22:32:13 +00:00
kubuntugamer d0f12706bf fix(cli): --help prints usage to stdout and exits 0
Asking for help is not an error, but `designcraft-cli --help` shared the
catch-all arm with unknown commands, so it wrote the usage block to
stderr and exited 1 — unusable in a pipeline and wrong by convention
(`--version` already exits 0).

Split the arm: `--help`, `-h` and `help` now print the same usage block
to stdout and succeed, while an unknown command keeps it on stderr and
still fails. The usage text and links line are hoisted into a shared
`USAGE` const and `usage_footer()` so the two paths can't drift.

Fixes #243

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-10-09 17:22:55 -05: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