Commit Graph
1024 Commits
Author SHA1 Message Date
Mat Strangeandopencode 3a15b19e7f Docs: the progress docs' status lines carry today's date
Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 17:20:53 +01:00
Mat Strangeandopencode d809c838c4 The CLI's --author names the session, it doesn't change the document
`--author` dispatched `file.setAuthor`, which a protected document refuses
now that the name is checked as the identity its exceptions are matched
against — so a script that named itself on a protected file failed before it
ran a single command.

The flag names the session, not the open document, so it sets the name
directly, with the same normalisation and blank-name check the command uses.
`file.setAuthor` keeps its protection check for the callers that mean to
change the document's identity.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 17:20:53 +01:00
Mat Strangeandopencode f36339ab4b A password guards the protection whatever its mode
The guard that stops the protection of a protected document from changing
asked whether the editing was restricted, so a formatting-only protection —
the mode "none", which limits the styles rather than the editing — was
exempted. A formatting-only protection can carry a password, and that password
guarded it: it was exempt from the guard, so `{"mode": "readOnly", "password":
"other"}` replaced the protection and the password with ones of the caller's
choosing, and the protection could then be stopped with them.

A password guards the protection whatever its mode, as it did before.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 17:20:53 +01:00
Mat Strangeandopencode 376a517cb0 Docs: the Restrict Editing gaps that are closed
Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:32:40 +01:00
Mat Strangeandopencode 823684807b The protection is stopped explicitly, and the docs say so
The test that stopped the protection used the "none" shorthand, which a
protected document now refuses; it stops it the explicit way instead, and
pins that the shorthand doesn't stop it. `file.protect` says the same: it
starts enforcement, and "none" doesn't stop a protected document.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:31:41 +01:00
Mat Strangeandopencode 9aa9eca9e8 Restrict Editing: the post-check compares the settings and the metadata
`outside_unchanged` is the check that rolls a command back when it
changed anything outside the editable regions. It compared the text, the
formatting, the page setup, the document defaults and the styles — but
not the document's settings, its metadata, its custom properties, the
resolved flag of its comments, its list definitions or its bibliography
sources. A command that changed the metadata of a protected document was
accepted and never rolled back: `file.properties {"title": "Forged
title"}` on a protected document with an exception changed the title and
stayed.

The settings, the core properties, the custom properties, the comments'
resolved flags, the list definitions and the sources are compared now, so
a command that changes them is rolled back like any other change outside
the editable regions.

The revisions and the media store stay out of it on purpose: an edit
inside an exception legitimately leaves a revision, and a picture
inserted there adds to the media store. The list definitions and the
sources are compared like the styles: the ones the document had must be
unchanged, but a new one may be added — a list or a citation inserted
inside an editable region adds its definition, like applying a style adds
one.

Saving is unaffected: it writes the metadata, but `file.save` is a pure
command and isn't post-checked, and the protection itself is changed by
`review.restrict`, which is always allowed.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:31:41 +01:00
Mat Strangeandopencode 790b5ba189 Restrict Editing: a protected document's protection needs an explicit stop
The guard in `restrict` refused a protected document only when its
protection had a password, so a password-less one could have its
protection changed — and `file.protect {"mode": "none"}` stopped it
outright. An agent could strip a user's protection silently and then edit
the document freely.

Two rules now, as Word has them (Stop Protection first):

- The protection of a protected document doesn't change, whatever the
  mode. The formatting-only protection doesn't count: it stores the mode
  "none", and it limits the styles, not the editing.
- Stopping the protection is explicit. The "none" mode is the shorthand
  for it, but on a protected document only `{"stop": true}` stops it — the
  way the pane's Stop Protection button does it — so a command that
  merely names a mode can't strip it.

The password still guards the stop, and the formatting-only path (the
mode "none" with the formatting restriction) still works.

`cmd::tools::tests::protection_blocks_edits` stops the protection with
`{"mode": "none"}`; it has to stop it explicitly now.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:31:41 +01:00
Mat Strangeandopencode b58348e738 Restrict Editing: the user name can't change under protection
`file.setAuthor` sets the name a protected document's named exception
ranges are checked against, and the author new revisions and comments are
recorded under. `always_allowed` waved through every `file.*` id, so on a
protected document the name could be changed: an agent could take the
identity of a named editor and unlock their exception range, and forge the
authorship of the revisions it leaves behind.

A protected document refuses the change now, with the rule Word uses for a
content control's properties: stop protection first. The check is in the
command itself — `file.setAuthor` is a pure command, so `check_before`
never sees it — and in `check_before` for the day it isn't.

Word lets the user name change under protection: it is a preference there.
This project doesn't, because that identity gates what may be edited.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:31:41 +01:00
Mat Strangeandopencode bcd751a588 Restrict Editing: a file command that changes the document is checked too
`always_allowed` waved through every `file.*` id, so on a protected document
the file commands that do change it went unchecked: the privacy options and
the properties wrote document settings, and the inspector's removals deleted
content. The privacy options' own comment already said they were "refused on
a protected document"; that wasn't true.

The allow-list now names the file commands that don't change the open
document, and the ones that do are checked like any other command. Reading
the document stays allowed: the inspector without removals, the privacy
settings, the info, and saving a version snapshot.

Restoring a stored version replaces the whole document, and a snapshot's own
protection is stale — restoring one lifted the protection and dropped its
password. The live document's protection, password and formatting limit now
survive a restore.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:31:41 +01:00
Dylan M. Taylor 9b4b948c6e Merge pull request #2854 from dylanmtaylor/chore/drop-competitor-references
docs: drop references to competing commercial software
2026-10-19 19:22:22 -04:00
e6c5a701dd The caret and clicks land between the letters of a ligature (#1974) (#2006)
* 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 <noreply@anthropic.com>

* Fix #1974: a long word never wraps inside a ligature

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

* Fix #1974: a hyphenation point never splits a ligature

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

* #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 <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-19 13:09:22 -04:00
4e2568e2da RTF import: heading and code formatting compared with the file's own stylesheet (gap 19 follow-up) (#2236)
* gap 19: RTF import compares heading and code run formatting with the file's stylesheet

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

* gap 19: review round 1: a style chain with formatting words is complete; unnumbered entries are Normal

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-19 12:57:16 -04:00
b8debb3050 Content controls: typed text takes the control's formatting, in any language (#3116)
* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-19 12:53:44 -04:00
f1b4be8a04 Restrict Editing: forms protection keeps a form's content controls (#3112)
* 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 <noreply@anthropic.com>

* Restrict Editing: parity row states what forms protection refuses

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

* Restrict Editing: correct the comment on content-control property edits in forms mode

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-19 12:49:05 -04:00
2304bf6ac6 Fix Paragraph::sentence_at at the first character of a sentence (#3105)
* 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 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-19 12:45:25 -04:00
NetterSilverandClaude Haiku 5.5 e8866ea58b Keep an ordered list's start number in Markdown import (#3103)
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 <noreply@anthropic.com>
2026-10-19 12:32:33 -04:00
NetterSilverandClaude Haiku 5.5 74ab8d2819 Keep a single leading space in HTML export paragraphs (#3102)
Found while testing crates/formats/src/html.rs (export, around line 1744; paragraph text writer)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 12:32:27 -04:00
NetterSilverandClaude Haiku 5.5 517ba09250 Write large whole numbers without decimals in format_number (#3107)
Found while testing crates/doc/src/locale_number.rs:114

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 12:32:21 -04:00
Diego GueriniandClaude Sonnet 5.5 49a934fba7 #3067: script parse test compares paths with Path::join (#3106)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 12:29:17 -04:00
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
SlavenDj fdf791a8b2 #3045: the empty window's ribbon tabs stay live, so File reaches the Start screen (#3064)
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.
2026-10-19 03:05:41 -04:00
MarekSpodBiedry dbc5c3407b Size the text cursor by the font, not the line spacing (#2920) (#3011) 2026-10-19 03:05:17 -04:00
MarekSpodBiedry 3f9c19d9f3 Opening a missing recent file keeps File open and shows the error (#2938) (#3012) 2026-10-19 03:05:11 -04:00
Martin BallandClaude Opus 5.5 435be0a4cb #2972: regression tests for File page AccessKit focus paths (mac chevron, Options back, Blank document) (#3019)
* #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) <noreply@anthropic.com>

* #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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 03:05:06 -04:00
Martin BallandClaude Opus 5.5 6950c22d73 #2999: a file dialog that "cancels" at once asks again without a parent, then shows the built-in browser (#3030)
* #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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 03:04:59 -04:00
Juan Camilo Agudelo e0ff4b0c3d Paste: Ctrl+V pastes a clipboard picture even when Ctrl is released before V (#3049) (#3058)
* 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)
2026-10-19 03:04:54 -04:00
SlavenDjandBrandon Thomas ebabb57e5e #3039: mini toolbar font colour Automatic returns focus to the document (#3043)
Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
2026-10-19 03:04:49 -04:00
SlavenDj 25ad07f868 #3038: mini toolbar highlight swatch returns focus to the document (#3042) 2026-10-19 02:59:34 -04:00
SlavenDj e1e6c31571 #3040: undo-list row returns focus to the document (#3044) 2026-10-19 02:59:29 -04:00
SlavenDj 396344c31e #3031: Tabs dialog says the list is empty instead of a blank box (#3050) 2026-10-19 02:59:24 -04:00
SlavenDj 6e3f800a8b #3032: Styles pane greys out Delete for built-in styles (#3051) 2026-10-19 02:59:19 -04:00
SlavenDj 83014016c6 #3036: AutoCorrect Replace list says when the filter matches nothing (#3053) 2026-10-19 02:59:14 -04:00
570b992947 #3037: file browser says when a folder (or the filter) shows no items (#3054)
* #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 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 02:59:09 -04:00
SlavenDj 76d8347f77 #2892: rules end flat on screen, like the PDF export (#3061) 2026-10-19 02:44:02 -04:00
SlavenDj 19e5bb48cf #3033: file browser Open/Save uses the themed primary button (#3047) 2026-10-19 02:43:57 -04:00
SlavenDj ab968b072d #3034: SmartArt Pyramid separators follow the theme line colour (#3052) 2026-10-19 02:43:52 -04:00
SlavenDj d26b8dcae7 #3035: WordArt reflection swatch follows the theme's dimmed text colour (#3055) 2026-10-19 02:43:46 -04:00
SlavenDj 68bc118a68 Add @SlavenDj to contributors/people.toml (#3048) 2026-10-19 02:43:34 -04:00
Diego GueriniandClaude Opus 5.5 18c7c1d114 #2845 follow-up: clippy passes on Windows and Linux again (#3006)
`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 <noreply@anthropic.com>
2026-10-19 02:43:28 -04:00
NetterSilverandClaude Haiku 5.5 60ad86acb3 Keep a single trailing space when exporting a paragraph to ODT (#3066)
Found while testing crates/formats/src/odt.rs:354-390 (plain_inlines)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 02:43:22 -04:00
Fin Warman d588e0b873 Limit table row deferral to genuine widow splits (#2925) 2026-10-18 17:11:52 -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
Diego GueriniandClaude Opus 5.5 2d0169e718 A Word 2003 XML or web page saved as .doc opens by what it holds (#2853) (#2942)
* #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 <noreply@anthropic.com>

* #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 <noreply@anthropic.com>

* #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 <noreply@anthropic.com>

* #2853: rustfmt

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:04:33 -04:00
dhruvLearner8 384d5ef307 Cross-reference: caption labels of your own are Reference types (#2940) (#2957)
* 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)
2026-10-18 17:01:41 -04:00
dfgodvananddfgodvan 99ff497180 Layout: a roman page number in the footer stays roman when the body shows the page count (#2966)
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 <180286157+dfgodvan@users.noreply.github.com>
2026-10-18 16:55:08 -04:00
dfgodvananddfgodvan 4b18b307be Selection rectangles walk only the paragraphs the range reaches (#2967)
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 <180286157+dfgodvan@users.noreply.github.com>
2026-10-18 16:53:39 -04:00
dhruvLearner8 972252d0b4 Ruler: margin handles on even mirrored pages edit the visible edge's stored margin (#2914) (#2975) 2026-10-18 16:49:22 -04:00
100dfb37c8 Layout: a word after a slash wraps whole, like Word (#2963) (#2985)
* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude (ThinkSystem) <claude@thinksystem.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 16:41:44 -04:00
Max Murphy 00abb59a85 render: keep the legacy font effect test off antialiasing (#2898 x #2899) (#2990)
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.
2026-10-18 16:40:22 -04:00
Max Murphy 72328ab521 cli: keep the non-UTF-8 file name test off macOS (#2993)
* 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
2026-10-18 16:37:59 -04:00