* 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>
* 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>
* #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>
* #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 for Windows' Alt+Q opens the "Tell me" box; in WordCraft it did nothing.
Interface commands (ui.*) aren't in the engine's keymap, so keys.rs gets a
small table of interface shortcuts, checked before the keymap from the page
and from the rest of the window. Alt+Q opens command search
(ui.dialog {"name": "commands"}) and its key text isn't typed. Not on macOS,
where Option+Q types a character.
Also fixes the ROADMAP progress log, whose separator row sat below two rows.
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>
View › Show stacked four check boxes in a column made for three: since
Hide White Space (#645) joined Ruler, Gridlines and Navigation Pane, the
fourth row was drawn over the "Show" group label. It now sits in a second
column.
A new test draws every tab in every interface language and fails on any
group taller than the ribbon's content area (CONTENT_H, 66 pt). It found:
- Developer › Controls, 75 pt: 3 pt after each row of small buttons. The
rows now fill the height with no gaps, like every other stack.
- Layout › Paragraph (#654), which panicked in debug builds: `add_space`
between grid cells trips egui's "add_space makes no sense in a grid
layout" assertion. The 8 pt gap before the Spacing column now sits
inside those cells, so it looks the same. The group was also 68.5 pt:
1.5 pt between rows instead of 3 fits it.
- widgets.rs: under cfg(test), group_titled records any group whose
content is taller than CONTENT_H.
- ribbon.rs: ribbon_groups_fit_above_their_labels.
- ROADMAP progress log and docs/ui-parity.md revision history.
- contributors/people.toml: my own entry.
Co-authored-by: Manuel Carmona <328347120+manucarcor@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The painted title uses a Latin-only face, so names in other scripts were shown as question marks. A covering fallback face is now loaded for the caption.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Observed in Word for Windows (Microsoft 365): pressing Alt badges the
tabs File F, Home H, Insert N, Design G, Layout P, References S,
Mailings M, Review R, View W, Help Y, and the Search box Q.
Before, WordCraft used Design L, References R, Review S and View V,
and File and Help had no letter, so Alt, W or Alt, R went to the wrong
tab for anyone used to Word. Now the letters match; Alt, F opens the
Backstage and ends keytips.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Fix paragraph alignment focus
Fixes#686
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Review fixes for #690: every ribbon command button returns focus to the document
Instead of four hard-coded alignment ids, big, small and split buttons and ribbon menu
items hand keyboard focus back to the canvas after running any engine command (ui.*
commands open dialogs and panes, which take focus themselves). The test also clicks Bold.
The ui-parity.md note moves into the change log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* M15: Polish interface, and Polish spelling, grammar and hyphenation
Interface
- Polish is the tenth interface language ("Polski", pl.tsv, 1,045 entries);
pl, pl-PL and pl_PL.UTF-8 system locales select it automatically.
- Right-click menu labels now go through translation in every language.
- Review > Language is a menu (English US/UK, Polski, Do not check spelling
or grammar); the status bar shows the language at the caret.
- With a Polish interface, new blank documents and opened .txt/.md files
default to pl-PL.
Proofing
- Each run is checked in its own language (w:lang, resolved through
styles): squiggles, F7, review.issues, suggestions and hyphenation.
Text in languages without proofing tools is no longer flagged as English.
- Polish spelling from the SJP.PL Hunspell dictionary (Apache-2.0 option),
read by a new Rust affix engine (crates/proof/src/affix.rs); 875 KB asset,
~4.5M word forms. Regenerate with the pl_dictionary example.
- Polish suggestions: case, missing diacritics, rz/ż, ó/u, ch/h confusions,
missing spaces (napewno -> na pewno), edit distance.
- Conservative Polish grammar rules: repeated words, spacing, doubled
punctuation, sentence capitals, missing comma before że/który/ponieważ/
aby/żeby/jeśli…, we/ze forms.
- Polish hyphenation with the hyph-utf8 hyph-pl.tex patterns (MIT option);
pattern keys widened to 20 letters, TeX exceptions parsed; the heuristic
no longer treats ł, ż, ś… as vowels.
review.language validates and normalizes tags, can set noProof alone and
the document default; format.state reports lang and noProof.
Tests for every part, docs and ATTRIBUTION.md/NOTICE updated;
cargo xtask ci passes (fmt, clippy, test, assets, layers, wasm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Layout: rustfmt the test module order left unformatted by #391
cargo fmt --check failed on main after #391 added mod tests_grid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Review fixes for #346: read Polish spelling from the installed Hunspell dictionary
- Polish spelling uses the Hunspell pl_PL dictionary installed on the
system (OS packages, Homebrew, LibreOffice, ~/.local/share/hunspell, or
WORDCRAFT_DICTIONARIES) instead of a bundled 870 KB SJP.PL file;
without one, Polish text gets no spelling marks, like Word without the
language's proofing tools. .aff/.dic are size-capped and decoded from
UTF-8 or ISO 8859-2 (crates/proof/src/hunspell.rs).
- The dictionary can be replaced at runtime (install_dictionary) or
re-searched (reload_dictionary); an empty one accepts every word.
- Tests use small synthetic dictionaries written for them, so they don't
depend on what the machine has installed.
- The bundled pl.dic, its ATTRIBUTION/NOTICE entries and the generator
example are removed; docs describe the installed dictionary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M15: Polish catalog covers the interface text merged from main
The merge of main (98456ac) brought ~575 new interface strings (charts,
SmartArt, content controls, Developer tab, AutoCorrect, outline view,
protection, compare, print, accessibility, references and Language/
Hyphenation dialogs, Draw additions). pl.tsv lacked them, so
polish_covers_every_key_of_every_other_catalog and
every_tab_and_command_is_translated failed. All are now translated.
cargo xtask ci: all 6 steps pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Like Word: the pointer turns into a resize arrow over a column border or a
row's bottom border, and a small handle shows past the table's bottom-right
corner. Dragging shows a dashed guide; releasing runs the new
`table.resizeDrag` command (page coordinates), so a drag is one undo step
and the selection is kept. Esc cancels a drag.
- Column border: the right neighbour gives or takes the difference, so the
table keeps its width; Shift, or the table's right edge, changes only the
column on the left. Columns stay at least 6 pt wide.
- Row border: sets the row's "at least" height; the margins and border bands
the layout adds are measured and taken off, so the row is drawn at the
dragged height.
- Corner handle: scales every column and the rows on that page.
- Widths come from the drawn layout, so autofit and percentage widths don't
make the result jump; a merged cell hides the border it covers.
- Borders are hit-tested in the existing canvas mouse handler, only within a
few pixels of a border, so clicks in cells still place the caret.
Engine: crates/engine/src/cmd/table_resize.rs (7 tests).
UI: crates/ui-egui/src/table_resize.rs, wired into canvas.rs; kittest tests
drive real pointer events through the whole window (one undo step per drag,
selection kept, clicks in cells still move the caret, Esc cancels).
The command label is translated in all nine interface catalogs.
Progress docs updated: gaps, ui-parity, target-app-parity, ROADMAP.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
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>
* Options: Animation effects setting that follows the system
The app eased wheel scrolling, Ctrl+wheel zoom and its fades whatever the
system's animation setting was: with Windows' Animation effects off, the
page still smooth-scrolled.
File > Options > Accessibility > Animation effects: Use system setting
(the default), On or Off; `ui.animations` for agents; kept in ui.json.
- Off: a wheel notch scrolls the page its whole distance at once, egui's
own scroll areas get the notch whole instead of eased in, Ctrl+wheel
zoom steps at once, and egui's fades and scroll-to glides are off.
Touchpad momentum and the caret blink are unchanged.
- Use system setting asks the host (`Services::animations_enabled`) at
start-up and when the window regains focus. Windows: the Windows
Runtime's UISettings, a safe API of the `windows` crate already in the
build. Web: `prefers-reduced-motion`. macOS and Linux/BSD aren't read
yet (docs/gaps.md); there it means on.
- The eight new strings are in all eleven interface catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Animation effects off: zoom steps land finished in one frame
Follow-up to the Animation effects setting: zooming could still look
animated with the setting off.
- Ctrl+wheel zooms in whole steps, each at once. A mouse-wheel notch is
two steps in its frame, the same zoom as before. Turns that arrive in
small pieces (a touchpad pinch that Windows reports as Ctrl+wheel, a
high-resolution or free-spinning wheel) add up to a step of about 10%
first, where each piece used to zoom a little and the zoom glided.
- A page re-rendered for a zoom step is drawn on the pixel grid in that
frame. It used to be drawn off the grid, slightly soft, and snap sharp
a frame later.
- The frame a zoom changes in asks for the next one at once, so the
status bar's percentage and slider follow in the next frame. They
could lag until the caret's next blink, up to half a second.
The last two apply whatever the setting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Animation effects: follow the system's setting on macOS, Linux and the BSDs
"Use system setting" only read Windows and the browser; elsewhere it
meant on.
- macOS: System Settings > Accessibility > Display > Reduce motion,
through NSWorkspace (a safe call in objc2-app-kit 0.3, already in the
build).
- Linux and the BSDs: the XDG settings portal over D-Bus (zbus, already
in the build through AccessKit). Three settings say whether animations
play, and any one saying no turns them off: the cross-desktop
reduced-motion preference, GNOME's enable-animations and KDE Plasma's
animation speed. A thread reads them at start-up and follows the
portal's change signal. Where no portal answers, or it knows none of
them, KDE's kdeglobals and GTK's settings.ini are read instead.
- The UI now asks the host every frame instead of at start-up and on
focus, so a change in the system's settings is followed while the app
runs. Hosts answer from memory: Windows and macOS are asked again at
most once a second, the web keeps its media query, and Linux/BSD is
kept current by its thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Animation setting on Linux: ask every setting again when one changes
KDE's portal announces a new animation speed only as the cross-desktop
reduced-motion preference, not under its own kdeglobals name. Keeping
each setting's last announced value left KDE's own one stale: after
going from instant back to normal speed, animations stayed off.
Every setting is now read afresh on any relevant change, and the
settings files' answer stands whenever the portal has none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Caret glide follows Animation effects; its canvas test turns the setting off, then on
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Linux: built-in file browser when the portal file dialog is unavailable (#576)
rfd 0.17 on Linux/BSD asks the xdg-desktop-portal FileChooser and falls back to
zenity; with neither (labwc with only xdg-desktop-portal-wlr, no zenity) it
returns None, the same as Cancel, so Open/Save As/Insert Picture silently did
nothing. The dialog thread now checks first (zenity on PATH, or the portal's
FileChooser interface via dbus-send) and answers with an error when neither is
there; the app then finishes the job in its own egui file browser (folder
field, Up/Home, folders and files of the chosen type, file name, type filter,
replace confirmation) with a hint on how to get the system dialog.
File > Open > Browse in WordCraft... (Linux/BSD) and `ui.fileBrowser` reach
it at any time, even while a system dialog never showed up;
WORDCRAFT_FILE_DIALOG=builtin forces it. Native only (not wasm32).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Turkish and Russian strings for the file browser (#576)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Add arcanii to the contributor names
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Caret: glide and fade like Word for Windows; File › Options › Animation turns it off
The caret no longer jumps and switches on and off. A key or typing glides
it to its new place in 100 ms along cubic-bezier(0, 0, 0, 1), the same for
one character and for a whole line; a second key before it arrives makes
it snap to where it was heading and set off from there. Clicks, and moves
made by scripts or the control channel, don't glide. The blink is a pair
of 100 ms fades (530 ms on, 530 ms off), and a key or a click turns the
caret on from however visible it was.
These are Word for Windows' numbers (16.0.20326, 100% zoom, 144 Hz),
measured black-box: a synthetic document in a separate Word instance, its
window captured frame by frame while key and mouse messages moved the
caret. Fourteen consecutive frames match the curve to 0.003. Word for Mac
was not measured.
Nothing glides when the view changed (zoom, a resized window, scrolling,
another document), when the caret wasn't drawn the frame before, or when
a move starts or ends off screen.
File › Options › Animation › "Animate the caret (it glides and fades)"
turns it off: ui.caretAnimation {"value"} for agents, kept in ui.json, on
by default, translated in the nine catalogs. Off, and whenever egui's
animations are off (the headless harness, so ui_shot and scripted
screenshots), the caret jumps and blinks exactly as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
File > Options > Advanced > Measurement units (#485) missed these two dialogs:
the Line Numbers "From text" distance and the Hyphenation zone read and
sent inches whatever the preference was. They now convert through
options::unit() like every other dialog; params still send points.
Reported by @Ledity.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Add fonts at run time: Add Font…, drop a font file, font.add (#582)
Rebased onto main as one commit (the branch had merge commits from main).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #582: font test no longer depends on the system's fallback font
The FreeBSD runner has no system fonts, so a missing family fell back to
Source Serif 4, the font the test compared against. The test now adds a
renamed JetBrains Mono under a name without Serif/Sans/Mono (which picks a
fallback of that kind), with narrow-letter sample text: monospaced, it
measures unlike any proportional fallback.
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>
* Font list: installed Chinese fonts are listed, by their Chinese names in a Chinese interface (#505)
The font database read only the English family name of each font, so a
Chinese interface listed SimSun, Microsoft YaHei and WenQuanYi Zen Hei
by their English names and never as 宋体, 微软雅黑 or 文泉驿正黑, and a
document naming a font by its Chinese name fell back to another font.
- fonts: keep each face's localized family names (name IDs 16 and 1,
every face of a collection, read during the scan without loading the
file); FontDb::families_in(lang) lists them in place of the English
names for a matching interface language (zh-hans, zh-hant, ja, ko);
face, has_family and styles accept either name.
- ui: the ribbon and mini toolbar font boxes show the interface
language's names and store the English name.
- East Asian font: choosing a Chinese, Japanese or Korean font also sets
it as the East Asian font (w:eastAsia), read and written in DOCX;
layout draws CJK text in a run's installed East Asian font.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Font dialog: separate Asian and Latin text fonts (#505)
Word's Font dialog sets the font of Chinese, Japanese and Korean text
apart from the font of Latin text; WordCraft's had one font box.
- Font dialog: an "Asian text font" box (the installed Chinese, Japanese
and Korean fonts, by their interface-language names, type to find)
above the "Latin text font" box, shown for a Chinese or Japanese
interface, East Asian text in the selection or a selection with an
Asian font. OK sets only the fonts changed, in one undo step.
- format.font takes "eastAsia" (w:eastAsia) besides "name"; either or
both; format.state reports fontEastAsia.
- fonts: the scan reads each font's OS/2 code pages, so
FontDb::east_asian_families lists CJK fonts without loading them.
- Tests: dialog sets each font on its own; layout sets CJK text in the
run's Asian font and Latin text in its Latin font (skipped without an
installed CJK font); command and undo; the scan finds a CJK font.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Two rows like Word's mini toolbar (font, size, grow, shrink; B I U S, highlight,
font colour, four alignments), sized from the ribbon widgets' real metrics so no
control is clipped any more (the one-row bar cut off everything after Align Left).
- Combo lists drop from the box's left edge instead of from the arrow.
- The dropdown triangle fills its box, so it no longer shrinks to a dot beside
combos and split buttons.
- Font list entries are an ink mask tinted with the theme's text colour, so they no
longer sit on white strips in dark mode.
Tests: the bar drawn over a real selection contains every control and is exactly as
wide as them; font entries take the theme text colour (dark and light) and paint no
background; the ink mask keeps coverage and drops the paper.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Find what understands Word's ^ codes (^p ^t ^? ^# ^$ ^^ ^l ^m ^n ^b ^+ ^= ^s ^~ ^- ^w ^g ^% ^v,
^nnn / ^unnnn); Replace with adds ^& (found text) and ^c (clipboard). `special: false` reads
the text literally.
- Use wildcards: ? * [abc] [a-z] [!x] {n} {n,} {n,m} @ < > ( ), \1-\9 in Replace with; always
matches case, like Word. Patterns are translated to the linear-time regex engine (no
catastrophic backtracking), length and counts capped, bad patterns are errors.
- ^p matches paragraph marks between sibling paragraphs; a ^p in the replacement splits the
paragraph. Replace All stays one undo step.
- Match prefix / suffix, Ignore punctuation / white-space characters.
- Dialog: Use wildcards checkbox, Search Options with the new options and a Special menu that
inserts codes into the focused field (a shorter list with wildcards); strings in all 9 catalogs.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
styles.updateToMatch copied only the direct run/paragraph formatting at the
caret onto the style, so text whose look came from its own style (Normal,
Title, ...) changed nothing; it then wiped that paragraph's direct formatting
without applying the style, so the selection lost its look too.
Now the style takes the selection's effective formatting (paragraph style
chain, character style chain, direct formatting) as a diff against what the
style currently gives, keeping its outline level and numbering. The selected
paragraphs take the style and drop only the direct formatting it now carries.
The Home styles gallery tiles get the right-click menu (Update to Match
Selection, Modify...).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Outline view: Outlining tab, levels, collapse/expand, Show Level (#493)
- View › Outline is its own view mode: the body on one long sheet, each paragraph indented
by its outline level behind a symbol drawn in code (circled plus for a heading with content,
circled minus for one without, a dot for body text); paragraph indents and alignment hidden.
- Collapsed headings hide their subtree and get a wavy line; Show Level 1-9 / All; Show First
Line Only; Show Text Formatting off draws everything in the Normal style. All of it is view
state (ViewState.outline), not saved; collapsed headings are found again by their text after
edits, and a caret entering hidden text moves on to the next shown paragraph.
- Commands: outline.promoteToHeading1/promote/demote/demoteToBody/level/moveUp/moveDown,
outline.expand/collapse/toggle/selectSubtree, outline.showLevel (+ showLevel1-9, showAll),
outline.firstLineOnly, outline.showFormatting, outline.close, outline.state. In Outline view
Move Up/Down and Promote/Demote take a collapsed heading's hidden content along.
- Keys: Alt+Shift+Plus/Minus expand/collapse, Alt+Shift+1-9 and Alt+Shift+A Show Level.
- Outlining tab (next to File while in Outline view): level box, promote/demote, move,
expand/collapse, Show Level, the two check boxes, Close Outline View. Clicking a symbol
selects the heading with its content; double-clicking expands or collapses it.
- Catalog: Outlining tab rows (421/453, 92.9%); translations in all nine catalogs; docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Docs: drop the stale shortcut and dialog rows left by the rebase (#493)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Templates and Add-ins: attached template, update styles, Organizer (#377)
- Model + DOCX: Settings keep the attached template (w:attachedTemplate's external
relationship of the settings part) and w:linkStyles; both round-trip, written in
CT_Settings order (after w:mirrorMargins, before w:trackRevisions).
- Engine: tools.templates (state), tools.attachTemplate, tools.detachTemplate,
tools.linkStyles, tools.updateStyles (copy the template's styles and defaults, one
undo step; path or base64 data), styles.organizer (list), styles.copyFrom (copies
missing bases, linked halves and the lists a style numbers with), styles.rename.
The template path comes from the document, so it is never read automatically (not on
open either): only an explicit tools.updateStyles / attach with update reads it.
- UI: View > Templates > Templates and Add-ins dialog (path, Attach..., automatically
update document styles, Organizer...) and a two-list Organizer (Copy, Delete,
Rename...); strings in every catalog, Estonian included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Review fixes for #382: never follow a template path to another machine
A document's w:attachedTemplate can name \\host\share\T.dotx, //host/share or
file://host/share. Opening it (Update Styles, Organizer) would make Windows
authenticate to that host over SMB and leak the user's NTLM credentials.
local_path now returns None for network paths; regression test added.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #377: refresh catalog count and parity checklist after rebase
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
File › Options is now a pane list (General, Display, Proofing, Save,
Language, Accessibility, Advanced, Agents), one function per pane in
crates/ui-egui/src/options.rs. Every setting shown is wired:
- General: theme, interface size (#475), mini toolbar on selection, dark page, community
button, user name and initials (comments use the initials).
- Display: tab characters, spaces, paragraph marks and hidden text shown
with Show/Hide ¶ off; rulers; print the page colour; update fields
before printing.
- Proofing: ignore words in capitals / with numbers / web and file
addresses (proof::Options, passed through layout), check spelling and
mark grammar as you type, custom dictionary add/remove (kept in ui.json).
- Save: AutoSave. Language: interface language. Accessibility: Read Aloud
speed and citations. Advanced: typing replaces selection, Insert key
overtype, overtype (text.overtype), recent documents count, measurement
units (dialogs, Layout ribbon indents and rulers).
file.options reads and sets the engine-side settings headlessly and only
opens the page when given no setting. ui.json loads leniently: a bad value
falls back to its default instead of dropping every setting.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Right-clicking a picture, shape, text box or chart selects it (as a
click would) and its context menu offers Wrap Text: In Line with Text,
Square, Tight, Through, Top and Bottom, Behind Text, In Front of Text
(current one checked), then Move with Text / Fix Position on Page.
The latter two are a new engine command, arrange.moveWithText, which
re-anchors a floating object's vertical position to its paragraph or
the page without moving it. The menu items live in one shared function
(ribbon::wrap_text_items, WRAP_STYLES) for the ribbon to reuse.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Model: `ParaProps::mirror_indents` (`w:mirrorIndents`, ECMA-376 §17.3.1.18), read and
written in DOCX; resolved `mirror_indents` and `suppress_line_numbers` (so styles count).
- Layout: a paragraph with mirrored indents moves over by its indents' difference on even
(left-hand) pages, so its left (inside) indent is on the right there.
- Paragraph dialog: Mirror indents (Left/Right become Inside/Outside), Don't add space between
paragraphs of the same style, and Formatting exceptions (Suppress line numbers, Don't
hyphenate) on Line and Page Breaks; OK sends only the options that changed, in the same
`para.set` as the indents and spacing.
- Strings in all nine catalogs; Reveal Formatting names mirrorIndents.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The canvas is now the AccessKit document node: body paragraphs become
paragraph, heading (with level) or list item (with level, grouped into
lists, list numbers as list markers) nodes holding one text run per laid-out
line with character lengths, positions, widths and word starts; tables are
table/row/cell nodes, pictures and charts image nodes named by alt text,
hyperlinks link nodes around their runs. The caret and selection are an
AccessKit text selection on the document node.
Only pages on screen and one either side are described, capped at 6000
nodes; nothing is built unless AccessKit is active, and the tree is rebuilt
only when the layout, selection, described pages or their screen place change.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Compare, Restrict Editing and AutoCorrect Options dialogs (#412)
- Compare / Combine Documents dialog: original (this document or a file) and
revised document, label changes with, More >> comparison settings, word or
character level, show changes in. review.compare honours case changes,
white space, formatting (tracked formatting changes), tables (compared and
kept, no longer dropped) and character level; the LCS no longer drops text
past its size cap. Unsupported settings are accepted and reported.
- Restrict Editing pane: formatting restrictions (locked styles,
w:formatting), editing restrictions, exceptions for everyone
(w:permStart/permEnd), Start Enforcing Protection with an optional password
(SHA-512, salt, spin count 100000; ECMA-376 §17.15.1.29) and Stop
Protection. Enforced in Session::run: outside the exceptions nothing may
change; undo can't lift protection; passwords never land in Repeat/macros.
DOCX round trip of protection, hash attributes, locked styles and ranges.
- AutoCorrect Options dialog: replacement list (add/replace/delete, built-in
entries too), capitalization options with exceptions, AutoFormat As You
Type switches, Math AutoCorrect list. Saved per user in ui.json; damaged
values read as defaults. Names of days are capitalised.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Restrict Editing: verify Word's legacy protection hash; refuse unsupported forms (#412)
- Stop Protection checks Word's legacy w:documentProtection hash
(w:cryptAlgorithmSid/w:hash/w:salt/w:cryptSpinCount) like Word: the password
becomes the legacy 32-bit key (ECMA-376 Part 4 §14.8.1 initial code array and
encryption matrix), its reversed bytes as an upper-case hex string are hashed
with the salt prepended and the iteration number appended. A wrong password
is rejected; the form no longer unlocks with any password.
- Unsupported or damaged hashes (MD2/MD4/MD5 and other SIDs, w:algIdExt,
unknown algorithmName, spin counts above 10,000,000, bad base64) refuse with
"This password type isn't supported" and the protection stays on. A
protection without a hash still stops without a password.
- Known-answer tests: the specification's "Example" key 0x64CEED7E and an
independently computed SHA-1 hash; pane message in all 9 catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Dialogs: Line Numbers, Hyphenation, Language, Envelopes and Labels (#407)
- Line Numbers (Line Numbering Options…): add numbering, start at, from
text (auto or a distance), count by, restart each page/section or
continuous; `layout.lineNumbers` takes start/countBy/distance.
- Hyphenation Options: automatic, words in CAPS, hyphenation zone,
consecutive-hyphen limit; kept in DOCX (w:hyphenationZone,
w:doNotHyphenateCaps, w:consecutiveHyphenLimit) and honoured by
layout. Manual…: `layout.manualHyphenation` finds words at line starts
that would fit hyphenated on the line above; Yes inserts an optional
hyphen, No skips.
- Language: localized language list, do not check spelling or grammar,
detect language automatically (per-user preference), Set As Default
(document default language).
- Envelopes: delivery/return address, common sizes (#10, #9, Monarch,
DL, C4, C5, C6, B5); Add to Document inserts an envelope section
before the letter. Labels: common sheets (Letter and A4), full page or
a single label at row/column, one per recipient; New Document builds
the sheet with gap columns.
- Ribbon: Line Numbering Options…, Hyphenation Options…, Manual…; the
Language, Envelopes and Labels buttons open their dialogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Remove duplicate Label: keys in ru and tr (#407)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Model: Float carries AltText (title, decorative; the description of shapes and groups);
InlineObject::alt_text/set_alt_text/is_decorative; TableProps.description. Capped input.
- DOCX: wp:docPr / pic:cNvPr / wps:cNvPr descr, title and the [MS-ODRAWXML] adec:decorative
extension read and written for pictures, shapes, text boxes, groups and members, charts;
OLE objects get their alt text in the kept XML; w:tblCaption + w:tblDescription.
- Commands: object.altText {text?, title?, decorative?} (picture.altText kept as alias),
table.altText {title?, text?}, view.accessibilityPane, view.altTextPane.
- Checker (cmd/accessibility.rs): alt text on every object and table (decorative passes),
heading order, header rows, blank table rows/columns, merged cells, WCAG contrast,
floating objects, blank paragraphs; issues carry pos/select and fix actions.
- UI: Accessibility pane (grouped, jump-to, fix buttons), Alt Text pane, Picture/Shape
Format > Accessibility > Alt Text, right-click > View Alt Text...; strings in all catalogs.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* References dialogs: Caption, Index, Table of Contents, Create Source (#402)
- Caption: labels (built-in plus New/Delete Label, kept with the interface
settings), above/below the item, leave the label out, Numbering (format,
chapter number from a heading level with a separator) -> SEQ \* \s and
STYLEREF n \s fields; Update Field renumbers SEQ with \s restarts, \r, \c, \h.
- Index: indented/run-in, columns, right-aligned page numbers with tab leader,
letter headings -> INDEX \h \c \e \r; Mark Index Entry (subentry,
cross-reference, page-range bookmark, bold/italic page numbers, Mark All)
-> XE \t \r \b \i. The index is rebuilt honouring them; columns become a
continuous section of their own. Index commands move to cmd/index.rs.
- Custom Table of Contents: page numbers, right align, tab leader,
hyperlinks, levels, Options (heading styles, other styles with levels,
outline levels, TC fields), Modify for TOC 1-9 -> TOC \o \h \z \u \t \f
\l \n \p; updating honours them (hyperlinks to _Toc bookmarks).
- Source Manager and Create/Edit Source: source types with their fields,
show every bibliography field (new Source fields); Insert Citation menu
lists the sources and Add New Source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fmt (#402)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Draw: Lasso Select, Add Pen, Ink to Shape and Ink Replay (#395)
- draw.lasso: with no points picks up the lasso; with a loop on a page it
selects the strokes mostly inside it and drawings whose centre is inside.
draw.lassoMove / draw.lassoColor / draw.lassoDelete move, recolour and
delete the selection (one undo step each); it lasts until the document
changes otherwise. In the app: drag a loop, drag the selection to move it,
Delete deletes, Esc lets go, the Color menu recolours it.
- draw.addPen / draw.removePen: a pen gallery (kind, colour, width; capped,
validated) shown after the built-in pens, kept in ui.json; damaged entries
are dropped on reading. Right-click a gallery pen to remove it.
- draw.inkToShape: toggles Ink to Shape (pen and pencil strokes that look
like a line, rectangle/square, ellipse/circle, triangle or polygon of up to
eight corners become that shape, in the same undo step); with ink
lasso-selected it converts the selection (draw.convertInk). Our own
recogniser (cmd/ink_shape.rs): chord test for lines, closure test,
ellipse RMS fit, closed RDP simplification with corner merging.
- draw.replay: Ink Replay plays the strokes back in drawing order with a
Rewind / Play-Pause / Forward / Close bar; pages are drawn without ink
meanwhile. Each new stroke records its drawing order (Freeform::order),
written to DOCX in the drawing name (`Ink Pen 3 #12`) and read back.
Catalog 402/434 -> 406/434 (93.5%). New strings in all nine catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Regenerate parity checklist after rebase (#395)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>