Commit Graph
77 Commits
Author SHA1 Message Date
skylartaylor 5d428e408f M-egui037: reconcile upstream and document migration review fixes 2026-10-25 06:42:20 +01:00
aiongg 5c779b3d96 Merge PR #4098: AutoFormat: *bold* and _italic_ become real formatting as you type
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/tools.rs
#	docs/typing-parity.md
2026-10-25 02:22:19 +00:00
skylartaylor a605fa5ca7 M-EGUI037: reconcile WordCraft main and preserve IME editing 2026-10-25 02:19:38 +02:00
dotCooCoo c4827112ef AutoFormat: AutoCorrect corrects the first word of a marked span of several words
With *Bold* and _italic_ as real formatting on, the opening mark is not
part of the word AutoCorrect reads, so typing `*teh end* ` gives bold
`The end ` instead of `teh end `. A sentence capital is decided from the
text before the mark.
2026-10-24 14:32:36 -07:00
skylartaylor c4974d9e8d egui037: reconcile current main, qualify dependency pins and preview accessibility 2026-10-24 23:22:06 +02:00
aiongg 6ba3d8d2a7 Merge PR #4123: AutoFormat: Enter twice after a short line makes it a heading 2026-10-24 21:05:05 +00:00
aiongg 540d0b2b34 Merge PR #4101: AutoCorrect: words after a no-break space are corrected 2026-10-24 20:42:03 +00:00
aiongg d0a9dde289 Merge PR #4120: Engine: keep cell widths in step after Insert Column; validate a version restore before saving 2026-10-24 20:38:50 +00:00
dotCooCoo 07c052f881 Merge main into feat/autoformat-bold-italic 2026-10-24 13:18:49 -07:00
galagersharp-code d784f328ea Table: a new column keeps cell widths in step with the grid, and a refused restore saves nothing
insertColumnRight shrank the grid but left each cell's width at the old value,
so the page laid the cells out wider than the grid and the saved w:tcW disagreed
with it; the cells now take their grid columns' widths.

file.versions with a restore also saved when the restore was refused (protected
document, no such version, or a restore that is not a non-negative integer):
the restore is checked before anything is saved, a non-integer restore is
refused instead of being read as absent, and an index that does not fit in
usize names no version rather than wrapping to a small one on a 32-bit target.
2026-10-25 03:03:08 +08:00
dotCooCoo 2f0f2952d6 AutoFormat: Enter twice after a short line makes it a heading
AutoFormat As You Type gains "Built-in Heading styles", off by default,
as `headingStyles` on `tools.autocorrect` and a checkbox under "Apply as
you type" in the AutoCorrect dialog.

With it on, Enter in an empty paragraph right after a line of one to five
words that ends in a letter or digit makes that line Heading 1. One or two
tabs before the line make it Heading 2 or 3, and the tabs are removed. The
line must be a Normal paragraph of the main text outside a list. The caret
stays in the empty paragraph after the heading, and Undo right after
brings back the tabs and the Normal style while the Enter stays. With Track
Changes on, the tabs are marked deleted (or removed, when the same author
inserted them) and the style change is a tracked formatting change; a line
holding a tracked deletion stays as it is. A heading style locked by
formatting restrictions is not applied.
2026-10-24 04:32:21 -07:00
dotCooCoo 18ec8b90e8 AutoFormat: Tab and Backspace at the start of a line set the paragraph's indents
AutoFormat As You Type gains "Set left- and first-indent with tabs and
backspaces", off by default, as `tabIndent` on `tools.autocorrect` and a
checkbox under "Automatically as you type" in the AutoCorrect dialog.

With it on, Tab before the first line of a paragraph with text indents the
first line by one default tab stop, and Tab before a later line (after a
line break or where the text wraps) indents the whole paragraph. A second
Tab before an indented or hanging first line also indents the whole
paragraph. Backspace at the start of the paragraph takes the first-line
indent back, then the paragraph's own left indent one tab stop at a time.
An empty paragraph and Tab inside a line still take a tab character.
While formatting is limited to the document's styles, Tab and Backspace
work as with the option off.
2026-10-24 03:18:43 -07:00
dotCooCoo ff19c7d68b AutoFormat: *bold* and _italic_ marks around punctuation alone stay as typed
A span converts only when the text between the marks holds a letter
or digit, so `*?!*` and `_-_` keep their marks.
2026-10-24 01:38:14 -07:00
dotCooCoo 755e2cfb90 AutoFormat: AutoCorrect goes on after *bold* and _italic_ marks
After a `*word*` or `_word_` span is converted on a space, punctuation
or Enter, the word without its marks is AutoCorrected as before:
replacements, capitals, ordinals and links (`*teh* ` becomes bold
`The `). Undo right after still restores the marks in one step, and
after Enter the Enter stays. A marked web address becomes a link
without carrying its bold or italic into the text typed after it.
2026-10-24 00:54:46 -07:00
47edcd2ff2 chore: resolve SonarCloud maintainability issues (#4083)
* chore: resolve SonarCloud maintainability issues

* fix: restore compiling refactors, revert behaviour changes, run cargo fmt

- fontdb::norm: keep the closure for filter (char::is_alphanumeric takes
  self, not &self, so it does not fit Iterator::filter).
- story.rs: write_mark_rpr needs &mut self (rev_attrs, rpr_change).
- Revert the viewport meta change and the return 0 added to
  workspace_version/sha256, which changed behaviour or masked failures.
- cargo fmt.

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

* Import Document in odt page_parts after dropping glob re-export

* fix: apply_kind_properties binds ContentControl; cell_ref keeps char closures

* fix(engine): pass case helpers by value (clippy needless borrows)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
2026-10-24 03:54:19 -04:00
dotCooCoo 37016564f3 AutoCorrect: words after a no-break space are corrected
AutoCorrect found the start of the word before the caret one byte after the
last white-space character. After a no-break space (two bytes in UTF-8) or an
ideographic space (three bytes) that offset is inside the character, so the
word could not be read and AutoCorrect left it as typed: no replacement, no
sentence capital, no ordinal superscript and no link.

The word now starts after the whole white-space character. A typing test
covers a replacement, a sentence capital and a web address after each of the
two spaces, and an ordinal after each.
2026-10-24 00:32:45 -07:00
skylartaylor fd071202e7 E37: migrate WordCraft to egui 0.37 with guarded IME and capture lifecycle 2026-10-24 08:30:31 +02:00
dotCooCoo 16454c5f30 AutoFormat: *bold* and _italic_ become real formatting as you type
AutoFormat As You Type has a new option, "*Bold* and _italic_ as real
formatting". When it is on and a word is finished by a space,
punctuation or Enter, text enclosed in asterisks becomes bold and text
enclosed in underscores becomes italic, for plain and complex-script
text, and the marks are removed. The opening mark has to start a word
and the enclosed text may not start or end with a space, so 2*3* and
file_name_ stay as typed, and so do marks around a picture, field or
content control.

Undo right after brings the marks back; after Enter, the Enter stays.
The paragraph Enter makes is formatted as after a plain Enter. With
Track Changes on, only marks the current author typed as tracked
insertions are converted, and nothing is converted while formatting is
limited to the document's styles.

The option is off by default. Scripts set it with "boldItalic" on
tools.autocorrect, and the AutoCorrect dialog shows it on the AutoFormat
As You Type tab.
2026-10-23 23:21:54 -07:00
SlavenDjandaiongg 3ed7fe977c Change Case follows each run's language for Turkic casing (#3479) (#3480)
Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
2026-10-23 17:14:34 -04:00
Michael MuandClaude Sonnet 5 02e71f2b33 Compare: don't let a character-level diff scramble a reordered digit run (#3224)
lcs_ops is a textbook LCS, which finds the mathematically longest common
subsequence, not the most readable edit script. On two short strings that
happen to share a few characters in a different order (very common with
dates, IDs, phone numbers), it faithfully threads a chain of single-character
matches through what a reader expects to be one clean replacement — e.g.
"01.09.2026" -> "19.10.2026" came out as an alternating run of tiny
deletions and insertions instead of deleting "01.09" and inserting "19.10".

Add collapse_noise_matches, called only for character-level compares: it
folds a short (length-1) match that sits strictly inside a run of edits back
into the surrounding delete/insert, flattening the region into one clean
delete followed by one clean insert. A match that opens or closes the whole
sequence is the genuine common prefix/suffix lcs_ops already stripped before
running its table, so it's never touched regardless of length, and the fold
is gated on character granularity only — a matched whole word at word-level
compare is essentially never a coincidence and must survive.

Fixes #3094.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-23 12:37:25 -04:00
NetterSilverandClaude Haiku 5.5 1272cebf30 Cap the headless ui_requests queue and keep one Paste Special request (#3997)
Fixes #3641

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-23 11:04:03 -04:00
skylar taylor-barrick 5220199b6c #1327: Keep snapshots with their document through Compare 2026-10-22 21:30:19 -06:00
skylar taylor-barrick 8280903f26 #3277: Test Inspector refusal without protection 2026-10-22 21:19:58 -06:00
Mat Strangeandopencode 706d6cc7ef A comparison clears the version snapshots of the document it replaces
`review.compare` and `review.combine` open the original with `s.doc = doc`,
which bypasses `set_document` — so the version snapshots of the document the
comparison replaces survived it, and a restore could put the previous document
back over the comparison result.

The snapshots go with the document they were taken from, as everywhere else.
The document itself is still assigned directly: `set_document` resets the undo
history, and the comparison's own undo step would go with it.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-22 21:19:58 -06:00
Mat Strangeandopencode 080d2caeb0 The inspector's tracking refusal doesn't cite an unrelated issue
#1344 is about the inspector missing hidden text, not about its removals
going untracked, so the reference only sent a reader to the wrong place.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-22 21:19:58 -06:00
Mat Strangeandopencode f87ab00a61 The Document Inspector refuses its removals while the document tracks changes
The inspector's removals delete straight from the document: the hidden
text, the comments, the revisions and the headers go without ever reaching
the engine's tracked-delete path. On a document that tracks changes — and
`trackedChanges` protection turns tracking on — the content was gone with
no revision to show for it, and the revision trail was broken behind the
user's back.

The removals are not tracked, so they cannot be: rather than untrack them
silently, the inspector refuses the whole removal form while the document
tracks changes, and says why ("the document tracks changes: stop tracking
before removing content"). The check comes before anything is removed, so
the document is left untouched, and reading the document — the inspector
without removals — keeps working. A document that does not track changes
is inspected as before.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-22 21:19:58 -06:00
Mat Strangeandopencode 5151809e11 Merge origin/main into fix/protection-file-commands
Resolves the conflicts with the changes that landed since the branch was cut:

- crates/engine/src/cmd/protect.rs: the forms-mode refusals stay behind
  upstream's `greyed_out()` (so the ribbon still greys the Developer tab's
  control buttons out), and the refusal of the document's properties and its
  privacy options is added on top of it.
- ROADMAP.md, docs/file-format-parity.md, docs/gaps.md: both sides' entries
  are kept; the status lines carry the merge's date and this change at the
  front.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-22 23:08:09 +01:00
aiongg e1e258e4d9 Integration: track t08
contributors/people.toml: union. crates/layout/src/tests.rs: both tracks' tests kept whole. Docs: status lines combined, rows unioned or word-merged.
2026-10-22 01:47:56 +00:00
aiongg ecef6d4ab6 Merge PR #3299: M15: persist the default proofing language across restarts (#2882)
ROADMAP.md: main's status lines; both progress-log rows kept.
docs/gaps.md: main's status line; revision rows unioned.
docs/localization-parity.md: main's status line; revision rows unioned.
2026-10-22 01:21:35 +00:00
aiongg 0af1a197ad Merge PR #3509: fix(engine): preserve existing table cell margins when side is omitted 2026-10-22 01:19:40 +00:00
5a6d2b6070 File › New: personal templates from a user templates folder (#2852) (#3133)
* #2852: personal templates in File › New, Save as Word Template

File › New lists the .dotx/.dotm files in the user templates folder under
Personal (a subfolder per category, search covers them); a click makes an
untitled document attached to the template. Save As › Save as Word Template
starts in that folder (<settings>/Templates, made on first use; File ›
Options › Advanced › File locations › User templates overrides it).
Engine: file.listTemplates (bounded, network folders refused, read root
applies), file.newFromTemplate sets the attached template, file.options
userTemplatesFolder, file.saveTemplate keeps a .dotm name. UI:
ui.saveAsTemplate; Save dialog requests carry a start folder.

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

* #2852: review — a personal template asks about unsaved changes; network templates folder refused before it's looked at; New lists the folder once per visit with Refresh; .dotx/.dotm labels alike

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>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-10-21 20:18:50 -04:00
4913b20259 #1281: English dialects in proofing — en-GB, en-CA, en-AU… flag each other's spellings (#1045) (#2632)
* #1281: English dialects in proofing — en-GB, en-CA, en-AU… flag each other's spellings (#1045)

English was one proofing language: en-GB, en-CA and en-AU all proofed like
US English, and US documents accepted British spellings. Now ProofLang has
three Englishes, picked by the tag's region:

- En (en-US, other regions, no language): British spellings flagged
  (colour → color).
- EnGb (en-GB, en-AU, en-NZ, en-IE, en-ZA, en-IN…): American spellings
  flagged (color → colour, center → centre, analyze → analyse); -ize and
  -ise both right (Oxford spelling).
- EnCa (en-CA): British -our/-re/-ence, American -ize, tire, program.

Data: assets/spelling/en-variants.txt, our own ~280 American/British
pairs with a policy per English (MIT OR Apache-2.0, ATTRIBUTION.md row).
Inflections by rule (crates/proof/src/en_variants.rs). A dialect's form
is right when the shared list knows it or its other spelling, so
"realisation" is right in en-GB. No system dictionary needed.

Suggestions put the dialect's own form first. Review › Language lists
English (Australia) and (Canada); the Language dialog adds India,
Ireland, New Zealand and South Africa (names in every catalog, not
native-reviewed). An English interface on an en-GB/en-CA/en-AU… region
writes new documents in that English. Options › Language shows the
document's language instead of a fixed "English (United States)".

Tests: per-dialect accept/flag/suggest, tag mapping, no-panic proptests,
engine end to end through review.language, DOCX w:lang round trip,
region-based editing language, language menu.

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

* #1281: review — pin the #40 words (judgement, defence, licence, programme, catalogue) in en-GB and en-US

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

* #1281: review — harness picks every proofing language in Review › Language and checks it is ticked

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

* #1281: review — pin more British spellings in en-GB (goitre, sceptre, enrol, fulfil, instalment, skilful, wilful, encyclopaedia…)

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>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-10-21 19:52:57 -04:00
Divyanshu Gupta 084b76d9de fix(engine): preserve existing table cell margins when side is omitted
In table.cellMargins, resolve existing margins from the table, its style, or defaults and only overwrite the sides explicitly provided in the command parameters.

Closes #3490
2026-10-22 00:04:20 +05:30
Mat Strangeandopencode f7f78cadd6 The document's properties and a version restore need the protection stopped
Two gaps the review found, both of them a change to a protected document that
went through without the protection seeing it.

The properties (`file.properties`) and the privacy options
(`file.privacyOptions`) are document-wide state, and they aren't tracked: under
tracked changes they changed the document with no revision to show for it, and
under the other modes they were only rolled back by the post-check, after the
command had run. They are refused now, before the command runs, as the content
control's properties are.

A version restore (`file.versions`) replaces the whole document, including the
content the protection kept read-only, and it did that without the password:
`file.versions` is a pure command, so the protection never saw it. Keeping the
live protection and its password stopped them being dropped, but the content
underneath was still replaced. A restore needs the protection stopped first,
and the password with it when the document has one.

The live protection still wins over the snapshot's stale one, so a restore
can't lock the user out of their own document either.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 22:00:25 +01:00
LQRD ab68111717 M15: persist the default proofing language across restarts (#2882) 2026-10-20 20:07:09 +05: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 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
70fc00c80f Symbols keep their symbol font when the text font changes (#2036) (#2037)
* Symbols: a w:sym character keeps its symbol font when the text font changes (#2036)

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

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

* #2036: review round 3: docx test reads sym_font, paste drops the symbol font, astral symbols stay text

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

* #2036: insert.symbol decides symbol or text per character

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>
2026-10-18 11:33:00 -04:00
8fcaa66e19 Turkish case mapping: i/İ and ı/I capitalise correctly (#1923) (#2009)
* WIP for #1923

* AutoCorrect: Turkish and Azerbaijani case mapping (#1923)

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

* AutoCorrect: Turkic language detection shares proofing's tag normalisation, I to dotless i covered (#1923)

Co-Authored-By: Claude Opus 5.5 (1M context) <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:43:32 -04:00
6311f9d411 AutoCorrect: replace the double-hyphen arrow (#2223) (#2263)
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:22:50 -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
4442390213 DOCX: keep the glossary document (building blocks) (#2631)
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:23:25 -04:00
7af9748ace M1: fields keep w:dirty through saves; computing a result clears it (#877) (#946)
A field begin marker flagged w:dirty by a producer that couldn't compute
its result (pandoc-style TOC placeholders) lost the flag on save, so Word
no longer refreshed the field on open. Mirror w:fldLock end to end:
FieldState, InlineObject::Field/FieldStart, the reader (fldChar and
fldSimple) and the writer (buffered, range and TOC-spanning begins).
Update Field/Update Table clear it on the fields they compute, like
Word's F9; untouched fields keep it. Round-trip and engine regression
tests, incl. a pin for #878 (imported TOC placeholder replaced in place,
fixed earlier by #617).

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 00:27:25 -04:00
Raj Patel 4062db0ac2 Shortcuts: more of Word's default keys (Insert-key clipboard, F2 file keys, field and outline keys) (gap #24) (#1166) 2026-10-14 23:01:17 -04:00
e4e389cdb3 Free part, list and comment ids after a file's largest ones (#1338) (#1428)
New parts, lists and comments took "largest id + 1": after an id of
u32::MAX (a .json model or a .docx can carry one) that overflowed, a
panic in debug builds and a wrap to 0 in release, and list.define's
saturating_add reused the taken id. free_id gives one past the largest,
or the lowest free id once that would overflow; try_add_part,
try_add_list and try_add_levels report ids running out and add nothing,
and the commands that add parts and lists use them.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 19:31:25 -04:00
2c33a66ca9 Word's text watermark in DOCX: drawn on screen and in the PDF, and kept on save (#1290) (#1328)
* #1290: read Word's VML text watermark from DOCX headers

Word's Design › Watermark is a VML shape with a v:textpath in the headers.
The reader dropped it, so screen and PDF showed no watermark. It now maps
it to the document's watermark model (text, font, fill colour,
semitransparent from v:fill opacity, diagonal when turned), which the
renderers already draw. Only Word's watermark shape (PowerPlusWaterMarkObject
id) or text centred on the page both ways counts; other header WordArt,
body WordArt and shapes inside text boxes are left alone. Writing it back
to DOCX is still missing (it was already lost on save).

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

* #1290: write the watermark to DOCX as a VML text shape in the headers

The DOCX writer dropped settings.watermark, so a watermark read from Word
(or set in WordCraft) was lost on save. Every header part now starts its
first paragraph with a VML shape (ECMA-376 Part 4) whose v:textpath carries
the text, font, fill colour, semitransparency (v:fill opacity) and diagonal
turn, centred on the margins behind the text, with the reader's
PowerPlusWaterMarkObject id so it reads back as the watermark, not a shape.
The shape type is our own: a straight path the text follows, fitted to the
box. Slots the pages show with no header of their own or linked from an
earlier section (default; first page with a title page; even pages with
even/odd headers) get a header holding only the watermark; linked sections
stay linked. A blank watermark isn't written. Check Compatibility no longer
warns about watermarks.

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

* #1290: give the watermark's text path a top and a bottom edge so Word draws it

With a single line as the shape type's path, Word loaded the watermark as
WordArt (right text, size and turn) but drew nothing. Checked black-box in
Word: diagonal, horizontal and header-less documents now show it.

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

* #1290: test the watermark through a Word-shaped file, table-only and even-page headers

Read → write → read of a file with Word's watermark keeps the watermark, the
headers' text and paragraphs and one shape per header; header WordArt that
isn't a watermark doesn't become one. A header holding only a table gains no
line; with odd and even pages an even header is added and later sections
stay linked.

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

* DOCX: read VML freeforms (v:shape with a path) as freeform shapes

Legacy VML shapes with their own `path` (ECMA-376 Part 4), such as rule
lines in headers, were dropped on open and lost on save. They are now
read as freeforms: m/t/l/r/c/v/x/e commands in the coordsize/coordorigin
space, nf/ns, v:fill and v:stroke opacity; arcs drawn straight, formula
references read as 0. Saved as DrawingML custom geometry.

Tests: header rule read and round-tripped; hostile paths and coordinate
spaces stay finite and never panic.

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

* DOCX: VML freeforms whose path uses formulas aren't drawn

Formulas (@n, from v:formulas) aren't evaluated; reading them as 0 drew stray lines. Parity row states the fill-opacity limit (none at 0, otherwise opaque, as for DrawingML fills).

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

* DOCX: VML freeforms reuse the watermark's vml_fraction

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

* #1290: move the VML freeform reader out of this PR

The three VML freeform commits (b7a6cc7, d8fa665, 945cbb4) aren't part of
#1290; they go to their own pull request so this one stays on the
watermark.

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-14 18:59:01 -04:00
9f7330664b Word forms keep their legacy form fields and rows' skipped grid columns when saved (#1184)
* Word forms keep their legacy form fields and rows' skipped grid columns when saved (part of #1174)

* Tests: field initializers name the new form field

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:58:07 -04:00
3d8f8144d9 File: recover unsaved work after a crash (#518) (#586)
* M10: retain local crash recovery on current main

* i18n: Backstage Recover in every catalog

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:21:57 -04:00
aedbbc9325 feat(engine): rename documents from the title bar and open account settings from the badge (#1016)
* M10: enable document renaming and account settings

* no-mistakes(review): Allow case-only document renames without overwriting files

* no-mistakes(review): Keep unsaved rename as session file name, not Title

* no-mistakes(review): Keep AutoSave and Recent across renames; Compare clears name

* no-mistakes(document): Document file.rename write-root rule; dedupe rename gap note

* vi: Rename Document strings

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 15:30:52 -04:00
Marco Arnulfo ee33d71173 Focus hides the ribbon, status bar and panes; Escape brings them back (#1017) 2026-10-14 15:09:32 -04:00