#282's list test checks left-aligned paragraphs; with #284 a justified list item's first line also ends at the column edge and its text starts at the left indent. The test runs both alignments.
Conflict: crates/compose/src/lib.rs: next's drop cap aware tab closure with #282's left-indent tab stop argument
Conflict: crates/compose/src/tests.rs: kept both
In a hanging indent (a negative first-line indent), the tab after a
bullet or number went to the next default tab stop, past the left
indent: the first line's text started late and pushed the line past the
column edge.
The left indent is now a left tab stop of its own. A tab before it
reaches it unless an explicit stop comes first, so the first line's
text lines up with the lines below.
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.
#263 adds a ☰ menu to the Properties panel's Paragraph section; #281 routes every menu through menus::menu_button so a menu taller than the window scrolls and a scroll-bar click keeps it open. This one gets the same behaviour.
#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.
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
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'.
#284 justifies only the text after a line's last tab; next's mojikumi justification (from the merged CJK PRs) distributed the line's extra space over the whole line, moving the text before the tab. It now works on the same segment.
Conflict: crates/compose/src/lib.rs: tab loop keeps #282's tab_advance and records #284's last tab (no stop = default left tab); squeeze_last keeps next's mojikumi condition with #284's seg_justifies; single-word gaps keep next's moji filter after #284's skip(seg)
Conflict: crates/compose/src/tests.rs: kept both
Incorporate @aiongg's shared menu-button, anchor-aware sizing, and
reopen-reset work from #279 (717ddb6), retaining #281's clipped-submenu
and scrollbar interaction fixes.
Use the wrapper for workspace, zoom, and panel menus as well as the
menu bar. Preserve checkbox dismissal and cover anchor placement,
scroll reset, and very short viewports with regression tests.
#282 measures tabs in the line breaker from where layout starts the line; next's drop caps (#83) start lines beside the drop cap after it (on the right in RTL). The breaker's tab closure uses the same indent as next's width closure and line placement; measured from the frame edge, a tab looked wider and the line broke a word early.
Conflict: crates/compose/src/breaker.rs: next's greedy (end_elastic, kinsoku priority 2, unconditional shrink) with #282's tab advance in its three uses of the glyph advance
A line ended by a column, frame or page break takes the paragraph's
last-line alignment, so a Left Justify paragraph sets it flush left.
A line ended by a forced line break stays justified.
The break that moves the following text to the next column or frame
is the line's last glyph, so a space after the break no longer keeps
the text in the same column.
Line breaking gave tabs no width: a tab only reached its stop when the
line was laid out, so a line holding a tab ran past the measure by the
tab's advance. A footnote whose separator is a tab overflowed its first
line by the gap between the number and the tab stop, and body lines
with tabs did the same.
The greedy breaker now asks for each tab's advance at its position on
the line, computed by the same tab_advance helper line layout uses.
#263 adds a ☰ menu to the Properties panel's Paragraph section; #279 routes every menu_button through menus::menu_button so a menu taller than the window scrolls. This one gets the same behaviour.
At a large UI scale the menu bar menus ran off the bottom of the window,
so items such as Edit > Preferences could not be reached (#267).
Every menu_button popup (menu bar menus and submenus, workspace and zoom
menus, panel menus) now goes through menus::menu_button. It caps the
menu's height at the larger of the spaces below and above its button,
and the rest scrolls with the mouse wheel. Short menus are unchanged.
#274 adds Column Rules, Insert Column Rule, Tint %, Top Inset, Bottom Inset
and Horizontal Offset to TABLE, ar and pt_br. #218's test needs it.rs to
cover TABLE and ar; #236's needs uk.rs to cover TABLE, ar and pt_br. The
entries are machine-translated and go in each file's machine-translated
block.
#274 draws column rules from the frame's columns and breaks them where a
paragraph's lines cross the gutter. The span-columns fix puts text after a
spanning paragraph below it (a band of columns under the heading). The test
sets a heading mid-frame in three columns and checks that each gutter's rule
stops above the heading and resumes at its bottom, with text after the
heading flowing below it.
Conflict: crates/compose/src/lib.rs: next computes spacing_for earlier; kept the fix's spanning/span_paras/next_column lines and dropped its duplicate spacing line
Conflict: crates/compose/src/tests.rs: tests only; kept both
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
#112's and #142's Control bar tests found controls by accessibility label
(Fill, Columns, Y:). #255 replaces control_object with object_control::show,
whose captions are painted and whose last group with nothing text-specific
selected is Align. The tests now use #255's group responses
(("object_control", id)): #142's second-row test checks the X/Y/W/H group,
#112's scroll test uses Align bottom edges as the last control and opens the
Fill picker from the appearance group with a frame selected (#255 disables
the controls without a selection), and the one-line caption test checks that
the text frame's Columns group keeps its full width after scrolling.
Conflict: apps/designcraft-cli/src/main.rs: #254's USAGE const and usage_footer(); #127's '(--page, --scale and --pdf-options apply to the exports that follow them)' line moved into USAGE
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.
`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>
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.
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.
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.
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>
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
#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.