* Track Changes: emptying a paragraph no longer turns its mark into the text's revision (#864)
Paragraph::delete copies the formatting of what it deletes onto the paragraph
mark when it empties the paragraph. That copy included the revision ids, so
accepting a tracked deletion of a whole paragraph text made the mark a deleted
mark too, and Accept All joined the following paragraph. Rejecting typed text
in an empty paragraph likewise rejected the paragraph mark. The mark now keeps
its own ins, del and fmt_change.
* Credit Silvertree2010 in the About window
* Docs: update the progress docs for this change
* Docs: Last reviewed stays as it was
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Microsoft Word treats generated numbered-list labels as independent words
in document word statistics, while omitting them from character counts.
Previously, word counting in WordCraft inspected plain run text only,
causing sample_document() to report 357 words instead of Word's 361.
- Add Document::story_word_count and story_word_count_of to compute words directly,
including generated numbered labels while skipping bullet markers and empty labels.
- Handle LevelSuffix::Nothing correctly by counting labels as independent words
without string concatenation artifacts.
- Keep Document::plain_text unadorned to maintain exact run text across docbin,
clipboard, and tests.
- In review.wordCount, report words via story_word_count while preserving character
counts from plain_text.
- Add regression tests covering numbered lists, bullets, LevelSuffix::Nothing, and
review.wordCount word vs character parity.
Fixes#893
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Docs: list the CharProps and ParaProps fields (#865)
* docs/mcp.md: color also takes RRGGBB and #RRGGBB (#865, #881)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A character colour given as a string now reads the way format.color reads it: RRGGBB, #RRGGBB, or auto/automatic in any case. The serialized forms ("Auto", {"Rgb": [r, g, b]}) read as before and are still what gets written, so documents and settings on disk round-trip unchanged. A colour that isn't one of these is still a parameter error.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Adds the 374 strings the Polish catalog still lacked, mostly from #750 by
@olekkut, with a few corrections, and drops the Russian exception from the
Polish coverage test now that Polish has every Russian key.
Co-authored-by: Aleksander Kutycki <74899889+olekkut@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Open RTF in its code page, and Word/RTF files whose extension is wrong
RTF import read every 8-bit byte as Windows-1252, so Cyrillic (and any
other non-Western) RTF came out as mojibake. Bytes now decode in the
current font's code page (\fcharset, \cpg), else the default font's,
else the document's (\ansicpg), for the single-byte Windows code pages
874 and 1250-1258.
A .doc, .docx or .rtf whose content is another of these formats now opens
by its signature, as Word does: RTF saved as .doc (hh.ru résumés and
accounting exports are commonly saved that way), a .docx renamed .doc, a
Word 97-2003 file renamed .docx.
* RTF: read the page size and margins, lay tables out on their cell edges
RTF documents opened on US Letter with 1" margins whatever they said, and
every table got equal columns and a full grid. Russian accounting and HR
exports (A4, 2 cm margins, borderless layout tables whose rows have
different \cellx edges) came out about twice as long as in Word.
- \paperw/\paperh/\margl…\margb (and the …sxn section forms), \headery,
\footery and \landscape set the document's section; out-of-range values
keep Word's defaults. Tables size to that page.
- A table's column grid is the union of its rows' \cellx edges; each cell
spans the grid columns between its left and right edges, as Word does.
- A table with no left/right cell borders is laid out without the grid
style (\clbrdrb rules alone don't make it ruled; \brdrnone is no border).
On 44 RTF files (mostly .doc-named exports) checked against Word's page
counts: total page-count difference 198 -> 30, exact matches 3 -> 17, none
worse.
* RTF: paragraph spacing and indents, RTF defaults, the default font
- \sb, \sa, \sl/\slmult (multiple, at least, exactly), \li/\lin, \ri/\rin
and \fi are read onto each paragraph (new model::Spacing); \pard resets
them.
- The document's paragraph defaults are RTF's (single spacing, no space
around paragraphs) instead of a new document's 1.15 and 8 pt after.
- Text that never selects a font is in the default font (\deff, font 0
when the file names none), as Word shows it.
Against Word's page counts on 251 real documents (44 RTF, mostly .doc-named,
plus .docx): exact matches 212 -> 216, within 10% 248 -> 249; a 68-page
Russian federal law RTF goes from 89 to 73 pages (the rest is the Word
installation's own default margins, which the file doesn't name).
* RTF: import headers and footers
\header, \headerr, \headerl, \headerf and the \footer… forms were skipped,
so letterheads, logos and "page N" lines were lost. Each group is now read
into its own output (the body's paragraph, table and row state is set aside
and put back when the group closes), built into the document with its
pictures (new model::part_blocks), and attached to the section as the
default, first-page or even-page header or footer; \titlepg sets the
separate first page. A header left open by a truncated file still ends.
40 of 41 RTF files in the corpus have headers or footers; against Word's
page counts exact matches go 216 -> 218 of 251, none worse.
* RTF: cell and paragraph borders
Tables either got a full grid (any left/right cell border) or none, and
paragraph borders were kept only on empty paragraphs (as a rule), so the
rules under letterheads and section titles were lost.
- Cells: \clbrdrt/l/b/r with their line style (\brdrs, \brdrdb, \brdrdot,
\brdrdash, \brdrth, \brdrtriple, \brdrwavy…, \brdrnone), \brdrw width and
\brdrcf colour become the cell's own borders (model::Cell::borders); RTF
tables get no grid style, so only the borders the file names are drawn,
as in Word.
- Paragraphs: \brdrt/b/l/r likewise become the paragraph's borders
(model::Para::borders), reset by \pard; an empty paragraph with only a
bottom border stays a rule.
40 of 41 RTF files in the corpus name cell borders; page counts against
Word are unchanged (218 of 251 exact).
---------
Co-authored-by: Магда М.Г. <mail@mgmagda.ru>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
RTF import read every 8-bit byte as Windows-1252, so Cyrillic (and any
other non-Western) RTF came out as mojibake. Bytes now decode in the
current font's code page (\fcharset, \cpg), else the default font's,
else the document's (\ansicpg), for the single-byte Windows code pages
874 and 1250-1258.
A .doc, .docx or .rtf whose content is another of these formats now opens
by its signature, as Word does: RTF saved as .doc (hh.ru résumés and
accounting exports are commonly saved that way), a .docx renamed .doc, a
Word 97-2003 file renamed .docx.
Co-authored-by: Магда М.Г. <mail@mgmagda.ru>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Native-speaker review of the rows added late in #729:
- Overtype is « Refrappe », the term current versions of Word use in French
- « Attacher… » to match « Attacher un modèle »
- Outline promote/demote and the Outlining tab (« Mode Plan »), consistent
with the existing Promote/Demote rows and « Fermer le mode Plan »
- SmartArt basic layouts « … de base »
- tense and wording in three help texts, and the Previous 1 or More wildcard
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Old Russian and Central European documents name fonts by their Windows 3.1
charset aliases: "Times New Roman Cyr", "Arial CYR", "Courier New CE",
"Arial (Hebrew)". Windows maps these to the font itself (FontSubstitutes);
we treated them as unknown families and fell back to the sans default.
resolve() now tries the base family, and substitutes for it when it isn't
installed (Times New Roman Cyr -> Liberation Serif, like Times New Roman).
Co-authored-by: Магда М.Г. <mail@mgmagda.ru>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word's symbol fonts are proprietary and never bundled, so text set in them
showed the substitute's letters. New wordcraft_doc::symbol_fonts maps every
code (stored as U+F020-U+F0FF, e.g. w:sym, or as a plain 8-bit code) to its
Unicode equivalent: Symbol from Unicode's Adobe symbol.txt, the Wingdings
and Webdings sets from Unicode L2/12-368 (WG2 N4384), checked against UCD.
Layout shapes the mapped characters (fallback faces draw them); PDF text,
copy as text and TXT export read as the symbols; the stored text and docx
round trips are unchanged.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
WMF and EMF pictures already drew (crates/metafile) when they came inside a
document, but Insert › Picture refused them ("not a supported image"), and the
file dialog, drag-and-drop and paste lists left them out. A metafile is now
placed at its own frame size (points for placeable WMF and EMF; very large
frames scaled to 22 inches on the longer side) and stored as .wmf or .emf, so a
save writes it as such. render gains metafile_size().
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #848: font list previews no longer load every family they show
The Font box drew each entry in its own face through the full layout
pipeline, which loaded the whole family into the font database for good
(faces are never unloaded). Every trip through the list kept tens of
megabytes. Entries are now drawn with FontDb::preview_face: a loaded
family's face is shared, an installed one is read into a face the
database doesn't keep and is freed when dropped, and render_text_line
draws it without the shared outline cache.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Font preview test tolerates other tests loading fallbacks; gaps rows 1–3 de-duplicated, #848 noted in row 3
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The previous release build failed in craft-common-actions, not in this
repo: riscv64 packaging got --formats deb,rpm,tar (fixed in
craft-common-actions#3), Linux jobs lacked the ALSA headers
(craft-common-actions#5), and Windows signing needed the AZURE_* secrets
listed in the caller.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A click in the left margin beside body text selects that line (trailing
space, and the paragraph mark on a paragraph's last line), a double-click
the paragraph, a triple-click or Ctrl/Cmd+click the document; dragging
there selects whole lines and Shift+click extends by lines. The pointer
shows the arrow there. Layout gains selection_bar_at, body_line_at and
line_span; select.line takes an optional pos; select.dragUnit takes the
units line and document.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Layout › Breaks (next page, continuous, even, odd) deleted the selection
before splitting, so with "one" selected the first section came out empty.
The break now goes just before the selection, which keeps its text and
direction, as in Word.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Closing an edited document asked Save / Don't Save / Cancel with no button
focused, so the arrow keys did nothing and only the mouse could pick Don't
Save or Cancel. Save now has focus when the prompt opens, as in Word: Enter
saves, and the arrow keys (egui's focus navigation) or Tab move between the
buttons.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Every frame fingerprinted every item of every page for the page texture
cache (thousands of cell borders a page, formatted to strings), and the
markup-area check walked every paragraph twice a frame. Page content keys
are now taken once per layout and only for pages on screen or cached; the
document facts behind the markup area are cached per revision; textures of
pages far from the view are dropped. Layout and rendering are unchanged.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Builds with craft-fonts (releases, the FreeBSD job) put only its Japanese
faces in the document font database; Noto Sans CJK SC served the interface
alone. Simplified-only ideographs in a document then had no face unless the
system had one, and the run-level CJK pick from #567 found no face covering a
mixed run, which the FreeBSD job's
east_asian_run_uses_one_face_covering_the_whole_run caught. The Chinese craft
faces now follow the Japanese ones (Mincho stays first for Japanese text).
fallback_reads_each_installed_font_file_once_however_many_characters_miss
used U+30001.., part of which Noto Sans CJK SC maps (CJK Extension G); it now
uses Supplementary Private Use Area-A, which no font here maps.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Find, Replace and Go To work like Word (#712)
Before: Ctrl+F opened both the Navigation pane and a Find window,
Advanced Find opened the same window, Ctrl+H failed with nothing to
replace, and Go To was a separate little window that only took a page.
After, as in Word:
- Ctrl+F and Home > Find open only the Navigation pane, with its search
box focused so typing searches; selected text is searched for.
- Find, Replace and Go To are one Find and Replace dialog with three
tabs: Advanced Find opens Find, Ctrl+H (edit.findReplace, which never
marks the document edited) opens Replace, Ctrl+G / F5 open Go To.
- Go To moves to a page (a number, or +2 / -1 from the page the caret
is on; empty, Next and Previous step a page) or picks a bookmark.
edit.goto takes pageBy for scripts and agents.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Go To: list bookmarks only while Bookmark is chosen (#712)
Finding a document's bookmarks walks every paragraph; the Go To tab did it every frame even
while it showed pages.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Rulers: table column and row markers; indents in a cell measured from it (#700)
With the caret in a table, the rulers show the table, as Word's do:
- The horizontal ruler marks the column borders of the caret's row (past
the table's left edge); the vertical ruler marks the bottom of each of
the table's rows on the page. Each marker has the resize pointer, a
tooltip and a name for screen readers.
- Dragging a marker drags that border exactly like a drag on the page
(#600): a dashed guide, Escape cancels, one undo step on release
through `table.resizeDrag`. On the horizontal ruler only the column
left of the border changes and the columns right of it move along, so
the table's width changes (Word's ruler); a row marker sets the row's
height. The markers follow any drag of the table's borders, on the
ruler or on the page.
- In a cell, the indent markers and tabs are measured from the cell's
text area (its box less its margins); they were placed as if the
paragraph started at the page's margin.
- Layout: `cell_margins` (a cell's own margins, else the table's, else
Word's), so the ruler uses what the layout does.
- UI: crates/ui-egui/src/table_ruler.rs; table_resize.rs starts a drag
from a ruler marker (`grab`) and tells the ruler where a dragged border
is (`moved`), sharing that computation with the dashed guide.
- Tests: layout (cell_margins matches where each cell's text starts);
kittest with real pointer events: a column marker drag (the marker
follows, one undo step, the columns right of it keep their widths), a
row marker drag, and an 18 pt drag of the left-indent marker in a cell
indenting the paragraph 18 pt.
Progress docs updated: ui-parity (rulers 70% -> 85%), gaps (the ruler's
column markers are off the list), target-app-parity, ROADMAP. No new
commands or labels (the tooltips reuse "Table Column Width" and "Table
Row Height"). `cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Rulers: Shift works the other way round on the ruler; row markers beside a vertical merge
Checked against Word (2019, Windows): on the ruler a column marker drag
changes only the column on its left and the columns right of it move
along (as before), and with Shift the neighbour makes up the difference,
so the table keeps its width; on the page Shift does the opposite. The
ruler now follows that: `ResizeDrag::plan` takes the Shift key and flips
it for a drag started on a ruler marker.
Since #669 a vertically merged cell's box spans all its rows, so a row
marker took the merge's bottom when the merge was the row's leftmost cell.
The marker now sits on the row's shortest box. (`Placed::Cell` patterns
take `..` for #669's new field.)
- Test (kittest, real pointer events): a Shift drag of a column marker
on the ruler gives the neighbour's width to the column on the left and
keeps the table's width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: Ctrl, Alt, left-edge and selected-cells border drags (#711)
Word's border drags that #600 left out:
- Ctrl while dragging a column border: the columns right of it share the
change in proportion to their widths, so the table keeps its width
(none narrower than the minimum). Ctrl wins over Shift; on the table's
right edge only the last column changes.
- The table's left edge can be dragged, from just outside it: the
table's indent moves and the first column makes up the difference, so
the other borders stay. A centred or right-aligned table becomes
left-aligned where the edge was dropped; a floating table moves its
horizontal offset. The horizontal ruler has a marker on it too.
- Alt while dragging a column border: the horizontal ruler shows each
column's width as it is going to be.
- With the selection across two or more cells, dragging one of their
column borders (in one of their rows) moves it only in their rows; the
grid gets a line for the new border and the cells' spans follow. A
border of a cell merged down past the selection moves in the whole
column. Text selected within one cell isn't a cell selection (it can't
be told from one selected cell yet), so it moves the whole column.
- Engine: `Share` (who makes up a column border's move),
`Edge::Column(0)`, `ResizeTarget::limit_to_selection`/`only`/
`planned_x`, `Plan::indent_by`/`cells`; `table.resizeDrag` takes
`"ctrl"`. `plan` still takes a bool for Shift. Each drag is one command
and one undo step.
- UI: table_resize.rs (keys, a guide down the rows that move,
`measuring`), table_ruler.rs (left-edge marker, `widths`).
- Tests: engine (Ctrl in proportion and at the minimum; the left edge,
also of a centred table; selected cells' rows only, with the grid and
spans; a merged cell past the selection); kittest with real pointer
events (a Ctrl drag, the left edge and its ruler marker, Alt's widths
mid-drag).
Progress docs updated: ui-parity (tables on the page 80% -> 90%),
target-app-parity, ROADMAP. No new commands or labels. `cargo xtask ci`
passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a centred or right-aligned table keeps its alignment when its left edge is dragged
Checked against Word (2019, Windows): dragging the left edge of a centred
table widens the first column by the whole drag and the table stays
centred (each side moves half); a right-aligned table keeps its right edge
and grows to the left. Only a left-aligned table's indent moves. #711 made
such a table left-aligned where the edge was dropped; now `move_left_edge`
leaves a centred or right-aligned table's alignment and indent alone and
says whether it moved anything, and the drop-point correction runs only
then.
The ruler's Shift (from #710) now goes through `share`: on a ruler marker
Shift makes the neighbour take up the change, without it only the column
on the left changes.
- Tests: the left-edge test checks the centred table (first column +20 pt,
the others unchanged, still centred) and a right-aligned one (only the
left edge moves); a kittest Shift drag on the ruler.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: rabis <340973947+rabis7575@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Search: Alt+Q opens it from anywhere, like Word (#819)
- Alt+Q (and Alt, then Q on the keytips) opens the Search box with its
text field focused, from the page or anywhere else; the key's own "q"
is never typed into the box or the document. The title-bar box shows
its Q keytip and its shortcut in the tooltip.
- An empty box suggests common commands; every word of a query has to
match (so "insert table" finds Insert > Tables > Table, as the box's
own hint promises); Enter runs the top match; "Find ... in the
document" opens the Navigation pane on the results. Matching is
about 2x faster than before.
- tools.search lists matching commands for scripts and agents.
Observed in Word for Windows (Microsoft 365): Alt+Q and Alt, Q focus
Search with nothing typed; multi-word queries match commands; the list
also offers finding the words in the document.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Search: Bulgarian, Czech, German, French, Hungarian, Italian and Polish strings
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Rulers: table column and row markers; indents in a cell measured from it (#700)
With the caret in a table, the rulers show the table, as Word's do:
- The horizontal ruler marks the column borders of the caret's row (past
the table's left edge); the vertical ruler marks the bottom of each of
the table's rows on the page. Each marker has the resize pointer, a
tooltip and a name for screen readers.
- Dragging a marker drags that border exactly like a drag on the page
(#600): a dashed guide, Escape cancels, one undo step on release
through `table.resizeDrag`. On the horizontal ruler only the column
left of the border changes and the columns right of it move along, so
the table's width changes (Word's ruler); a row marker sets the row's
height. The markers follow any drag of the table's borders, on the
ruler or on the page.
- In a cell, the indent markers and tabs are measured from the cell's
text area (its box less its margins); they were placed as if the
paragraph started at the page's margin.
- Layout: `cell_margins` (a cell's own margins, else the table's, else
Word's), so the ruler uses what the layout does.
- UI: crates/ui-egui/src/table_ruler.rs; table_resize.rs starts a drag
from a ruler marker (`grab`) and tells the ruler where a dragged border
is (`moved`), sharing that computation with the dashed guide.
- Tests: layout (cell_margins matches where each cell's text starts);
kittest with real pointer events: a column marker drag (the marker
follows, one undo step, the columns right of it keep their widths), a
row marker drag, and an 18 pt drag of the left-indent marker in a cell
indenting the paragraph 18 pt.
Progress docs updated: ui-parity (rulers 70% -> 85%), gaps (the ruler's
column markers are off the list), target-app-parity, ROADMAP. No new
commands or labels (the tooltips reuse "Table Column Width" and "Table
Row Height"). `cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Rulers: Shift works the other way round on the ruler; row markers beside a vertical merge
Checked against Word (2019, Windows): on the ruler a column marker drag
changes only the column on its left and the columns right of it move
along (as before), and with Shift the neighbour makes up the difference,
so the table keeps its width; on the page Shift does the opposite. The
ruler now follows that: `ResizeDrag::plan` takes the Shift key and flips
it for a drag started on a ruler marker.
Since #669 a vertically merged cell's box spans all its rows, so a row
marker took the merge's bottom when the merge was the row's leftmost cell.
The marker now sits on the row's shortest box. (`Placed::Cell` patterns
take `..` for #669's new field.)
- Test (kittest, real pointer events): a Shift drag of a column marker
on the ruler gives the neighbour's width to the column on the left and
keeps the table's width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: rabis <340973947+rabis7575@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Tables: select, insert and move with the mouse; double-click a border to AutoFit
Like Word, on top of the border resizing:
- Selection areas: just above a column the pointer becomes a down arrow,
left of a row the row arrow, along a cell's left edge (inside it) the
cell arrow. A click selects the column, row or cell; a drag selects every
one up to the pointer. Selected cells are shaded as cells, empty ones too.
- Insert buttons: left of a row border and above a column border a "+"
shows with the border marked; a click adds a row or column there and puts
the caret in it.
- Move handle past the table's top-left corner: a click selects the table;
a drag shows a dashed outline and, on release, the table floats where it
is dropped with the text wrapping around it (Word's behaviour). One undo
step. Nested tables and tables outside the main text are only selected.
- A double-click on a column border fits the column left of it to its
contents (the right neighbour gives or takes the room; on the right edge
the table's width changes, up to the right margin).
- Table Layout > Select > Select Column (catalog 418/449).
Whole-cell selection: a column (or a rectangle of cells over several rows)
is not one run of the text, so selecting it makes a block of its cells'
paragraphs (`CellRange` on the existing `ColumnBlock`). Bold, alignment,
Delete, typing, copy and numbering act on those cells only (a numbered
column is one list); table commands (shading, merge, delete columns) take
the rectangle; Tab moves on without clearing it; Undo brings the selection
back. Cells of one row, or whole rows, stay an ordinary stream selection.
The format painter no longer clears a block's text (edit.pasteFormat was
handled like a paste). The mini toolbar stays hidden over whole-cell
selections and during these drags: pinned above the selection it covered
the column areas, the "+" buttons and the move handle.
Engine: crates/engine/src/cmd/table_mouse.rs - table.selectAt,
table.insertAt, table.moveDrag, table.fitColumn (page coordinates, so
agents and the control channel can do the same) and table.selectColumn;
hit testing reuses table_resize's drawn geometry, and borders keep priority
for resizing. 10 engine tests (areas, a column edited cell by cell,
numbering, rows/cells/drags, insert buttons, move, fit, format painter,
hostile params).
UI: crates/ui-egui/src/table_mouse.rs, wired into the existing canvas mouse
handler (no extra ui.interact areas, so clicks in a cell's text still place
the caret); kittest tests with real pointer events: column click, row drag,
"+" button, move-handle drag in one undo step, double-click fit.
New labels translated in all eleven catalogs. Progress docs updated: gaps,
ui-parity, target-app-parity, ROADMAP, parity checklist.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a selected column's word count is its cells'; merged cells shade once
With whole cells selected (a column, or a rectangle that isn't one run of
the text) the status bar said "12 of 23 words": `Session::selected_text`
took everything between the selection's ends. It now takes each piece's
own text, a line each, for a column block too, so the status bar, Word
Count and the other callers see what is selected.
Since #669 a vertically merged cell is one box as tall as its rows, with a
`covered` box for each continuation row; shading selected cells skips
those, so a merged cell isn't shaded twice. `selected_paths` follows
main's `para_paths_in` (#418): a selection ending at the start of the next
paragraph doesn't take it.
- Test: a selected column's text is its three cells' and Word Count says
three words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: double-click AutoFit and the "+" buttons work as in Word
Checked against Word (2019, Windows):
- A double-click on a column border fits the column left of it to its
contents and the columns right of it keep their widths, moving with the
border, so the table narrows or widens (up to the right margin). The
right neighbour used to give or take the difference.
- A "+" insert button selects the new row or column (the caret used to
go into it); typing then goes into its first cell. If the table can't
take another row or column, nothing changes.
- A double-click along a cell's left edge (the cell selection area)
selects its row.
- Tests: engine (fit keeps the columns on the right; insert selects the
new row or column, typing lands in its first cell) and kittest (real
double-click on a border; "+" selects the row; double-click by a cell's
edge selects the row).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: drags and Shift+arrows over cells select whole cells
An ordinary drag, Shift+click or Shift+arrow from one table cell into
another now selects the rectangle of whole cells between them, as Word
does, instead of the run of text between the two points: bold, alignment
and Delete leave the cells outside the rectangle alone (they already did
for Select Column and the table's selection areas). Between a cell and
text outside its table the selection takes whole rows, from the cell's
row out to the table's edge, and the text beyond. Extending again goes on
from where the selection really started and ends: dragging back into the
first cell selects its text again, Shift+Up after Shift+Down shrinks the
rectangle, and from a nested table's cell into another cell of the table
around it the outer table's cells are taken.
- Engine: `table::extend_selection` (the rule, down through nested
tables) and `Session::sel_ends` (where a widened selection really
runs); every caret command run with `extend` goes through it in
`column::dispatch`, so agents get the same with `caret.*`.
- UI: a text drag extends through `caret.set` (still straight to the
session, so nothing scrolls on every frame); the view follows the real
caret.
- Tests: engine (drag across cells and back, nested tables, Shift+Down
and Shift+Up through and past a table, Shift+click, from text into a
table) and a kittest with a real mouse drag.
Progress docs updated: gaps (the known gap from #637 is gone),
ui-parity, target-app-parity, ROADMAP. `cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: rabis <340973947+rabis7575@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Tables: select, insert and move with the mouse; double-click a border to AutoFit
Like Word, on top of the border resizing:
- Selection areas: just above a column the pointer becomes a down arrow,
left of a row the row arrow, along a cell's left edge (inside it) the
cell arrow. A click selects the column, row or cell; a drag selects every
one up to the pointer. Selected cells are shaded as cells, empty ones too.
- Insert buttons: left of a row border and above a column border a "+"
shows with the border marked; a click adds a row or column there and puts
the caret in it.
- Move handle past the table's top-left corner: a click selects the table;
a drag shows a dashed outline and, on release, the table floats where it
is dropped with the text wrapping around it (Word's behaviour). One undo
step. Nested tables and tables outside the main text are only selected.
- A double-click on a column border fits the column left of it to its
contents (the right neighbour gives or takes the room; on the right edge
the table's width changes, up to the right margin).
- Table Layout > Select > Select Column (catalog 418/449).
Whole-cell selection: a column (or a rectangle of cells over several rows)
is not one run of the text, so selecting it makes a block of its cells'
paragraphs (`CellRange` on the existing `ColumnBlock`). Bold, alignment,
Delete, typing, copy and numbering act on those cells only (a numbered
column is one list); table commands (shading, merge, delete columns) take
the rectangle; Tab moves on without clearing it; Undo brings the selection
back. Cells of one row, or whole rows, stay an ordinary stream selection.
The format painter no longer clears a block's text (edit.pasteFormat was
handled like a paste). The mini toolbar stays hidden over whole-cell
selections and during these drags: pinned above the selection it covered
the column areas, the "+" buttons and the move handle.
Engine: crates/engine/src/cmd/table_mouse.rs - table.selectAt,
table.insertAt, table.moveDrag, table.fitColumn (page coordinates, so
agents and the control channel can do the same) and table.selectColumn;
hit testing reuses table_resize's drawn geometry, and borders keep priority
for resizing. 10 engine tests (areas, a column edited cell by cell,
numbering, rows/cells/drags, insert buttons, move, fit, format painter,
hostile params).
UI: crates/ui-egui/src/table_mouse.rs, wired into the existing canvas mouse
handler (no extra ui.interact areas, so clicks in a cell's text still place
the caret); kittest tests with real pointer events: column click, row drag,
"+" button, move-handle drag in one undo step, double-click fit.
New labels translated in all eleven catalogs. Progress docs updated: gaps,
ui-parity, target-app-parity, ROADMAP, parity checklist.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a selected column's word count is its cells'; merged cells shade once
With whole cells selected (a column, or a rectangle that isn't one run of
the text) the status bar said "12 of 23 words": `Session::selected_text`
took everything between the selection's ends. It now takes each piece's
own text, a line each, for a column block too, so the status bar, Word
Count and the other callers see what is selected.
Since #669 a vertically merged cell is one box as tall as its rows, with a
`covered` box for each continuation row; shading selected cells skips
those, so a merged cell isn't shaded twice. `selected_paths` follows
main's `para_paths_in` (#418): a selection ending at the start of the next
paragraph doesn't take it.
- Test: a selected column's text is its three cells' and Word Count says
three words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: double-click AutoFit and the "+" buttons work as in Word
Checked against Word (2019, Windows):
- A double-click on a column border fits the column left of it to its
contents and the columns right of it keep their widths, moving with the
border, so the table narrows or widens (up to the right margin). The
right neighbour used to give or take the difference.
- A "+" insert button selects the new row or column (the caret used to
go into it); typing then goes into its first cell. If the table can't
take another row or column, nothing changes.
- A double-click along a cell's left edge (the cell selection area)
selects its row.
- Tests: engine (fit keeps the columns on the right; insert selects the
new row or column, typing lands in its first cell) and kittest (real
double-click on a border; "+" selects the row; double-click by a cell's
edge selects the row).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: the right-click menu offers the table's commands
Right-clicking a table cell now offers what Word's table menu does, all
through existing commands: Insert (rows above/below, columns left/right),
Delete (cells, rows, columns, table), Select (cell, column, row, table),
Merge Cells, Split Cells, Distribute Rows, Distribute Columns, AutoFit
(contents, window, fixed column width), Text Direction, Cell Alignment
(the nine alignments) and Table Properties… (opens the dialog, as the
ribbon's button does).
A right-click beside selected cells now moves the caret there, as in
Word: with a column selected, the cells between its first and last cell
in the text (the other columns of those rows) counted as "inside the
selection", so the selection stayed and the menu acted on the column
instead of the cell clicked. A right-click on a selected cell keeps the
selection, so Cell Alignment, Distribute and the rest act on it.
- UI only (crates/ui-egui/src/canvas.rs): the table part of
`context_menu()` and a small check before the right-click caret logic
(separate from it, so it doesn't overlap #521's change to that block).
Table Layout's nine cell alignments are one shared list (`CELL_ALIGN`
in ribbon.rs) for the ribbon grid and the menu.
- Two new labels, "Cell Alignment" and "Table Properties…", translated
in all eleven catalogs; the rest reuse the ribbon's labels.
- Tests: kittest with real pointer events: right-click in a cell, the
menu's table entries, Select > Select Column; with a column selected,
a right-click on it keeps it and Cell Alignment > Align Bottom Right
aligns just the column (one undo step), and a right-click beside it
drops it.
Progress docs updated: ui-parity (context menus 55% -> 60%), gaps,
target-app-parity, ROADMAP. No new commands, so the catalog is unchanged.
`cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: the right-click menu's table items follow the selection, as in Word
Checked against Word (2019, Windows): Word's table items in the context
menu depend on what is selected, and there is no Select or Cell
Alignment submenu among them.
- Caret in a cell: Insert, Delete Cells, Split Cells, Text Direction,
Table Properties…
- Several cells: Insert, Delete Cells, Merge Cells, Text Direction,
Table Properties…
- Whole columns: Insert, Delete Columns, Merge Cells, Distribute Rows,
Distribute Columns, Text Direction, Table Properties… (whole rows the
same with Delete Rows; not checked in Word).
- The whole table: Insert, Delete Table, Distribute Rows and Columns,
AutoFit, Text Direction, Insert Caption…, Table Properties…
Insert lists the columns first, as Word does. Select and Cell Alignment
are gone, and with them the shared CELL_ALIGN constant in the ribbon;
"Insert Caption…" replaces "Cell Alignment" in all fifteen catalogs.
Word's Border Styles and Insert Cells… aren't here yet.
- Tests (kittest, real right-clicks): the menu with the caret in a cell
and with the whole table; a selected column keeps its column menu and
Text Direction turns only it; beside it the menu is a cell's again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: rabis <340973947+rabis7575@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The right-click menu could only be opened with the mouse. Shift+F10,
Windows' keyboard way to the context menu (the Menu key's equivalent),
now opens the same menu just below the caret, with the spelling
suggestions and synonyms for the word there; Escape closes it.
ui.contextMenu opens it for scripts and agents.
The Menu key itself still does nothing: egui-winit drops that key
before the app sees it, so it needs an upstream change (or a winit
hook) to reach the same path.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* R17: no automatic paragraph spacing between list items
Word drops automatic (HTML-style) space before and after between two list
items, of one list or two, at any level; the first item keeps its space
before and the last its space after, and measured spacing stays. Measured in
Word for Windows: 16 synthetic cases. NIH's bulleted IND report items now
touch as in Word.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ROADMAP: keep the progress log separator under its header
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found by rendering 12 US government Word documents (NIH, EPA, MCC) in
Word for Windows and WordCraft and comparing the pages side by side,
then pinning each rule down with synthetic documents measured in Word.
- Kerning: the shaper kerned every run, so WordCraft's lines held more
text than Word's and broke differently. Word kerns only a run with
Kerning for fonts (w:kern) at or above that size; no w:kern and
w:kern="0" are unkerned. New documents keep kerning from 1 pt, like
Word's Normal template.
- Automatic paragraph spacing (w:beforeAutospacing/w:afterAutospacing,
common in text pasted from web pages) was ignored. It is 14 pt at
any size, none before the document's or a cell's first paragraph,
none after a cell's last, and the larger of two neighbouring spaces
counts. Read, written back, and settable through para.spacing
(beforeAuto/afterAuto); a number, Ctrl+0 and Remove Space After
replace it as in Word.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Tables: select, insert and move with the mouse; double-click a border to AutoFit
Like Word, on top of the border resizing:
- Selection areas: just above a column the pointer becomes a down arrow,
left of a row the row arrow, along a cell's left edge (inside it) the
cell arrow. A click selects the column, row or cell; a drag selects every
one up to the pointer. Selected cells are shaded as cells, empty ones too.
- Insert buttons: left of a row border and above a column border a "+"
shows with the border marked; a click adds a row or column there and puts
the caret in it.
- Move handle past the table's top-left corner: a click selects the table;
a drag shows a dashed outline and, on release, the table floats where it
is dropped with the text wrapping around it (Word's behaviour). One undo
step. Nested tables and tables outside the main text are only selected.
- A double-click on a column border fits the column left of it to its
contents (the right neighbour gives or takes the room; on the right edge
the table's width changes, up to the right margin).
- Table Layout > Select > Select Column (catalog 418/449).
Whole-cell selection: a column (or a rectangle of cells over several rows)
is not one run of the text, so selecting it makes a block of its cells'
paragraphs (`CellRange` on the existing `ColumnBlock`). Bold, alignment,
Delete, typing, copy and numbering act on those cells only (a numbered
column is one list); table commands (shading, merge, delete columns) take
the rectangle; Tab moves on without clearing it; Undo brings the selection
back. Cells of one row, or whole rows, stay an ordinary stream selection.
The format painter no longer clears a block's text (edit.pasteFormat was
handled like a paste). The mini toolbar stays hidden over whole-cell
selections and during these drags: pinned above the selection it covered
the column areas, the "+" buttons and the move handle.
Engine: crates/engine/src/cmd/table_mouse.rs - table.selectAt,
table.insertAt, table.moveDrag, table.fitColumn (page coordinates, so
agents and the control channel can do the same) and table.selectColumn;
hit testing reuses table_resize's drawn geometry, and borders keep priority
for resizing. 10 engine tests (areas, a column edited cell by cell,
numbering, rows/cells/drags, insert buttons, move, fit, format painter,
hostile params).
UI: crates/ui-egui/src/table_mouse.rs, wired into the existing canvas mouse
handler (no extra ui.interact areas, so clicks in a cell's text still place
the caret); kittest tests with real pointer events: column click, row drag,
"+" button, move-handle drag in one undo step, double-click fit.
New labels translated in all eleven catalogs. Progress docs updated: gaps,
ui-parity, target-app-parity, ROADMAP, parity checklist.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a selected column's word count is its cells'; merged cells shade once
With whole cells selected (a column, or a rectangle that isn't one run of
the text) the status bar said "12 of 23 words": `Session::selected_text`
took everything between the selection's ends. It now takes each piece's
own text, a line each, for a column block too, so the status bar, Word
Count and the other callers see what is selected.
Since #669 a vertically merged cell is one box as tall as its rows, with a
`covered` box for each continuation row; shading selected cells skips
those, so a merged cell isn't shaded twice. `selected_paths` follows
main's `para_paths_in` (#418): a selection ending at the start of the next
paragraph doesn't take it.
- Test: a selected column's text is its three cells' and Word Count says
three words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: double-click AutoFit and the "+" buttons work as in Word
Checked against Word (2019, Windows):
- A double-click on a column border fits the column left of it to its
contents and the columns right of it keep their widths, moving with the
border, so the table narrows or widens (up to the right margin). The
right neighbour used to give or take the difference.
- A "+" insert button selects the new row or column (the caret used to
go into it); typing then goes into its first cell. If the table can't
take another row or column, nothing changes.
- A double-click along a cell's left edge (the cell selection area)
selects its row.
- Tests: engine (fit keeps the columns on the right; insert selects the
new row or column, typing lands in its first cell) and kittest (real
double-click on a border; "+" selects the row; double-click by a cell's
edge selects the row).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: rabis <340973947+rabis7575@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Layout: space before at the top of a page after page breaks, as in Word
Found comparing Microsoft's specification documents with Word for
Windows, then measured with 32 synthetic documents in compatibility
modes 12 (Word 2007) and 15 (Word 2013+):
- A paragraph that ends with a page break keeps its mark with the
break: WordCraft put an empty line at the top of the next page.
- The paragraph after it, first on the new page, gets no space before.
- A paragraph that starts with a page break starts on the new page
with its space before there (WordCraft put the space on the old page).
- A page break before keeps the space before in modes up to 14 and
drops it in mode 15.
- Natural breaks, section breaks and the first page were already right.
All 32 cases now land where Word puts them (within 0.3 pt), and the
caret still reaches a paragraph mark kept with its break.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Layout: after a page break before or a section break, the space after above counts
Measured in Word for Windows (10 more cases): at the top of a page after a page break before
(modes up to 14) or a next-page section break, the previous paragraph's space after comes off the
space before (24 pt before after 10 pt after leaves 14 pt; after 30 pt leaves none). This put the
numbered Heading 1s of Microsoft's specification documents 9 pt too low. All 42 page-top cases now
match Word.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found in NIH's SARP form compared with Word for Windows: the form's
second section has a narrow label column (67 pt) and a wide text column
(438 pt), and each answer starts with a column break into the wide one.
Before: the paragraph's lines were broken once, for the column it
started in, so the text after the break ran a word or two wide down the
wide column. After: when a column break lands the rest of a paragraph
in a column of another width, that rest is broken again for it, as in
Word. Equal columns are unaffected (nothing is laid out twice).
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
322 rows only Italian and Russian had (the 24 that #695 adds are left
to it). The Estonian/Turkish/Polish coverage tests no longer require
Spanish to have exactly their keys: the extras must be gallery/style
keys (in Italian, not in German).
Models: Opus 5.5, Sol 6.1, DeepSeek V4 Flash
Co-authored-by: Diego Ospina <diegozpy@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word positions a floating object "relative to paragraph" (and to its line)
from the paragraph's start above its space before, below the previous
paragraph's space after. WordCraft measured from the first line, so floats
in spaced paragraphs sat lower by the space before. Measured in Word for
Windows: 10 synthetic cases.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #701: Undo history list beside the Quick Access Toolbar's Undo
A drop-down arrow next to Undo lists recent steps, newest first; hovering a
row highlights it and every newer step, the footer reads "Undo N Actions"
(Cancel when none is highlighted), and a click undoes them all at once.
Engine: Session::undo_history (newest first, capped at UNDO_LIST_MAX = 100)
and Session::undo_steps; edit.undoTo {"count": n} (0, negative and non-number
counts rejected, counts past the depth clamped, stops at protection) and the
read-only edit.undoHistory for CLI and MCP. Catalog: Undo List.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Hungarian and French strings for this change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #701: Bulgarian for the Undo history strings; drop a duplicate Turkish entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* R21: an empty continuous section mark takes no room
An empty paragraph that only carries a section break before a continuous
section takes no room in Word: the text after it follows right on, and
columns balanced above it don't count it. A break on a paragraph with text
keeps its line. Measured in Word for Windows: 10 synthetic cases. The SARP
form's fields drifted a line lower at each of its continuous breaks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ROADMAP: keep the progress log separator under its header
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #766: start on File › Home (Start screen) when launched with nothing to open
Desktop and web start on Backstage Home (Blank document, templates, Recent)
when no file, --sample or --control is given; Esc/back leaves the empty blank
document. File › Options › General › Start-up checkbox (ui.startScreen, kept
between runs, default on). WordApp::new is unchanged; hosts opt in via
WordApp::start_screen. ui_shot: {"empty":true,"startScreen":true}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Hungarian and French strings for this change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #766: Bulgarian for the Start screen setting
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word moves a "keep with next" paragraph to the next page only when it would
fit on this one by itself; one that doesn't fit anyway breaks as usual, its
last lines going over with the next paragraph (with keep lines together it
moves whole). A chain of kept paragraphs moves together. Measured in Word
for Windows: 20 synthetic cases. NIH's five-line kept paragraph no longer
leaves a third of a page empty.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #835: AutoCorrect's English replacement list applies only to English text
Built-in word entries (teh, i, dont…) are replaced only when the word's
language (run w:lang, style, document default) is English or unset;
symbols, fractions and user entries apply in every language. Polish,
Croatian, Bosnian and Serbian "i" ("and") is no longer turned into "I".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #835 review: untagged text follows the editing language; English as proofing decides it
- Text with no language at all (a .docx without w:lang) is in the editing language, so with a
Polish interface `Ala i kot ` keeps its `i` there too.
- `wordcraft_proof::ProofLang::from_tag` decides what is English (it also takes `eng`), so
AutoCorrect and proofing agree; the local `is_english` is gone.
- Tests through `set_editing_language` (the document-default path) and with no language at all.
- docs/typing-parity.md: the Croatian/Bosnian/Serbian case needs the text tagged, as those
interfaces set no editing language yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A numbered paragraph style's own indents (and those of styles based on it)
now beat the list level's, its base styles' don't, and the paragraph's own
list beats every style. A paragraph that switches its list off (numId 0)
drops the styles' indents, as Word does; the first Backspace in a List
Paragraph item makes the item's indent the paragraph's own so the text stays
put. Measured in Word for Windows: 27 synthetic cases, all match. NIH's
Heading 3s sit at their 0.9" and its "sample text" paragraphs at the margin.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found in NIH's monitoring-plan template compared with Word for Windows:
its footer puts the page number in a text frame (w:framePr: centred,
text wrapping around it), and Word draws "NCCIH DSMP Template" ending
just left of the number on the same line.
Before: frames other than drop caps were ignored on import, so the
number took a line of its own above the footer text (making every page
a line shorter), and the frame was lost on save.
After: a paragraph's text frame is read (size, anchors, alignment,
offsets, wrap, distances) and written back. In headers, footers, table
cells and text boxes it is placed like a floating object, sized to its
text unless it has a width, and the text after it wraps around it; the
DSMP footer now matches Word's. Frames in the body text still lay out
in line.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Read and write w:hideMark on table cells, and lay it out like Word: a cell's
empty last paragraph takes no room, spacing and all, so a row of only such
empty cells is just its borders; an ordinary empty cell keeps the row a line
tall. Measured in Word for Windows: 9 synthetic cases. NIH's Table A no
longer shows a blank row.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #836, #837: language-aware sorting and locale numbers in tables
Sorts (Table > Sort, Home > Sort, index and its letter headings, bibliography,
Table of Authorities) use the text language's alphabet via a small pure-Rust
collation (crates/doc/src/collate.rs). Table sort and formulas read numbers
with the cell language's decimal/group separators and write formula results
with its decimal separator (crates/doc/src/locale_number.rs). Sort comparators
are now total orders.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #836, #837 review: ordinals, leading zeros, percentages; untagged text; decomposed letters
- parse_number: a trailing separator with no digits is the whole number (`1.`, a
numbered "Lp." column, sorts as 1); a first group of `0` is never grouping
(`0.125` in Polish is 0.125, not 125); a trailing `%` is ignored (`12 %` is 12);
an en dash is a minus.
- Text with no language follows the document default, else the editing language
(`cmd::default_lang`), as AutoCorrect does: a Polish interface reads `3,50` as
3.5 in an untagged document. Table sort and formulas, paragraph sort, the
index, the bibliography and the Table of Authorities use it.
- Collation composes a letter and a following combining mark first (table from
Unicode NFC), so a decomposed `ż` (z + U+0307) sorts as `ż`.
- Tests: every Polish letter and its index heading, decomposed letters, the
ordinal column, an untagged Polish SUM, Polish bibliography order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word sizes a list item's first line with the label's ascent when it is
taller, but keeps the text's descent below the baseline: a Symbol bullet's
deeper descent (and a Courier New label's) doesn't count. Measured in Word
for Windows: 6 synthetic cases. NIH's bulleted items were a fifth of a point
taller each, enough to push lines over a page bottom.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found in NIH's monitoring-plan template, compared with Word for Windows.
Before: a w:numPr that gives a level (w:ilvl) but no list (w:numId) was
dropped. Outline-numbered templates do this all the time: Heading 3 is
based on Heading 2 (which names the list) and only says "level 3", so
every Heading 3 numbered as a Heading 2 (6.15 where Word shows 6.4.5).
After: such a level takes the list of the nearest style in its based-on
chain that names one (ECMA-376 17.3.1.19), in styles and in paragraphs;
with no list anywhere in the chain there is no numbering. Saved files
carry an ordinary list reference.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>