Commit Graph
170 Commits
Author SHA1 Message Date
Gabriel MatozoandBrandon Thomas 117aea31a8 Navigation pane: a long heading wraps within the pane instead of ending in an ellipsis (#3015) (#3029)
The pane drew each heading with `Button::selectable(..).truncate()`, so a heading wider than the
pane ended in an ellipsis and its text had to be read from the hover tooltip. Word wraps the row
instead.

`nav_heading` now wraps: the button takes `Button::wrap()` and a minimum size of the available width
and one row, so the label continues on further lines inside the pane, the row grows with the text,
and the pane still keeps its width. The trailing grow atom still keeps the text flush left under its
parent (#1199). The hover tooltip goes with the ellipsis: nothing is hidden now, and a word wider
than the pane is broken by the wrap instead of overflowing.

The navigation test asserts the wrap instead of the ellipsis. For a heading well past the width and
for a single word wider than the pane, the label has more than one line, is not elided, stays inside
the pane, does not widen it, and takes a taller row than a one-line heading in the same pane.

`docs/ui-parity.md` and `ROADMAP.md` record the new behaviour.

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
2026-10-19 03:08:47 -04:00
ReesandBrandon Thomas 61e565cd1c fix(ui): keep recent fonts visible after an unmatched query (#2068) (#2929)
Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
2026-10-18 17:10:01 -04:00
3af927501b Ribbon collapse review follow-ups: collapsed groups stay open, Styles kept (#1285) (#1293)
* #662: ribbon groups shrink to fit the window (compact, then one drop-down)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #1285: collapsed ribbon groups stay open while used; Styles gallery trims before it folds

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Ribbon collapse: accessible name for folded groups, Tables/Links icons (#662)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Ribbon: Esc in a collapsed-group popup doesn't leave full screen; tab-strip test ignores same-named ribbon buttons (#1285)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Ribbon tab-strip test: pick the tab, not a same-named folded group (#662)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Ribbon: a folded group's open state is keyed by its place in the row, not just its title (#1285)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* rustfmt: collapsed_group signature (#1285)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 12:03:32 -04:00
e85fc75808 File (Backstage): an icon beside each side-menu item (#1714) (#1723)
* File (Backstage): an icon beside each side-menu item (#1714)

The side menu (Home, New, Open, Info, Save, Save As, Print, Export,
Close, Options) was text only. Each item now has an 18 pt icon in the
label colour, greyed out with it when no document is open. Existing
icons where they fit (house, new page, open folder, info, save, printer,
close), and three new ones drawn in code: Save As (the save disk with a
pencil), Export (a page with an arrow leaving it) and Options (a gear).
Original artwork, no asset files.

Test: every item names its own icon, and each one is a drawn icon, not
the letter-tile fallback an unknown name gets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: one Backstage row with both the Close (#1712) and side-menu icon (#1714) notes

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>
2026-10-18 11:57:55 -04:00
528da1f6a2 Comments: a draft survives a failed commit (#1348) (#1512)
* #1348: WIP

* #1348: keep comment drafts per document, keep them on a refused edit, write them before save/close/new/open

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #1348: a refused comment draft can be dropped; fields read-only where comments can't change

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #1348: ui-parity: one Task panes and Context menus row after the rebase

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Docs after rebasing on main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #1348: keep the window open while a web save is unconfirmed; split the roadmap row by operation

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #1348: pending comment drafts count as unsaved changes for the browser; a save error isn't hidden by a draft refusal

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #1348: an agent can't replace the document over a pending comment draft without force

Co-Authored-By: Claude Sonnet 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>
2026-10-18 11:57:07 -04:00
914e92b163 Ribbon groups shrink to fit the window (#662) (#708)
* #662: ribbon groups shrink to fit the window (compact, then one drop-down)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Ribbon collapse: accessible name for folded groups, Tables/Links icons (#662)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Ribbon tab-strip test: pick the tab, not a same-named folded group (#662)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:56:02 -04:00
8b0f1762a1 Split Cells on merged cells: divisor message and 1x1 top-row cut (#2592) (#2913)
* #2592: Split Cells on a merged cell: divisor message, 1 x 1 cuts the top row off (Word check)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #2592: Split Cells dialog shows the divisor message; docs

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #2592: clippy

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #2592: Split Cells 1 x 1 on a merged cell changes nothing (corrected Word check)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Split Cells on a merged cell follows Word: divisor message, 1 x 1 changes nothing (#2592)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:55:32 -04:00
2646700ca0 #2573: Picture Format › Size takes sizes in any unit, pixels too, and opens a Size dialog (#2889)
* #2573: Picture Format › Size takes sizes in any unit, pixels too, and opens a Size dialog

The picture size options were too limited (#2573, and #1901's request to enter pixels without
deforming the picture): the ribbon's W/H fields were points only, rejected "300 px", resized on
every keystroke (one undo step per digit), and there was no way to scale from the original
size, lock or unlock the aspect ratio, or reset the size.

- Picture Format › Size: W/H show the interface unit (Options › Advanced) and take a size typed
  in any unit, `px` included; it applies on Enter as one undo step, and the other side follows
  so the picture keeps its proportions. A drag still applies live.
- The Size group's launcher (and `picture.size` run without a size) opens a Size dialog: height
  and width, scale of the original size or of the size it opened with, Lock aspect ratio,
  Relative to original picture size, the original size with its pixels, and Reset.
- `picture.size {"original": true, "scale"?}` sizes from the picture's original size (its pixels
  at 96 ppi, read from the image adjustments started from, less what is cropped). A size past
  4000 pt that keeps the proportions now shrinks as a whole instead of flattening.
- New strings translated in every interface catalog.

The crop tool already exists (Picture Format › Size › Crop) and is being extended in #2027 and
#2440; options cut off in a narrow window are what #708 and #1293 (ribbon groups that shrink to
fit) address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C

* contributors: credit @matteo-andreuzza by their GitHub profile name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C

* #2573: Size keeps the proportions at the 1–4000 pt limits and follows the selection

Review follow-ups (CodeRabbit on #2889):

- Size dialog Reset on a photo larger than 4000 pt (a 6000 px wide one is 4500 pt) set it to
  the original size, which `picture.size` then clamped side by side: the picture was flattened.
  Reset now shrinks both sides by one factor.
- Locked, a side typed past the limit clamped the other on its own (15×30 pt with width 4000
  became 4000×4000). Both sides now scale by one factor.
- `fit_size` (engine) only shrank: a proportional size under 1 pt (scale 1% of 50×200 pt) became
  1×2 pt. It now picks the factor nearest 1 that brings both sides within 1–4000 pt, falling
  back to side-by-side clamping only for shapes too long and thin for any; the dialog uses it.
- Dialogs aren't modal: picking another picture while the Size dialog was open made OK size it
  with the first one's numbers. The form now follows the selection (starts over on the new
  object, closes when nothing is left to size).
- Doc comments on the Size form's methods.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C

* #2573: Size dialog reads the picture again after a crop, change or resize

Review follow-ups (CodeRabbit on #2889):

- The open Size form only compared the selected object's position. A crop from the ribbon,
  Change Picture or a handle drag while the dialog is open left Reset and OK working from the
  old crop and size. The form now remembers the picture, crop and size it was read from (bit for
  bit, without comparing whole objects that may hold large embedded data) and reads them again
  when any changes.
- An egui harness test drives the dialog end to end: launcher, a height typed in pixels, nothing
  applied before OK, OK sizes in proportion as one undo step and closes the dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C

* #2573: size fields never resize a picture just by showing it

Review follow-up (CodeRabbit on #2889): egui's DragValue clamps a value already outside its
range when it is drawn and reports that as a change. The Size fields act on changes, so:

- Picture Format's W/H resized a picture smaller than 1 pt (a 1×1 px spacer is 0.75 pt) or
  larger than 4000 pt as soon as the tab was shown, an edit and an undo step nobody asked for.
  The code before this PR had the same problem with its 4–2000 pt range.
- The Size dialog's scale fields silently changed a picture enlarged past 10000% (a small icon
  made large), and its lengths a size outside 1–4000 pt, which OK then applied.

The range now bounds only what is typed or dragged (`clamp_existing_to_range(false)`). Both
regression tests fail without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C

* #2573: Shape Format and Chart Design size fields leave existing sizes alone too

Review follow-up (CodeRabbit on #2889, outside the diff): the same egui clamp as in d56a105.
Shape Format's W/H (4–2000 pt) and Chart Design's (36–2000 pt) clamped a size outside their
range as soon as the tab was shown and resized the object unasked. The new Size dialog makes this
easier to reach, since it takes shapes up to 4000 pt, but a thin line under 4 pt was already cut.
Both now show such a size as it is and act only on a new value. Shape Format's range follows
`picture.size` (1–4000 pt), so dragging a 3000 pt shape no longer snaps it back to 2000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C

* #2573: Esc cancels a size typed in Picture Format; a typed size applies when the field is left

Review follow-up (CodeRabbit on #2889, outside the diff) asked to apply typed sizes on Enter only.
That is not done: a field keeps what was typed when it loses focus, as text fields do, and
otherwise typing a height in the Size dialog and clicking OK would throw it away. A harness test
now pins that path with a real click (press, then release frames later).

Checking it turned up a real bug: Esc did not cancel in the ribbon's W/H fields. egui 0.36 keeps
a DragValue's typed text after Esc and applies it the next frame, when the field still counts as
having just lost focus but Esc is no longer pressed. The fields now remember when Esc ended the
typing and drop that late value. (In the dialog Esc cancels the whole dialog, so nothing applies
there either; both are tested.)

Docs and comments now say "applied once typed (Enter, Tab or a click elsewhere; Esc cancels)"
instead of "applied on Enter".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:54:25 -04:00
866b48ac55 Recipient list: find box and merged-only view (#1576) (#2621)
* #1576: Recipient list search and filtered view

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #1576: drive the recipient grid through egui_kittest

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #1576: recipient search treats final sigma as sigma

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* #1576: removing a row lets go of the focused cell

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:45:59 -04:00
8c812bf1d6 #1230: Insert Caption on pictures and shapes; Different First Page in Page Setup (#2615)
* #1230: Insert Caption on pictures and shapes; Page Setup header and footer options

- Right-clicking a picture, shape, text box or chart offers Insert Caption…, as in Word.
- references.caption with a picture or shape selected puts the caption next to the
  paragraph holding it, in the same cell or text box (it used to go after the whole table).
- The Caption dialog proposes Figure for a picture selected in a table, not Table.
- Page Setup: Headers and footers check boxes (Different Odd & Even Pages, Different First
  Page), applied on OK only if changed, in one undo step; a title page can go unnumbered.
- Tests: engine caption_for_an_object_in_a_cell_stays_in_the_cell; kittest
  right_click_on_a_picture_offers_insert_caption and
  page_setup_different_first_page_leaves_the_title_page_unnumbered.
- Docs: ui-parity, target-app-parity, ROADMAP progress log.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW

* ui-parity: keep Insert Caption on the context-menu row

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:45:24 -04:00
beedd1b30d Drop Cap Options dialog (#2601) (#2602)
* #2601: Drop Cap Options dialog

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* gaps: keep the Drop Cap Options note on the dialog-depth row

* i18n: translate the two Drop Cap disabled reasons in the audited catalogs

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:44:30 -04:00
bdb7672c0f Dragging the scrollbar names the page at the top of the window (#2551) (#2575)
* #2551: dragging the scrollbar names the page at the top of the window

While the canvas's vertical scrollbar is dragged, a tip beside the pointer
says which page is at the top of the window, in the status bar's words
("Page 7 of 10"), so a drag through a long document shows where it will land.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #2551: the scrollbar tip names the page whose visible band is at the top, also with Hide White Space

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>
2026-10-18 11:43:09 -04:00
ReesandBrandon Thomas 1005c59bfc fix(ui): keep note editing text readable (#2545) (#2562)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:41:58 -04:00
Marco ArnulfoandBrandon Thomas 913621c1c4 Font dialog: Kerning for fonts and its Points and above size (#2295)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:41:26 -04:00
63ee34c557 #2105: Highlight No Color sticks to the button (#2208)
No Color removed the highlight from the selection, but the Highlight
split button kept the colour picked before, so the next click on it
highlighted again. As in Word, No Color now becomes the button's choice
and the next click removes highlighting. The ribbon and the mini toolbar
share one highlight menu (the ribbon's copy is gone); No Color is
translated in the mini toolbar and no longer shows Ctrl+Alt+H beside it
in the ribbon.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:40:55 -04:00
Antoine BoxhoandBrandon Thomas 61a626820e #1552: no spelling squiggle under the word being typed (#2183)
Word draws no red squiggle under the word being typed. It appears when the word ends or the
caret leaves it by a key or a click. The session records the word after a text command that
edits the caret's paragraph, with apostrophes and hyphens counted as the spelling check counts
them, and drops it once the caret is anywhere else. The layout leaves that one range out of the
spelling squiggles, and a caret move without an edit lays out only that paragraph again. F7 and
the Spelling pane still report the word, and grammar marks are unchanged.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:39:21 -04:00
Antoine BoxhoandBrandon Thomas aed0b71786 #1878: Rulers thin out with the zoom and span the caret's page (#2093)
Ruler numbers now thin out as the page shrinks on screen: a number
every 1, 2, 4, 8... units, whichever keeps them at least 30 px apart,
and the small ticks drop out below 4 px, with the text at its usual
size. A number gives way to an indent, tab or column marker sitting on
it, and the margins get ticks only. Both rulers use the caret page's
rect and scale in every layout, including One Page and Multiple Pages,
and follow the caret to another page. Tests cover the density rule, the
marks, the marker rule for indents, tab stops (set or dragged) and
table columns and rows, and the multi-page geometry.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:34:26 -04:00
Antoine BoxhoandBrandon Thomas 0f5e945a69 #1945: Hide the Quick Access Toolbar and show it again (#2089)
The Quick Access Toolbar's menu ends with Hide Quick Access Toolbar, as Word's does. Show Quick Access Toolbar brings it back from the ribbon's right-click menu and from a check box on Customize Ribbon's Quick Access Toolbar page. The setting is kept between runs, and keytips skip the toolbar's digit badges while it is hidden. The UI command ui.quickAccessToolbar drives it from scripts and MCP. Regenerates docs/parity-checklist.md, which was behind the catalog. Adds my contributor entry.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 11:34:10 -04:00
ReesandBrandon Thomas ff39a5c357 fix(ui): deliver control input in inactive windows (#2420) (#2530)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 10:28:37 -04:00
ReesandBrandon Thomas 1595343fa0 fix(ui): expose save confirmation context to screen readers (#2489)
* fix(ui): expose save confirmation context (#1015)

* #1015: require alert-dialog semantics in save regression

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 10:28:20 -04:00
09b822fdbe Update Field after save and reopen of a table of contents; no extra blank line per cycle (#2182) (#2553)
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 10:27:11 -04:00
28d6260a81 #1789: Add Font imports every selected font file (#2404)
* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #1789: cap font bytes waiting in the web inbox

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 10:24:26 -04:00
5c4408f341 Paste: plain text keeps the formatting of the paragraph it lands in (#2423) (#2424)
Paste of plain text from another program gave every line after the first
a paragraph with default formatting. The text became a fragment of
unformatted paragraphs (Fragment::from_text), and insert_fragment keeps
the target's formatting only in the first one. Pasting several lines into
a right-to-left paragraph turned all but the first left-to-right, and the
text after the caret lost its paragraph formatting too.

Paste now inserts plain text as Keep Text Only does. Each line after the
first splits the paragraph as Enter does, so it keeps the paragraph's
direction, style, alignment, indents, spacing and list. Empty lines become
empty paragraphs with that formatting. Copies from WordCraft and HTML or
RTF from other programs keep their own formatting as before. A paste is
still one undo step.

Tests cover a right-to-left paragraph, empty lines and a final line break,
and a list. The typing and UI parity docs and the ROADMAP progress log
have rows for the change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 10:23:43 -04:00
b8279c4da5 #1960: add editable table cell margins dialog (#1988)
Co-authored-by: justinjohn0306 <babyjjbabyjj549@gmail.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:56:48 -04:00
Diego GueriniandClaude Opus 5.5 879e688fec File › Close closes the document and keeps the window (#1712) (#1996)
* File › Close closes the document and keeps the window (#1712)

File › Close (Mod+W, Mod+F4) quit the whole app. In Word it closes the
document and the window stays. WordCraft already has that state from
the Start screen (#1129): no page, the ribbon greyed out but for Help,
File › New and Open to go on.

`WordApp::execute` now handles `file.close` itself. After the usual
Save / Don't Save / Cancel, the document is replaced by the placeholder
blank document that marks "no document open" and the Backstage closes.
The window's close button passes `{"quit": true}`, so it still asks and
then quits. The parameter travels with the prompt, so a Cancel leaves no
state behind. With no document open, Close has nothing to close.

Tests: the Close tests that used `quit_requested` as "Close went through"
now check that the document closed and the window stayed. Closing from
the empty window asks nothing, File › New goes on from there, and the
window's close button still quits after Don't Save.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Close keeps the window for programmatic closes too (#1712, #1391)

#1391 landed on main meanwhile: a programmatic `file.close` on unsaved
work needs `{"force": true}`. Its tests, and the save prompt's focus
test, took "Close went through" to mean `quit_requested`. With #1712 a
close, forced or not, closes the document and keeps the window, so they
now check that the document closed and the app is still running. The
`force` guard itself is unchanged.

docs/control-protocol.md: a closed document leaves the window open, and
`file.new` or `file.open` goes on from there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #1712: a direct file.close never discards unsaved changes unless forced; Don't Save forces it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:38:16 -04:00
Marco ArnulfoandBrandon Thomas 4362e6624d Word's Spike: Ctrl+F3 collects cuts, Ctrl+Shift+F3 inserts them (#2507)
* Spike: Ctrl+F3 collects cuts, Ctrl+Shift+F3 inserts them and empties it (gap #24)

* Search test: a lone INSERT's witness within the box's 14 results; Table checked past it

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:57:27 -04:00
7c95f02050 #1743: Ctrl+wheel zoom no longer scrolls the document (#2386)
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:53:42 -04:00
559f16601d A mouse drag on a table no longer scrolls the view to the caret (#1738) (#2008)
* Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:43:13 -04:00
SlavenDjandBrandon Thomas 31cb2c8714 #1861: searchable Interface Theme command (View, light/dark/system + toggle) (#1868)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:26:19 -04:00
c5a9c954f7 Track Changes Options: Moves (move tracking, Moved from/to marks and colours) (gap 5) (#1779)
* track-move-options: WIP

* track-move-options: Moves options in Track Changes Options and w:doNotTrackMoves

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* track-move-options: Moved from Hidden leaves the layout; per-revision hiding

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* track-move-options: Word's mark lists, (none) moves are plain, no borders on collapsed paragraphs

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* track-move-options: hr and sv translations for Moved from:/Moved to:

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:24:34 -04:00
e7f5d32c35 Numbered headings: heading-linked multilevel list presets keep the heading styles (#1793) (#2239)
* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #1793: wording: other paragraph styles become Heading 1, not only body text

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>
2026-10-18 04:22:12 -04:00
1d37043128 AutoCorrect: reversing a capitalisation adds an automatic exception, as in Word (#1857) (#2227)
* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #1857: forget the capital when a command fails; INitial CAps auto-add checked in Word

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>
2026-10-18 04:21:42 -04:00
Antoine BoxhoandBrandon Thomas 72cea2daf4 #1885: dialog close box fills red on hover, whole box clickable (#2090)
Dialogs, the Read Aloud player and the file browser used egui's small close glyph: the hit area was the glyph alone and hovering only changed its stroke. The close control is now a box at the right end of the title bar, the full title-bar height and 1.6 times as wide, as a Windows caption button on a Word dialog. The whole box closes the window. It fills red with a white cross while the pointer is over it and darker red while pressed and still under the pointer, with colours from the theme tokens in both themes. The box is pointer-only, so Tab never lands on it; dragging from it does not move the window and Escape closes dialogs as before. Its accessibility name is translated like its tooltip.

docs/parity-checklist.md is the regenerated checklist.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:04:12 -04:00
a30a1866e2 Web: keep preferences such as dark mode between visits (#2132) (#2205)
The web shell never loaded or saved the UiState the desktop app keeps in
ui.json, so a reload reset the theme and every other preference. It now
restores it from localStorage at startup and writes it back whenever it
changes (a reload runs no exit code).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:03:16 -04:00
Antoine BoxhoandBrandon Thomas 04eadcb2ca #2068: Recently Used Fonts at the top of the font list (#2192)
The font box (ribbon and mini toolbar) opens with a Recently Used Fonts
section: the fonts applied from the box, the mini toolbar or the Font
dialog, most recent first, once each, ten at most, kept in ui.json. All
Fonts follows. The sections show while nothing is typed; a query lists
its matches only. Up/Down walk the recent entries then the list and Enter
applies the highlighted one; a click on a heading keeps the list open.
The typed list is sized again when its length changes, in a discarded
pass so it never blanks; it used to stay one row tall after a one-match
query. Names over 128 bytes are not kept, and reading a saved list stops
at ten names.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:02:57 -04:00
1cc3d190a7 Fix Find navigation to show search results (#2096) (#2118)
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:00:07 -04:00
6670b9c867 Tab strip never draws tabs over Comments / Editing / Share (#2026) (#2084)
The strip kept File, the active tab and contextual tabs however narrow the window got, so
they were drawn under the buttons on the right. The strip is now planned first: the buttons
keep their room and the tabs give way in order (tab padding, Comments and Editing labels,
Share label, plain tabs into », active and contextual labels cut with …, last the active tab
into »). File always stays. A regression test checks widths 300-1280 in en, pl and de.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:59:19 -04:00
AlikandBrandon Thomas d8b11c69d3 Columns on selected text go in their own section (#2024) (#2071)
With text selected, Layout > Columns used to change the whole section. As in
Word (observed black-box), a continuous section break now goes where the
selection starts and one where it ends, splitting a paragraph there, unless that
end already meets a section boundary (the document's start or end, or an
existing break); the columns go on the section between, in one undo step.
`layout.columns` gains `apply: "selection"`, its default when text is selected
in the main text. More Columns... offers "Selected text" and picks it then.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:59:01 -04:00
8fb4d44d2c Different First Page: page 1's header can be edited and inserted (#2826) (#2833)
With Different First Page on, page 1 shows the first-page header, but
Insert > Header/Footer/Page Number always wrote the default one (which
page 1 no longer shows), Edit Header without a page went to the default
too, and a double-click in page 1's header area was ignored while
another header was being edited (the usual case: edit the header, tick
Different First Page, double-click page 1). Now hf_part and edit_hf use
the slot of the caret's page, and a double-click in a header/footer
zone switches to the one that page shows even from another header.

Fixes #2826
Fixes #2811


Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:47:13 -04:00
ce38b0c372 #1297: copied equations go in the MathML clipboard formats Word reads (#2614)
A copy or cut of one equation on its own now also puts its MathML in the
platform's MathML clipboard formats, beside the HTML (with MathML, #816) and
the linear text: "MathML" and "MathML Presentation" (UTF-16LE) on Windows,
public.mathml.presentation and public.mathml on macOS. These are the names
W3C MathML 3 section 6.2.4 registers, and Microsoft's "MathML Support in
Microsoft 365" doc says Office checks them first when it pastes math. No
public clipboard format carries OMML. A copy with text around the equation
doesn't offer them, so Word keeps the text.

Not yet verified in Word: the parity docs say MathML clipboard formats, not
an editable paste.


Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:46:15 -04:00
55bef52d38 Comments readable in the dark theme (#2045) (#2563)
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:22:46 -04:00
df9c29f673 Read Mode keeps the keyboard and shows whole, unobscured columns (#1772) (#2591)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:22:26 -04:00
b2b8ad8f52 Font list: entries of tall-ascent fonts are no longer cut off (#2570) (#2598)
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>
2026-10-18 03:21:48 -04:00
aa665b16a9 #2592: Split Cells and Convert Text to Table dialogs (#2593)
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:21:14 -04:00
8c1a39ba95 A new source's Tag follows Word's format; editing keeps the Tag (#2094) (#2571)
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:20:56 -04:00
16622f0853 #1876: Web Layout text fills the window width (#2539)
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 03:20:37 -04:00
Rees 2f6311cc37 M15: keep inherited proofing choices in Language dialog (#2717) (#2736)
Read effective character formatting so unchanged OK preserves language and no-proof choices inherited from defaults and styles.
2026-10-17 17:00:10 -06:00
b3b6c451e3 fix(ui): draw Arabic and other non-Latin comment text in balloons (#950)
* fix(ui): draw Arabic and other non-Latin comment text in balloons

Comment balloons and the Comments pane draw comment text and author
names with the interface faces (Inter, Source Sans, CJK), which have no
Arabic, Hebrew, Thai and so on: every such letter was a box. The app now
borrows a document fallback face that covers the comments, as the window
caption does (#840), checked once per document revision.

egui also lays bidi runs and words out left to right, so a right-to-left
comment read backwards. Read-only balloons now wrap the text by word
widths, put each row in display order (Unicode bidi runs, words of a
right-to-left run reversed, brackets mirrored) and set right-to-left
paragraphs against the right edge. Text without right-to-left letters is
laid out exactly as before. The editable fields (the selected balloon and
the Comments pane) keep logical order.

Also adds my contributor entry (sync_github_name).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* One contributor entry each

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Mohamed Youssef <ms.rightsteps@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 01:34:11 -04:00
dac8c6d6c7 Table style review follow-ups: No Border on inherited parts, cell Top beats the style (#1288) (#1296)
part that inherits its lines sends it, and the picker only shows No Border checked for it.
- CellProps::valign is Option<VAlign>: a cell's own Top (Align Top, w:vAlign="top") beats a
  table style part's vertical alignment, and round-trips through .docx.
- tableStyle edit rejects Part fields at the top level without "part".
- "font": null or "" makes a part's font inherited; clearing the dialog's font box sends it.
- Document ui.dialog's optional "part" in docs/control-protocol.md.

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:26:42 -04:00
5b49d692a5 Table style editor formats every table style part (#714) (#887)
* #714: table style editor formats every table style part

Modify/New Table Style: "Apply formatting to" any of the 13 ECMA-376
§17.7.6 parts (whole table, header/total row, first/last column,
odd/even row and column bands, four corner cells), each with font,
size, bold, italic, underline, colour, fill, borders (line style,
width, colour; all/none/outside/inside/each edge) and the 9-way cell
alignment, over a live preview.

- doc: TablePart, PartFormat, PartSet; TableStyleParts::part/set_part
  (new parts and per-part jc/vAlign in `regions`)
- layout: generic part resolution incl. band2, corner cells, alignment
- docx: every tblStylePr type read/written with pPr jc and tcPr vAlign
- engine: table.newStyle/modifyStyle take ECMA part names, `part`,
  `parts`, per-edge `borders`, `underline`, `align`/`vAlign`/`cellAlign`
- ui: dialog moved to dialogs_table_style.rs; ui.dialog takes `part`

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>

* Review fixes for #887: Bulgarian strings for the table style parts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #714: Vietnamese strings for the table style parts (main's new vi catalog)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* hr: match the catalog's word for banded

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>
2026-10-15 01:13:56 -04:00