Commit Graph
55 Commits
Author SHA1 Message Date
SlavenDj 68bc118a68 Add @SlavenDj to contributors/people.toml (#3048) 2026-10-19 02:43:34 -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
ambroandBrandon Thomas 12428df221 Italian: fixes from a native speaker review (#1717)
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 <echelon@gmail.com>
2026-10-18 11:57:39 -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
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
c9a5191b70 i18n: French interface catalog completed (2,615 entries) with native-speaker review (#2492)
Adds the 402 entries the French catalog lacked: equation gallery (matrices, accents, brackets, integrals, operators, functions), built-in style and table-style names, citation styles, paper sizes, watermarks, and the Bookmark, Go To and Print dialog strings. Drops the obsolete 'Style: APA' entry. Terminology reviewed by a native French speaker and Word user. The Polish superset test exempts fr like the other catalogs carrying newer strings. Progress docs and people.toml updated.

Co-authored-by: Vivien JANNY <vivien@avertissance.fr>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 10:29:17 -04:00
.CooCoo f5b3c5d069 Contributors: dotCooCoo's entry shows the GitHub profile name again (#1982)
The merge of main into #1250 (ea0d27a6) resolved its conflict in
contributors/people.toml without the sync_github_name = true line that
#1250 added to [people.dotCooCoo]. The line is back, so the credits
show the name on the GitHub profile as this entry's display name
(crates/ui-egui/build.rs).
2026-10-18 09:38:11 -04:00
Marco ArnulfoandBrandon Thomas 5e2b63b710 Contributors: add Marco Arnulfo (#2302)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:44:03 -04:00
9acc34f22c i18n(pl): citation style names, audit coverage and attribution (#750) (#1720)
* i18n(pl): citation style names, audit coverage and attribution (#750)

Follow-up to #871.
- pl.tsv: add the 12 citation style names (APA, Chicago, GOST, ISO 690,
  ...) that hu.tsv and bg.tsv already translate.
- tests.rs: Polish also checked against the citation styles; Italian and
  Hungarian may differ only by the obsolete "Style: APA".
- audit.rs: Polish joins the catalogs that must cover every looked-up string.
- ATTRIBUTION.md: credit Aleksander Kutycki (@olekkut) with Gemini 3.8 Flash for pl.tsv.
- contributors/people.toml: add [people.olekkut] entry.

Co-Authored-By: Gemini 3.8 Flash <noreply@google.com>

* i18n(pl): one-line equation gallery pieces (Large, Limit and, Log) so Polish matches Italian

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

---------

Co-authored-by: Gemini 3.8 Flash <noreply@google.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 04:23:54 -04: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
6a2bad8afa Tables: formulas with cell references, functions, number formats and F9 (#907)
* Tables: formulas with cell references, functions, number formats and F9

Table Layout › Data › Formula was a fixed set of string checks: any
unknown function was silently summed, cell references weren't read, and
Update Field never recalculated a formula field.

Formulas are now parsed and evaluated (crates/engine/src/cmd/table_formula.rs):
- cell references and ranges by grid column (=B2+B3, =SUM(B2:C3)),
  + - * / ^, parentheses and the comparisons = <> < <= > >=
- SUM, AVERAGE, COUNT, MAX, MIN, PRODUCT, ABS, INT, SIGN, ROUND, MOD,
  IF (only the branch taken is worked out), AND, OR, NOT, TRUE, FALSE,
  DEFINED
- ABOVE/LEFT/RIGHT/BELOW sum the first run of numbers next to the cell,
  as observed in Word; `=SUM()` is a syntax error, as in Word; a bare
  `=SUM` takes ABOVE or LEFT from the cell
- the \# numeric picture (`#,##0.00`, `0%`, `;` sections)
- an unknown function or a syntax error is an error and inserts nothing;
  input is bounded (formula length, nesting depth)

Update Field (F9) re-evaluates formula fields from the cells they read.

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

* Review fixes for #907: clamp formula ranges to the table, cell budget, depth for ^ and unary +

- A range reads only the rows and grid columns the table has, so SUM(A1:BK32767) over a small
  table no longer walks two million grid positions.
- One evaluation reads at most 100,000 cells; past it the formula fails with "Formula too
  complex" (F9 shows !Formula too complex) instead of hanging on a hostile document.
- Each ^ in a chain and each unary + counts toward the nesting limit of 32, like unary -.
- Regression tests for all three.

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

* M2.1: locale-aware number parsing and formatting in table formulas

* contributors: credit rbr48

* Merge main; rustfmt; one contributor entry for rbr48 (#906 had added one)

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

* Drop a stray merge marker from ROADMAP

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

---------

Co-authored-by: rbr48 <rajbrur48@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 01:31:18 -04:00
Brandon ThomasandClaude Opus 5.5 e127a080a1 CI: drop a duplicate contributor entry in people.toml
#1708 merged with main added [people.finwarman] a second time; the build
then fell back to usernames only.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:14:53 -04:00
109956bb90 Tables: preserve row table border exceptions (#1708)
* Tables: preserve row table border exceptions

* Tests: shorten the row border regression name

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 00:58:59 -04:00
a9d3597549 Tables: Backspace after a nested table stays in its cell (#906)
Backspace at the start of the paragraph after a table nested in a cell
moved the caret into the last cell of the table around it: the search for
the table's last paragraph compared only the first index of the path, which
a nested table shares with its outer table. It now matches the table's
whole path. Regression test: backspace_after_a_nested_table_stays_in_its_cell.

Also adds my contributor entry (rbr48, GitHub profile name).

Co-authored-by: rbr48 <rajbrur48@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 00:17:03 -04:00
1e02093462 i18n: add Croatian (hr) interface translation (#1271)
* i18n: add Croatian (hr) interface translation

Croatian interface catalog (2,505 entries: every key of the reference catalog, built-in style
names, equation gallery, print dialog), registered as `hr` ("Hrvatski"). `hr`, `hr-HR` and
`hr_HR.UTF-8` system locales pick it automatically.

- Count-neutral wording (`Riječi: 21`), since Croatian numerals take three noun forms.
- New `template` catalog context: the "Letter" template can be called "Pismo" while the "Letter"
  paper size keeps its name. Other languages fall back to their plain entry, so nothing changes.
- Locale, saved-preference, catalog-coverage and bundled-font tests; `hr` added to the
  interface-audit languages; Polish's coverage test lists `hr` with it/hu/bg.
- Written from the meaning of the English labels (clean room, no Word localisation consulted),
  proofread by a second model and reviewed by a native speaker.
- ATTRIBUTION row, contributors entry for the author, docs and roadmap.

Croatian spelling and grammar proofing is separate (#1114).

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

* ATTRIBUTION: name the models and the human review for the Croatian catalog

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

* i18n(hr): native-speaker corrections to math terms and messages

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

* i18n(hr): envelope names without their size

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

* docs: Croatian entry count

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 00:09:07 -04:00
3cbea099f1 i18n: add Swedish (sv) interface translation (#1219)
* i18n: add Swedish (sv) interface translation

A 2,504-entry Swedish catalog: every key of the reference catalog, the equation gallery's
labels, everything the interface audit finds, and 44 newer strings no catalog had yet (the
native print settings among them). Written from the meaning of the English labels in ordinary
Swedish computing vocabulary; no other product's localisation and no other catalog was used.
An AI-assisted draft whose terminology and running app were reviewed by a native speaker.
Counts use count-neutral wording ("21 ord").

- sv, sv-SE, sv_SE.UTF-8 and sv-FI select it automatically; "Svenska" in Options and
  ui.language with {"value":"sv"}.
- The interface audit checks Swedish the way it checks Russian, Hungarian, Turkish and
  Bulgarian.
- Tests: locales and saved preference, catalog coverage and key translations, the search
  hint's example, count wording, and the bundled Inter/JetBrains Mono faces covering å ä ö é.
- native_print: the narrow-window test pins English, since it finds controls by their English
  labels (it failed on a Swedish machine once those labels had a translation).
- The Hungarian locale test used sv-SE as an unsupported language; it now uses fi-FI.
- ATTRIBUTION, README, ROADMAP, docs/localization-parity.md and a new
  docs/localization-sv.md; a contributors/people.toml entry for @holmbom.

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

* Swedish rows for the cross-reference dialog

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-15 00:03:13 -04:00
60b9500347 Web: add option to load system installed fonts in dropdown (#1390)
* feat(web): add option to load system installed fonts in dropdown

Add explicit 'Load System Installed Fonts…' button in font dropdowns for web (Wasm).

- Dispatches font.loadSystem via transient user activation on click
- Calls window.queryLocalFonts() to enumerate and load installed fonts into FontDb
- Invalidates font family cache in FontPreviews upon new font installation
- Complete localization across all 18 languages
- Credit GhostMitra in contributors/people.toml

* fix(ui, engine): canvas focus after chrome click and checked file access in sniff

* Revert "fix(ui, engine): canvas focus after chrome click and checked file access in sniff"

This reverts commit b0c925bf79.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:24:19 -04:00
8d04d14d96 fix(formats): preserve leading whitespace in markdown escape_line_start (#1568)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:14:54 -04:00
Wesley LuizandBrandon Thomas 097d81a41b M15.1: print-area strings translated in every interface catalog (#1702)
* M15.1: print-area strings translated in every interface catalog

* Add leoncral to contributors (sync GitHub profile name)

* Docs: progress log for the print-area translations

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 22:00:30 -04:00
13070ca85e Layout: field instructions do not add blank paragraphs (#1665)
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 21:59:41 -04:00
Fin WarmanandBrandon Thomas faa621104c Layout: empty line breaks do not move floating tables (#1658)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 21:22:19 -04:00
Fin WarmanandBrandon Thomas 2c26fa58a8 Draw arrowheads on open freeform paths (#1645)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 21:04:33 -04:00
a3ec498f2a Italian proofing: po', qual è, È and un/un' grammar rules; more elisions in spelling (#1585)
* Italian proofing: po', qual è, È and un/un' grammar rules; more elisions in spelling

- grammar (works without a dictionary): un pò / un po -> un po'; qual'è -> qual è;
  E' -> È; un'altro -> un altro and un altra -> un'altra (also nessun, ciascun,
  qualcun, alcun), gender from the ending with masculine -ista/-ma/-cida... left alone;
  more emphatic doubles allowed (pian piano, poco poco, così così...)
- spelling: more elisions (dov'è, cos'è, anch'io, senz'altro, nessun'altra, n'ha...),
  truncations po'/mo'/be' accepted, pò and qual'è left to grammar (no double marks)
- tests: rule tests, grammar proptests, Italian suggestion proptest, It in mixed-span proptest

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

* Contributors: credit lupant by their GitHub profile name

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 19:50:35 -04:00
e9e8af517c Layout: a table's percentage width past 100% is kept (#1598)
* Layout: a table's percentage width past 100% is kept

Word keeps a preferred width over 100% of the text column (w:tblW
w:type="pct" above 5000). A cover-page footer table at 131% with the
left margin as a negative indent spans the page edge to edge in Word;
WordCraft capped it at 100%, so it ended three-quarters of the way across.

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

* Contributors: add Fin Warman

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 19:38:18 -04:00
81b5602bf0 insert.picture and insert.icon leave the caret after the picture (#965) (#1437)
* contributors: add Nael-Nathanael

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

* insert.picture and insert.icon leave the caret after the picture (#965)

A script that typed right after inserting a picture replaced it, so the
picture was missing from the saved file. Commands now leave the caret
after the new picture; Picture Format commands still act on it. The app
still selects a picture the user inserts, as in Word.

Fixes #965

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

* Select a pasted picture again, keep pictures inserted in a row (#965)

Desktop paste inserts through execute, so after the caret change it
left a pasted picture unselected. WordApp::select_inserted_picture
selects the picture just before the caret; run and the paste path call
it, paste once after the last picture so a multi-file paste keeps them
all. Tests for paste selection and for two inserts in a row.

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 19:31:55 -04:00
.CooCooandBrandon Thomas 00c8ee3407 Fonts: the interface CJK fallback skips faces without outlines (#1250)
* Fonts: the interface CJK fallback skips faces without outlines

The interface CJK fallback has listed PingFangUI.ttc from
FontServices.framework as its last macOS candidate since #495. The skrifa
crate reads glyph outlines only from the glyf, CFF, CFF2 and VARC tables.
On macOS 27.0.1 the 32 faces in PingFangUI.ttc have none of them and keep
their outlines in Apple's hvgl table. pick_face counts coverage from each
face's cmap alone. The file's first face maps all 1,114 CJK characters of
the zh-hans and zh-hant catalogs. When no earlier candidate maps all of
them, find_ui_font returns that face for a Chinese interface built without
craft-fonts, and the interface would draw the characters it takes from
that face as blank space.

pick_face now skips a face with none of those four tables, and find_ui_font
goes on to the next candidate. VectorCraft's font scan has the same check
(storytold/vectorcraft#781).

* Contributors: add dotCooCoo (Robert Lee)

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 18:01:13 -04:00
8ae23cee29 Desktop: a maximized window no longer flashes on and off at start-up on Windows (#23) (#1194)
* Desktop: a maximized window no longer flashes on and off at start-up on Windows (#23)

winit shows a window the moment it creates it maximized, even though eframe starts it hidden
until the first frame. A window saved as maximized therefore appeared and disappeared about four
times in a third of a second before settling. On Windows it now opens at its normal size, still
hidden, and the first frame maximizes it (WindowGeometry::maximize_after_open). Other platforms
are unchanged. Measured by polling the window's visibility every 15 ms: before, show/hide four
times; after, hidden until the first frame, shown once.

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

* Contributors: add my display name

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 17:59:04 -04:00
Gert Pellin - GPEandBrandon Thomas 1e4ed377d8 README: install on Gentoo from the ::snakebyte overlay (#16)
* README: install on Gentoo from the ::snakebyte overlay

* Contributors: credit switch87 as Gert Pellin

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 16:55:59 -04:00
Sazid Al BayazidandBrandon Thomas 3962d397e5 Contributors: add Sazid Al Bayazid (albayazid) to people.toml (#1074)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 16:24:28 -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
90762b0529 i18n(cs): wording fixes after #387 (#973)
* i18n(cs): wording fixes after #387

SmartArt graphic and layout terms consistent with the rest of the
catalog, inline equation, plural-safe print count, grammar terminology
(mluvnice), tab stop type, file-not-found gender, review author label.
Also adds the [people.Losminek] contributor entry.

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

* i18n(cs): shorter Proofing label

"Kontrola pravopisu a mluvnice" overflows the fixed 170 px Options
pane list into the content. "Kontrola pravopisu" fits and also reads
well as the Review ribbon group name.

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

---------

Co-authored-by: Losminek <301412321+Losminek@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 15:30:20 -04:00
93175f979f Word 365 cover pages: page-relative positions and freeforms drawn back to their start (#743) (#1487)
- A position wrapped in mc:AlternateContent inside wp:anchor (wp14:pctPosVOffset)
  takes the Fallback's wp:posOffset; the shapes were placed at the top of the column.
- A custGeom path that ends where it started without a:close is closed, so it's
  filled as in Word; the cover's panel (and its white title) was invisible.
- Credits: add benandez to contributors/people.toml.

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

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 13:43:24 -04:00
cyril265andBrandon Thomas 1be06ca323 Fields: PAGEREF in headers and footers shows its bookmark's page (#1477)
* Fields: PAGEREF in headers and footers shows its bookmark's page

Headers and footers are laid out once the body is paginated, so a page
reference there now shows the number of the page its bookmark starts on, in
that page's number format, instead of the result stored in the file. Like the
page totals in headers and footers, it is filled in after pagination and
doesn't make the body lay out again. Mail-merged letters use this for
"Page X of Y" per letter (Y = PAGEREF to a bookmark at the letter's end). Page
references in the body keep their result until fields are updated.

* Contributors: add cyril265

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 13:42:53 -04:00
1059b196c1 i18n: add Vietnamese (vi) interface translation (#1458)
* i18n: add Vietnamese (vi) interface translation (#1434)

* vi: translate the strings main gained since the catalog was drafted

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 13:19:39 -04:00
Dante Souza e SouzaandBrandon Thomas 196f828f3f Expand sample-text formulas on Enter (#1067) (#1101)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 01:55:30 -04:00
Jason Brashear 4fe9891572 LaTeX: a "[" after a line break or at the start of a list item stays text (#1099)
* Contributors: add Jason Brashear to people.toml

* LaTeX: a "[" after a line break or at the start of a list item stays text
2026-10-14 01:53:05 -04:00
NetterSilverandBrandon Thomas 159b84f54c Track Changes: Accept All no longer joins an extra paragraph after a tracked delete from a paragraph end (#864) (#914)
* Track Changes: emptying a paragraph no longer turns its mark into the text's revision (#864)

Paragraph::delete copies the formatting of what it deletes onto the paragraph
mark when it empties the paragraph. That copy included the revision ids, so
accepting a tracked deletion of a whole paragraph text made the mark a deleted
mark too, and Accept All joined the following paragraph. Rejecting typed text
in an empty paragraph likewise rejected the paragraph mark. The mark now keeps
its own ins, del and fmt_change.

* Credit Silvertree2010 in the About window

* Docs: update the progress docs for this change

* Docs: Last reviewed stays as it was

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 12:08:57 -04:00
284818b467 Tables: the right-click menu offers the table's commands (#694)
* Tables: select, insert and move with the mouse; double-click a border to AutoFit

Like Word, on top of the border resizing:

- Selection areas: just above a column the pointer becomes a down arrow,
  left of a row the row arrow, along a cell's left edge (inside it) the
  cell arrow. A click selects the column, row or cell; a drag selects every
  one up to the pointer. Selected cells are shaded as cells, empty ones too.
- Insert buttons: left of a row border and above a column border a "+"
  shows with the border marked; a click adds a row or column there and puts
  the caret in it.
- Move handle past the table's top-left corner: a click selects the table;
  a drag shows a dashed outline and, on release, the table floats where it
  is dropped with the text wrapping around it (Word's behaviour). One undo
  step. Nested tables and tables outside the main text are only selected.
- A double-click on a column border fits the column left of it to its
  contents (the right neighbour gives or takes the room; on the right edge
  the table's width changes, up to the right margin).
- Table Layout > Select > Select Column (catalog 418/449).

Whole-cell selection: a column (or a rectangle of cells over several rows)
is not one run of the text, so selecting it makes a block of its cells'
paragraphs (`CellRange` on the existing `ColumnBlock`). Bold, alignment,
Delete, typing, copy and numbering act on those cells only (a numbered
column is one list); table commands (shading, merge, delete columns) take
the rectangle; Tab moves on without clearing it; Undo brings the selection
back. Cells of one row, or whole rows, stay an ordinary stream selection.
The format painter no longer clears a block's text (edit.pasteFormat was
handled like a paste). The mini toolbar stays hidden over whole-cell
selections and during these drags: pinned above the selection it covered
the column areas, the "+" buttons and the move handle.

Engine: crates/engine/src/cmd/table_mouse.rs - table.selectAt,
table.insertAt, table.moveDrag, table.fitColumn (page coordinates, so
agents and the control channel can do the same) and table.selectColumn;
hit testing reuses table_resize's drawn geometry, and borders keep priority
for resizing. 10 engine tests (areas, a column edited cell by cell,
numbering, rows/cells/drags, insert buttons, move, fit, format painter,
hostile params).
UI: crates/ui-egui/src/table_mouse.rs, wired into the existing canvas mouse
handler (no extra ui.interact areas, so clicks in a cell's text still place
the caret); kittest tests with real pointer events: column click, row drag,
"+" button, move-handle drag in one undo step, double-click fit.
New labels translated in all eleven catalogs. Progress docs updated: gaps,
ui-parity, target-app-parity, ROADMAP, parity checklist.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).

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

* Tables: a selected column's word count is its cells'; merged cells shade once

With whole cells selected (a column, or a rectangle that isn't one run of
the text) the status bar said "12 of 23 words": `Session::selected_text`
took everything between the selection's ends. It now takes each piece's
own text, a line each, for a column block too, so the status bar, Word
Count and the other callers see what is selected.

Since #669 a vertically merged cell is one box as tall as its rows, with a
`covered` box for each continuation row; shading selected cells skips
those, so a merged cell isn't shaded twice. `selected_paths` follows
main's `para_paths_in` (#418): a selection ending at the start of the next
paragraph doesn't take it.

- Test: a selected column's text is its three cells' and Word Count says
  three words.

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

* Tables: double-click AutoFit and the "+" buttons work as in Word

Checked against Word (2019, Windows):

- A double-click on a column border fits the column left of it to its
  contents and the columns right of it keep their widths, moving with the
  border, so the table narrows or widens (up to the right margin). The
  right neighbour used to give or take the difference.
- A "+" insert button selects the new row or column (the caret used to
  go into it); typing then goes into its first cell. If the table can't
  take another row or column, nothing changes.
- A double-click along a cell's left edge (the cell selection area)
  selects its row.

- Tests: engine (fit keeps the columns on the right; insert selects the
  new row or column, typing lands in its first cell) and kittest (real
  double-click on a border; "+" selects the row; double-click by a cell's
  edge selects the row).

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

* Tables: the right-click menu offers the table's commands

Right-clicking a table cell now offers what Word's table menu does, all
through existing commands: Insert (rows above/below, columns left/right),
Delete (cells, rows, columns, table), Select (cell, column, row, table),
Merge Cells, Split Cells, Distribute Rows, Distribute Columns, AutoFit
(contents, window, fixed column width), Text Direction, Cell Alignment
(the nine alignments) and Table Properties… (opens the dialog, as the
ribbon's button does).

A right-click beside selected cells now moves the caret there, as in
Word: with a column selected, the cells between its first and last cell
in the text (the other columns of those rows) counted as "inside the
selection", so the selection stayed and the menu acted on the column
instead of the cell clicked. A right-click on a selected cell keeps the
selection, so Cell Alignment, Distribute and the rest act on it.

- UI only (crates/ui-egui/src/canvas.rs): the table part of
  `context_menu()` and a small check before the right-click caret logic
  (separate from it, so it doesn't overlap #521's change to that block).
  Table Layout's nine cell alignments are one shared list (`CELL_ALIGN`
  in ribbon.rs) for the ribbon grid and the menu.
- Two new labels, "Cell Alignment" and "Table Properties…", translated
  in all eleven catalogs; the rest reuse the ribbon's labels.
- Tests: kittest with real pointer events: right-click in a cell, the
  menu's table entries, Select > Select Column; with a column selected,
  a right-click on it keeps it and Cell Alignment > Align Bottom Right
  aligns just the column (one undo step), and a right-click beside it
  drops it.

Progress docs updated: ui-parity (context menus 55% -> 60%), gaps,
target-app-parity, ROADMAP. No new commands, so the catalog is unchanged.
`cargo xtask ci` passes.

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

* Tables: the right-click menu's table items follow the selection, as in Word

Checked against Word (2019, Windows): Word's table items in the context
menu depend on what is selected, and there is no Select or Cell
Alignment submenu among them.

- Caret in a cell: Insert, Delete Cells, Split Cells, Text Direction,
  Table Properties…
- Several cells: Insert, Delete Cells, Merge Cells, Text Direction,
  Table Properties…
- Whole columns: Insert, Delete Columns, Merge Cells, Distribute Rows,
  Distribute Columns, Text Direction, Table Properties… (whole rows the
  same with Delete Rows; not checked in Word).
- The whole table: Insert, Delete Table, Distribute Rows and Columns,
  AutoFit, Text Direction, Insert Caption…, Table Properties…

Insert lists the columns first, as Word does. Select and Cell Alignment
are gone, and with them the shared CELL_ALIGN constant in the ribbon;
"Insert Caption…" replaces "Cell Alignment" in all fifteen catalogs.
Word's Border Styles and Insert Cells… aren't here yet.

- Tests (kittest, real right-clicks): the menu with the caret in a cell
  and with the whole table; a selected column keeps its column menu and
  Text Direction turns only it; beside it the menu is a cell's again.

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

---------

Co-authored-by: rabis <340973947+rabis7575@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 10:29:45 -04:00
1677c2d9cf Ribbon groups fit above their labels (View › Show, Developer › Controls, Layout › Paragraph) (#910)
View › Show stacked four check boxes in a column made for three: since
Hide White Space (#645) joined Ruler, Gridlines and Navigation Pane, the
fourth row was drawn over the "Show" group label. It now sits in a second
column.

A new test draws every tab in every interface language and fails on any
group taller than the ribbon's content area (CONTENT_H, 66 pt). It found:

- Developer › Controls, 75 pt: 3 pt after each row of small buttons. The
  rows now fill the height with no gaps, like every other stack.
- Layout › Paragraph (#654), which panicked in debug builds: `add_space`
  between grid cells trips egui's "add_space makes no sense in a grid
  layout" assertion. The 8 pt gap before the Spacing column now sits
  inside those cells, so it looks the same. The group was also 68.5 pt:
  1.5 pt between rows instead of 3 fits it.

- widgets.rs: under cfg(test), group_titled records any group whose
  content is taller than CONTENT_H.
- ribbon.rs: ribbon_groups_fit_above_their_labels.
- ROADMAP progress log and docs/ui-parity.md revision history.
- contributors/people.toml: my own entry.

Co-authored-by: Manuel Carmona <328347120+manucarcor@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 09:27:44 -04:00
0acf6bd512 fix(windows): start 32-bit builds on OpenGL so launch no longer exits (#839)
* fix(windows): start 32-bit builds on OpenGL so launch no longer exits

DirectX 12 graphics pipeline creation fails on 32-bit Windows, so the app quit before the window appeared.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add @odedigo to the contributor names list

The About window credits this username; no real name or display name was chosen.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 09:20:53 -04:00
691e887004 i18n: complete Turkish interface catalog with 2,170 entries and full … (#774)
* i18n: complete Turkish interface catalog with 2,253 entries and full parity

* Review fixes for #774: Turkish entry count after merging main (2,325)

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-12 07:04:50 -04:00
59b04d7644 web: use File System Access API showSaveFilePicker with download fallback (#728)
* web: use File System Access API showSaveFilePicker with download fallback

* Review fixes for #728: mark the document saved only after the picker writes the file

A cancelled or failed save in the browser's save picker marks the document unsaved again, and
the beforeunload guard keeps warning while a picker save is in flight. The picker's file types use
our own neutral format names.

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-12 05:11:28 -04:00
199ac88a2e i18n: add Czech (cs) interface translation (#387)
* i18n: add Czech (cs) interface translation

Translate the 1,246-entry interface catalog clean-room from the English source labels (cross-referenced with PdfCraft, VectorCraft, and PhotoCraft Czech catalogs) and register Czech (Čeština) for system locale detection and persisted selection. Add tests for Czech locale resolution, full catalog key parity, count-neutral word count formatting, and bundled UI font glyph coverage for Czech diacritics.

# Conflicts:
#	ROADMAP.md

* Czech: translate interface strings added on main since this PR

Adds 221 Czech entries so cs.tsv again has the full zh-hans key set
(2,021 entries) and drops the stale "Wildcards (regex)" row; updates the
documented entry count.

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>
Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
2026-10-12 04:09:20 -04:00
cbd9291909 Caret: glide and fade like Word for Windows; File › Options › Advanced › Animation turns it off (#570)
* Add arcanii to the contributor names

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

* Caret: glide and fade like Word for Windows; File › Options › Animation turns it off

The caret no longer jumps and switches on and off. A key or typing glides
it to its new place in 100 ms along cubic-bezier(0, 0, 0, 1), the same for
one character and for a whole line; a second key before it arrives makes
it snap to where it was heading and set off from there. Clicks, and moves
made by scripts or the control channel, don't glide. The blink is a pair
of 100 ms fades (530 ms on, 530 ms off), and a key or a click turns the
caret on from however visible it was.

These are Word for Windows' numbers (16.0.20326, 100% zoom, 144 Hz),
measured black-box: a synthetic document in a separate Word instance, its
window captured frame by frame while key and mouse messages moved the
caret. Fourteen consecutive frames match the curve to 0.003. Word for Mac
was not measured.

Nothing glides when the view changed (zoom, a resized window, scrolling,
another document), when the caret wasn't drawn the frame before, or when
a move starts or ends off screen.

File › Options › Animation › "Animate the caret (it glides and fades)"
turns it off: ui.caretAnimation {"value"} for agents, kept in ui.json, on
by default, translated in the nine catalogs. Off, and whenever egui's
animations are off (the headless harness, so ui_shot and scripted
screenshots), the caret jumps and blinks exactly as before.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 02:15:02 -04:00
Eduardo SoaresandBrandon Thomas 833d0b00c2 #755: Line Numbers and Hyphenation measure in the Options unit (#795)
File > Options > Advanced > Measurement units (#485) missed these two dialogs:
the Line Numbers "From text" distance and the Hyphenation zone read and
sent inches whatever the preference was. They now convert through
options::unit() like every other dialog; params still send points.

Reported by @Ledity.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 01:32:44 -04:00
Debarghaya MitraandBrandon Thomas 9a58b5ba33 Web: add wordcraft.ico as favicon via Trunk (#717)
Registers assets/app-icon/wordcraft.ico in apps/wordcraft-web/index.html
using Trunk's rel=icon asset pipeline.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 01:04:33 -04:00
DevCoderMaxandBrandon Thomas 0652e7f72f Pictures in headers/footers can be moved, resized and rotated (#591)
* Pictures in headers/footers can be moved, resized and rotated

Hit testing (layout.object_at_in), arrange.bounds/position/nudge/group and the canvas selection, drag, resize and rotation handles used to be body-only, so pictures in headers and footers could not be moved (#82). They now work within their own header/footer story: drag to move, handles to resize, rotation handle to turn, arrows to nudge. Regression test: header_pictures_can_be_resized_and_dragged.

* Credit DevCoderMax (Gleyson do Nascimento Carvalho) in About

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 22:27:44 -04:00
alexanderalberandBrandon Thomas aaaf68c6c7 Contributors: add my own entry (alexanderalber) (#566)
generated with Claude Code

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 22:09:49 -04:00
afb4720644 Add Estonian interface translation (#294)
* I18N.et: add Estonian interface translation

* I18N.et: record the verified headless fork push step

* i18n: send interface text that skipped translation through the catalog

A test-only pseudo-language wraps every catalog lookup in ⟦…⟧ marks; a new
audit test lays the app out in it (every tab, File page, dialog and pane,
the right-click menu, and a hover and a click on every ribbon control,
including the Table and Equation tabs) and fails on any visible text
outside the marks. The document is drawn as an image, so only interface
text is checked; document text, font and style names, shortcuts, icon
lettering, contributor handles and command error messages are allowed.

What it found, now translated in every language that has the strings:
- Right-click menu items (Cut, Paste, Font…, Paragraph…, Link…, New Comment).
- Paragraph dialog: the alignment choices showed code values (`left`);
  the label now sits before the dropdown.
- Table Design › Table Style Options checkboxes.
- Table Layout › Alignment tooltips showed code values (`topLeft`).
- Design › Fonts: the theme's name is translated, its font names are not.
- `pt` units, the Insert › Table grid label, the default watermark text,
  the font dialog's preview sample, and the untitled document's name.
- Contributors and Models dialogs: headings, sort and name choices, summary.

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

* Draw Table and Eraser: draw tables and split or merge cells with a pen (#303)

Insert › Tables › Draw Table and Table Layout › Draw › Draw Table / Eraser.
table.draw takes a pen stroke in page points: a rectangle outside a table
inserts a one-cell table of that size at the paragraph under its start; a
straight vertical or horizontal line inside a table splits the cells it
crosses (new grid column or row; untouched cells span it). table.eraser
merges the two cells beside the border nearest a point. Each stroke is one
undo step. In the app the buttons toggle pen and eraser modes with a live
preview, a crosshair with the tool's icon, and Esc to stop; canvas clicks
don't move the caret while a tool is on. Splitting through part of a
vertically merged cell and erasing beside one are refused with a clear
error. Catalog 384/430 -> 387/430.

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

* Draw tab: pen, pencil and highlighter ink, stroke eraser and Hide Ink (#307)

Ink strokes are floating freeform shapes: a new Freeform geometry (paths in
shape-local coordinates, opacity, and the pen that drew it) on
InlineObject::Shape with ShapeKind::Freeform. draw.stroke inserts one
anchored to the paragraph under the first point (one undo step; points
capped per stroke, strokes capped per page, NaN dropped); draw.erase /
draw.eraser with a point deletes the stroke under it; draw.pen, draw.pencil,
draw.highlighter, draw.eraser and draw.select set the Draw tab's tool in the
session view state; review.hideInk toggles showing ink on screen.

The canvas draws a live stroke while a pen is dragged and commits it on
release; the eraser deletes strokes it passes over (one undo step per drag);
Esc or Select goes back to selecting. Draw tab buttons show the tool in use,
with colour and thickness menus; Review gets an Ink group with Hide Ink.
Ink renders with round caps and joins on screen and in PDF; highlighters are
translucent and behind the text. DOCX writes freeforms as wps:wsp with
a:custGeom (moveTo/lnTo) in a wp:anchor and reads them back, along with
simple custom-geometry freeforms from other files.

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

* #315 review fixes: eraser undo test, sanitized tap dots, custGeom path cap, ink docs

- draw.eraser with a point erases through draw.erase: test that it is one
  undo step (undo/redo) and marks the document dirty.
- Render and PDF: a single-point stroke's dot is drawn at the sanitized
  point, not the raw first one.
- DOCX custGeom reading stops once MAX_PATHS paths (subpaths included) are
  collected, across a:path elements.
- docs/file-format-parity.md: ink is saved as custom geometry, not InkML, so
  Word shows it as a freeform.
- widgets test: theme::apply takes an Appearance (#317).

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

* #313: catalog rows for the newly translated interface text

Rows in all eight catalogs for the strings #313 now sends through the
catalog (cell alignment tooltips, credits columns and summary, theme names,
context-menu items, table picker, pt suffix, Document1, the Font dialog
sample) and for the Table Style preview's sample table, which the
untranslated-text audit flagged after main's table style editor landed.
The audit runs headless (no host services: no file pickers or speech) in
about 20 s, so it stays enabled.

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

* fmt

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

* i18n: Borders and Shading line widths through the catalog ({n} pt)

#313's untranslated-text audit flagged main's new dialog (#327).

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

* land-294: Estonian rows for main's newer dialogs

Define New Multilevel List and Track Changes Options (#342), rotation (#332),
Tabs and Borders and Shading (#327), Columns, Symbol and Field (#335): et
keys equal es again.

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

* i18n audit: run on demand (#[ignore]) until its rare failure under load is understood

It failed once in ~46 runs on a heavily loaded machine and passed 35 straight reruns,
so CI shouldn't depend on it yet. Run it with --ignored.

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: vangel76 <vangel1976@googlemail.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 19:06:45 -04:00
7b8c38fa62 Contributors: add my own entry (dkhlapov) (#238)
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:26:53 -04:00