* Fix#1974: the caret and clicks land between the letters of a ligature
The glyph's advance is shared evenly among its letters (mirrored in
right-to-left text); ligature letters keep their character spacing.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Fix#1974: a long word never wraps inside a ligature
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Fix#1974: a hyphenation point never splits a ligature
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* #1974: ligature tests use a bundled font that draws fi and ffi as ligatures
They assumed the installed default-font stand-in draws ligatures; Liberation Sans (Linux) draws
none, so they failed there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Dmitrii Khlapov <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* gap 19: RTF import compares heading and code run formatting with the file's stylesheet
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* gap 19: review round 1: a style chain with formatting words is complete; unnumbered entries are Normal
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Dmitrii Khlapov <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* Content controls: typed text takes the control's formatting, in any language
Text typed in place of a content control's placeholder takes the
control's own run properties (w:sdtPr/w:rPr, ECMA-376 §17.5.2.27), as
in Word: in a protected form filled in Word for Mac, all 17 entered
values carry the style their control's rPr names. WordCraft kept that
rPr only as XML to write back and gave typed text the placeholder's
formatting minus the placeholder style.
It also knew the placeholder style only by its English id,
PlaceholderText. German Word writes Platzhaltertext (the name stays
"Placeholder Text"), so in forms from German Word every entry stayed
grey, on screen and in the saved file, and restoring a placeholder
added a second Placeholder Text style. The style is now found by its
built-in name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Content controls: typing drops a showing placeholder's own style
A control without run properties that shows its placeholder: text typed
over it drops the placeholder run's character style whatever its id
and name. Word localizes only the placeholder style's id, but another
program may localize its name too, and the name lookup then misses it.
Restoring a placeholder and new controls keep the name lookup.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* Restrict Editing: forms protection keeps a form's content controls
With Filling in forms enforced (w:edit="forms"), Word greys out the
Developer tab's control buttons and Properties, and a field's shortcut
menu offers no Remove Content Control: only the fields' content can be
filled in. WordCraft refused these only outside the fillable fields, so
with the caret in one it removed the control (its content left as
plain, no longer fillable text), inserted new controls inside it, and
renamed it; only lock changes were refused.
Under forms protection, inserting any content control, Properties
(every setting) and Remove Content Control are now refused before they
run. Typing, Toggle Check Box, Choose List Item and Set Date still fill
the fields. Other protection modes are unchanged.
Observed in Word for Mac 16.113 on a protected real-world form
(72 content controls: rich text, check boxes, date pickers); the same
form filled in WordCraft opens in Word still protected, with its values,
ticks and dates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Restrict Editing: parity row states what forms protection refuses
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Restrict Editing: correct the comment on content-control property edits in forms mode
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* Fix Paragraph::sentence_at at the first character of a sentence
Found while testing crates/doc/src/para.rs:1397-1427
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* sentence_at: a terminator followed directly by text doesn't end the sentence at the caret
"Hello.World" at offset 6 gave an empty (6,6) range; only treat the caret as just past a
sentence end when the character at the caret is a space.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Haiku 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Found while testing crates/formats/src/markdown.rs:160-161,463 and crates/formats/src/model.rs:111 (ListInfo)
Co-authored-by: Claude Haiku 5.5 <[email protected]>
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 <[email protected]>
With no document open, the empty window greys the Share / Editing /
Comments buttons out by disabling the whole right-to-left line, and the
tab strip is a child of that line: egui gates interactions on a widget's
enabled state, so every tab was dead. File did nothing, and the empty
window had no way back to the Start screen, or to making a document.
Only those three buttons are disabled now. The test presses Escape on
the Start screen (the empty window, as #1129 has it), clicks the File
tab and asserts the Start screen, with its Blank document, is back.
* #2972: regression tests for File page / sidebar AccessKit focus paths
The macOS back chevron beside the traffic lights (the report's "sidebar
collapse toggle"; there is no other toggle there), File > New/Home > Blank
document, Escape, and File > Options > back (both layouts, with a document
and from the Start screen) all run under kittest's AccessKit. The chevron
and Options tests panic with "Focused ID ... is not in the node list"
before #2856 and pass on main.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* #2972: review — fresh document per Blank document path; Options back asserts closing and focus in every case
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
* #2999: a file dialog that "cancels" at once asks again without a parent, then shows the built-in browser
rfd folds the xdg-desktop-portal's failure response into "cancelled", so on desktops whose
FileChooser portal fails to show a dialog, Save As, Export and Open did nothing and logged
nothing. A cancel quicker than anyone can make one (300 ms) is now taken as that failure: the
dialog is asked for once more without the parent window (GridCraft, which passes none, works on
the reporter's machine), and if that too comes back at once the app shows its built-in browser.
Each step logs a warning.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #2999: review — the quick-cancel retry runs only on Linux and the BSDs; Windows and macOS answer a quick cancel as a cancel
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
* Paste: Ctrl+V pastes a clipboard picture even when Ctrl is released before V (#3049)
egui-winit keeps the Mod+V press, so a clipboard picture is pasted on V's
release, which required Mod still held. Letting Ctrl up first (the natural
one-hand Left Ctrl+V) pasted nothing. A V release with no V press seen was
Mod+V: use the modifiers held with Mod then, so Mod+Shift+V and Mod+Alt+V
stay apart.
Fixes#3049
* Paste: record the modifiers held with Mod in event order (#3049)
Mod can go down and up within one frame, so the frame's modifiers may no
longer have it when the canvas reads them. Take them from each
ModifiersChanged and key press as they arrive. Review feedback on #3058.
* Paste: document the Mod+V release helpers and tests (#3049)
* #3037: file browser says when a folder (or the filter) shows no items
* #3037: Czech and Vietnamese catalogs carry the file browser's no-items string
* #3037: show the no-items hint only for a folder that was read
An unreadable folder already shows its error below the list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
`graphics.rs` allowed dead code only on macOS (where the Windows items are unused). Since #2845
the two indirect-validation functions are called only on macOS, so on Windows and Linux
`cargo clippy --workspace --all-targets -- -D warnings` stopped with "function is never used".
They now carry `allow(dead_code)` off macOS.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* #2853: Word XML, web pages and MHTML saved as .doc open by what they hold
A Word 2003 XML Document named `.doc` went to the Word 97-2003 reader, which refused it ("Invalid
CFB file"), and the window showed a blank page. Word XML (2003 or Flat OPC), a web page and a
Single File Web Page (MHTML) under a `.doc`, `.docx` or `.rtf` name now open by what they hold,
as in Word. MHTML is read for the first time (`.mht`/`.mhtml` too): the MIME parts are split and
decoded, the page is imported as HTML in its charset and its pictures load from the other parts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* #2853: leave MHTML to #1104, keep Word XML and web pages saved as .doc
#1104 adds an MHT reader (`wordcraft_formats::mht`), so this branch drops its own MHTML reader and
the `.mht`/`.mhtml` extensions instead of adding a second one. A Word XML Document or a web page
under a `.doc`, `.docx` or `.rtf` name still opens by what it holds (the #2853 fix).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* #2853: a web page's signature needs its tag name to end (`<htmlish>` is other XML)
CodeRabbit: `starts_with("<html")` took XML with a root like `<htmlish>` under a `.doc` name for a
web page, though XML that isn't a Word document stays an error. `<html` and `<!DOCTYPE html` now
need white space, `>` or `/` after them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* #2853: rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Cross-reference: caption labels of your own are Reference types (#2940)
* Caption labels come from the SEQ field's caption, not a digit (#2940, review)
* Caption label after a chapter number field (#2940, review)
A paragraph with a PAGE field is cached by the page number, the totals, the
section and the bookmarks, but not by the number format the field shows the
page number in. Paginating lays the footer out to know where the body ends,
before the section's number format is set in the field context, so as
decimal. When the body shows NUMPAGES or SECTIONPAGES, the pass that has the
right totals made that layout's key equal to the page's own footer's, and the
cached decimal one was drawn: page 2 of a section numbered in lower-case
roman showed "2", not "ii". The number format is now part of the key.
Co-authored-by: dfgodvan <[email protected]>
Document::paths_between listed every paragraph of the story (every cell
paragraph of every table) and then kept those between the two paths, so each
selection_rects call cost the whole document. The canvas calls it every frame
for each Find reading highlight (up to 5,000), each content control frame,
Read Aloud and the hyperlink under the pointer. It now walks only the
blocks, rows and cells between the two paths: for a one-paragraph range in a
document of 26,000 paragraphs (four 1,500-row tables), 1.0 ms -> 2.6 us.
Co-authored-by: dfgodvan <[email protected]>
* Layout: a word after a slash wraps whole, like Word (#2963)
UAX #14 (LB13) forbids a break between a space and a slash, and the break
after a slash is dropped on purpose (Word keeps "and/or" whole). Together
"caption /word" had no break opportunity at all, so a line too short for
both filled letter by letter and split the word. Add the break before a
slash that follows whitespace and starts a word.
Co-Authored-By: Claude <[email protected]>
* Layout: no break before a slash after a no-break space (#2963)
The break before a slash word took any whitespace, U+00A0, U+2007 and
U+202F included, so "Nr. /12" could wrap after the no-break space.
Only a breakable space counts now. The parity checklist lists the rule
on its own row as untested against Word.
Co-Authored-By: Claude <[email protected]>
* Layout: test the slash word in a fixed-width table cell (#2963)
The report came from a table cell, where the cell's own width is the
line width.
Co-Authored-By: Claude <[email protected]>
* Docs: gaps and typing parity for the slash-word break (#2963)
Doc wording taken from #2994 (maxm11), with the test names of this PR.
Co-Authored-By: Claude <[email protected]>
---------
Co-authored-by: Claude (ThinkSystem) <[email protected]>
Co-authored-by: Claude <[email protected]>
The shadow copy is solid RGB 128; the old 100-160 grey band also counted the glyph's antialiasing, whose centroid hid the copy's offset with some fonts. This was already failing on origin/main on macOS; count the copy's own grey.
* cli: keep the non-UTF-8 file name test off macOS
APFS refuses file names that aren't valid UTF-8, so the fixture can't be
made there (as in tests/non_utf8_args.rs); the unit test runs on the
other unixes.
* chore: add @maxm11 to About-window contributors with their own name
* #662: ribbon groups shrink to fit the window (compact, then one drop-down)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* #1285: collapsed ribbon groups stay open while used; Styles gallery trims before it folds
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Ribbon collapse: accessible name for folded groups, Tables/Links icons (#662)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* 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 <[email protected]>
* Ribbon tab-strip test: pick the tab, not a same-named folded group (#662)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* 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 <[email protected]>
* rustfmt: collapsed_group signature (#1285)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Dmitrii Khlapov <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* 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 <[email protected]>
* docs: one Backstage row with both the Close (#1712) and side-menu icon (#1714) notes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Went through the whole Italian catalog. Fixed the ones that were wrong (Mailings is Lettere in Word, Redo is Ripristina, cursor commands say riga) and made some terms consistent (input penna, Disponi testo, Ombreggiatura).
Also marked Italian as reviewed in the localization doc and added myself to people.toml.
Co-authored-by: Brandon Thomas <[email protected]>
* #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 <[email protected]>
* #1348: a refused comment draft can be dropped; fields read-only where comments can't change
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* #1348: ui-parity: one Task panes and Context menus row after the rebase
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Docs after rebasing on main
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* #1348: keep the window open while a web save is unconfirmed; split the roadmap row by operation
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* #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 <[email protected]>
* #1348: an agent can't replace the document over a pending comment draft without force
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Dmitrii Khlapov <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>