Commit Graph
42 Commits
Author SHA1 Message Date
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
72467d037b Keep the selection's font when typing over a backward selection (#2911)
Fixes #2879

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

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

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

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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 10:23:43 -04:00
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
0254f3afc7 Hebrew: field results like STYLEREF draw right to left; smart quotes match Word (#1670) (#2007)
* Field results (STYLEREF, REF, PAGE text, note marks) shown as one atomic
cluster now place their inner clusters in UAX #9 visual order, in the
direction of the text around the field, so "פרק א" in a header no longer
draws reversed. Smart quotes already matched Word's logical-order result
in Hebrew (” in acronyms, not gershayim); pinned with a typing test.

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

* #1670: fix round 1: field results shaped per bidi run, so brackets mirror correctly

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:42:54 -04:00
da8b7b7feb Tables: Backspace removes a selected table, rows or columns; Delete clears them (part of #1844) (#2005)
* WIP for #1844

* Tables: Backspace removes a selected table, rows or columns; Delete clears them (#1844)

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

* Tables: delete_range edge-case tests, single-cell Backspace clears, deep tables kept (#1844)

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:42:35 -04:00
c9f3da2fc5 DOCX: mixed Hebrew/Latin keeps its order in Word (runs split by direction, w:rtl and language) (#1543) (#1950)
* WIP for #1543

* DOCX: split mixed-direction runs and mark right-to-left pieces w:rtl so Word orders them as WordCraft does (#1543)

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

* DOCX: right-to-left pieces of split runs say their language (w:lang w:bidi) (#1543)

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

* DOCX: no phantom tracked formatting on split runs; typed text doesn't inherit run direction (#1543)

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

* Engine: text typed or inserted anywhere takes its direction from its own letters, not the caret's run (#1543)

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:41:59 -04:00
82bf806118 Web: input-method rewrites erase before retyping, so non-Latin typing no longer repeats (part of #1480) (#1949)
* WIP for #1480

* Web: an input method's DeleteSurrounding erases around the caret, so non-Latin typing no longer repeats (#1480)

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

* Web: input-method rewrites keep formatting and the typing undo group; AutoCorrect resets the web text field (#1480)

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:41:39 -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
e7f5d32c35 Numbered headings: heading-linked multilevel list presets keep the heading styles (#1793) (#2239)
* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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

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

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

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

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:22:12 -04:00
1d37043128 AutoCorrect: reversing a capitalisation adds an automatic exception, as in Word (#1857) (#2227)
* Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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

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

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

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:21:42 -04:00
f354e7fc9b fix(engine): support nested quotations and opening delimiters in smart quotes (#2176)
* fix(engine): support nested quotations and opening delimiters in smart quotes

* docs: typing-parity line for smart quotes after guillemets, low quotes and nesting

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-18 04:02:37 -04:00
Marco ArnulfoandBrandon Thomas cdb13144b3 Lists: Enter on an empty nested item numbered by its paragraph style moves it up a level (#2143) (#2151)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:01:19 -04:00
Marco ArnulfoandBrandon Thomas 7bd3eb8ca7 Backspace and Delete remove a section break, imported or not (#2133)
* Backspace and Delete remove a section break, imported or not (fixes #2127)

* Delete removes a section break on the last paragraph too (#2127)

A continuous break on the mark of the document's last paragraph, with the
last section holding no paragraph (as in the file attached to #2127), had
nothing for Delete to join, so it stayed. Delete at the end now removes it.
The text takes the last section's setup, as in Word, and the headers and
footers that section showed by linking to the removed one stay, here and
when the break goes by joining two paragraphs. Undo brings it back.

* Deleting over several section breaks keeps the headers they linked to (#2127)

A range across two or more breaks dropped the paragraphs in between with
their sections, so only the first section's headers and footers were carried
over. The joined section now takes those it leaves out from every removed
section, nearest first, as "link to previous" resolved them before the delete.
The typing-parity row says that Delete with Track Changes on still leaves a
break on the last paragraph alone.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 04:00:49 -04:00
4d8204c2f2 #2634: a table inserted in right-to-left text is right to left, so Tab moves left (#2688)
Tab already follows reading order (next logical cell) and right-to-left
tables (w:bidiVisual) already lay the first cell out at the right. The
bug was that insert.table ignored the caret paragraph's direction, so a
table made while writing RTL text was a left-to-right table and Tab went
rightwards. Like Word, insert.table now makes a right-to-left table when
the caret's paragraph reads right to left.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 03:43:47 -04:00
fd0bb2fb1e #954: macOS ⌘←/⌘→ go to the line's start and end (#972)
On macOS, Cmd+Left/Right were bound to Word Left/Right (same as Option+arrows).
Bind them to Line Start/End there, as in Word for Mac and every Mac text field;
Option+arrows still move by word. Windows/Linux keys are unchanged.

Co-authored-by: justinjohn0306 <babyjjbabyjj549@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 01:35:42 -04:00
2c7629fa7a Shift+Delete cuts the selection on macOS and Linux too (#1669) (#1676)
Word for Windows (checked by hand): Shift+Backspace erases like
Backspace, Shift+Delete cuts the selection, and Shift+Delete with nothing
selected does nothing. On Windows egui-winit already turns Shift+Delete
into a Cut event, so WordCraft matched. On macOS and Linux it arrives as
the Delete key with Shift, no shortcut was bound to it, and it did
nothing, even with text selected.

The canvas now runs Cut for Shift+Delete when there is a selection,
through the same clipboard path as Mod+X, and does nothing otherwise.
Shift+Backspace already erased (#707).

Tests: Shift+Backspace erases, Shift+Delete without a selection changes
nothing, Shift+Delete on a selection cuts it to the clipboard (fails
without the change); the key check accepts Shift alone.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 22:00:11 -04:00
Marco ArnulfoandBrandon Thomas 70a4917973 Tables: Select All then Delete removes a table the document starts with; right-click Delete offers Delete Table again (#1202)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 17:59:17 -04:00
8196624e83 Caret: Down and Up from a line start beside a table go into it, not past it (#1021)
Down from the start of the line above a table (or Up from the start of the
paragraph after it) skipped the whole table: `DocLayout::vertical` took the
nearest line covering the caret's x, and a cell's lines start a cell margin in
from the table's edge, so the paragraph beyond the table won. Lines between
the caret's paragraph and that line in document order now come first: the
caret goes into the nearest such line's table row, to the cell under it or the
horizontally nearest. Another cell of the caret's own row never counts, so Down
in a table still goes to the cell below. Pinned in `tests_typing.rs` and
`docs/typing-parity.md`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 15:31:22 -04:00
987c41364d Tables: AutoFit in a nested table, whole cells shaded for a selection through a table, Shift+Right at a cell's end (#1004)
* Tables: AutoFit in a nested table fits within its cell (#971)

A double-click on a nested table's column border let the table widen up to
the page's right margin, far past the cell around it. It now widens only up
to the right edge of that cell's text (a right-to-left table's start margin
on the right); the columns right of the border still keep their widths.

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

* Tables: a selection through a table shades its rows' whole cells (#971)

A selection from text into or across a table takes whole rows (#722), but
only the letters in the cells were highlighted. Every row it takes from start
to end now shades all its cells (an empty one too), as selected cells do, and
the letters in them aren't shaded twice (`table_mouse::selection_rects`, used
by the canvas). A selection that starts and ends in one table keeps its text
highlight, so selecting all the text of a cell doesn't look like selecting the
cell; a nested table in a shaded row shades with it.

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

* Typing: Shift+Right at a cell's end selects the cell first (#971)

Shift+Right out of the cell the selection is in selected that cell and the
next one at once. As in Word, the first step out of the cell now takes the
cell alone (its end-of-cell mark) and the next press goes on into the next
cell; in an empty cell the first press selects it. Shift+Left in
right-to-left text, the forward step there, does the same. Pinned in
`tests_typing.rs` and `docs/typing-parity.md`; progress docs for all three
table fixes of #971.

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 15:08:57 -04:00
37d41150e6 Track Changes and content controls: table delete, Change Case and Unlink Fields respect them (#1329) (#1514)
* #1329: WIP

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

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

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

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 14:07:47 -04:00
Marco ArnulfoandBrandon Thomas 7d3767ba99 Zotero: text typed just after a citation goes outside its field (#1162)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-14 12:51:00 -04:00
086d3d0384 Alt+numpad character codes (#961) (#1146)
* #961: Alt+numpad decimal character codes (ANSI, OEM 437/850, Unicode)

Hold Alt, type a decimal code, release Alt: the character is typed at the
caret through the new text.typeCode command. A leading 0 means
Windows-1252, no leading 0 the OEM code page (437, or 850 for Western
European locales), above 255 the Unicode character. Not on macOS.

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

* #961: Alt codes follow the system code pages; ignore auto-repeat, drop on blur

text.typeCode takes optional ansi (874, 1250-1258) and oem (437, 737, 850,
852, 857, 866) code pages; OEM 852/866/737/857 tables added from the public
mapping files. On Windows the shell passes the system locale's pages and, for
codes up to 255, lets the character Windows composes replace ours (above 255
ours stays and Windows' byte-reduced text isn't typed). Auto-repeat of a held
digit adds nothing, and losing focus drops a half-typed code.

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-14 12:49:57 -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
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
c9079196db AutoCorrect: the English replacement list applies only to English text (#835) (#846)
* #835: AutoCorrect's English replacement list applies only to English text

Built-in word entries (teh, i, dont…) are replaced only when the word's
language (run w:lang, style, document default) is English or unset;
symbols, fractions and user entries apply in every language. Polish,
Croatian, Bosnian and Serbian "i" ("and") is no longer turned into "I".

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

* #835 review: untagged text follows the editing language; English as proofing decides it

- Text with no language at all (a .docx without w:lang) is in the editing language, so with a
  Polish interface `Ala i kot ` keeps its `i` there too.
- `wordcraft_proof::ProofLang::from_tag` decides what is English (it also takes `eng`), so
  AutoCorrect and proofing agree; the local `is_english` is gone.
- Tests through `set_editing_language` (the document-default path) and with no language at all.
- docs/typing-parity.md: the Croatian/Bosnian/Serbian case needs the text tagged, as those
  interfaces set no editing language yet.

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-12 09:56:29 -04:00
2f9bf4e800 AutoCorrect: TWo INitial CApitals, cAPS LOCK and table-cell capitals (#544) (#545)
- Correct TWo INitial CApitals: a finished word of 3+ letters with two
  leading capitals and the rest lowercase loses its second capital
  ("WOrd" -> "Word"), before the replacement list ("TEh" -> "The").
  Built-in exceptions (IDs, CDs, PCs, TVs, ...) plus user exceptions.
- Correct accidental cAPS LOCK: "wORD" -> "Word" (not iOS, mRNA, ...).
- Capitalize first letter of table cells, under its own option.
- Session::autocorrect_caps (AutoCorrectCaps, all on by default);
  tools.autocorrect takes twoInitialCaps / capsLock / capCells /
  addException / removeException.
- Each fix is one undo step right after it (the existing AutoFormat
  snapshot); case changes replace letter by letter, keeping formatting.
- Tests in tests_typing.rs; docs/typing-parity.md and ROADMAP updated.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 07:35:11 -04:00
62a17b841f Bidi: auto-detect RTL paragraph direction when typing Kurdish, Arabic and Hebrew (#713)
* Bidi: auto-detect RTL paragraph direction and keep typing caret advancing smoothly

When typing in Kurdish, Arabic or Hebrew in a new document, default
paragraph direction (bidi: None) was resolving to LTR. In an LTR
paragraph, visual reordering pinned newly typed characters and the
caret to the left margin (x = 72.0), resetting to the start of the line
with every keystroke.

- Add first_strong_is_rtl() following UAX #9 rules P2/P3.
- Auto-resolve unassigned paragraph direction to RTL in layout when the
  paragraph starts with an RTL script.
- Auto-promote paragraph to RTL (bidi = Some(true)) when typing text
  starting with an RTL character.
- Add regression test in wordcraft-engine.

* Review fixes for #713: RTL only when typing into an empty paragraph

- Drop the layout fallback that drew any paragraph without w:bidi as RTL when its first
  strong character is Arabic/Hebrew: opened DOCX paragraphs keep their direction, and a
  style that sets bidi=false is respected.
- type_text promotes a paragraph to RTL only when it had no letters before the insert and
  nothing (paragraph, style chain, document defaults) sets its direction; typing into a
  paragraph that already has text no longer flips it.
- docs/typing-parity.md: Text direction section; test in tests_typing.rs.

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 06:51:03 -04:00
25048c8b9c M7: ignore ellipses in sentence capitalization proofing (#742)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:29:57 -04:00
3fd97c3e58 Complex scripts: shape Myanmar, Khmer and Indic text; caret and line breaks respect syllables (#604) (#652)
Rebased onto main as one commit (the branch had merge commits from main).

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-11 23:22:57 -04:00
850fa0576a AutoFormat: +---+ tables, arrow bullets, smileys inside words, border-line undo (#546) (#547)
- `+---+------+` Enter turns the paragraph into a one-row table across the text
  width, columns proportional to the hyphens, caret in the first cell (body,
  top level, at most 63 columns).
- `> ` `-> ` `=> ` start a bulleted list with an arrow bullet (➢ ➔ ⇨).
- Smileys `:)` `:-)` `:(` `:-(` `:|` are replaced also straight after a word.
- Undo right after a border line or table AutoFormat restores the typed
  characters and keeps the Enter (its own undo step).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 21:11:22 -04:00
900c0f4412 Outline view: Outlining tab, levels, collapse/expand, Show Level (#493) (#499)
* Outline view: Outlining tab, levels, collapse/expand, Show Level (#493)

- View › Outline is its own view mode: the body on one long sheet, each paragraph indented
  by its outline level behind a symbol drawn in code (circled plus for a heading with content,
  circled minus for one without, a dot for body text); paragraph indents and alignment hidden.
- Collapsed headings hide their subtree and get a wavy line; Show Level 1-9 / All; Show First
  Line Only; Show Text Formatting off draws everything in the Normal style. All of it is view
  state (ViewState.outline), not saved; collapsed headings are found again by their text after
  edits, and a caret entering hidden text moves on to the next shown paragraph.
- Commands: outline.promoteToHeading1/promote/demote/demoteToBody/level/moveUp/moveDown,
  outline.expand/collapse/toggle/selectSubtree, outline.showLevel (+ showLevel1-9, showAll),
  outline.firstLineOnly, outline.showFormatting, outline.close, outline.state. In Outline view
  Move Up/Down and Promote/Demote take a collapsed heading's hidden content along.
- Keys: Alt+Shift+Plus/Minus expand/collapse, Alt+Shift+1-9 and Alt+Shift+A Show Level.
- Outlining tab (next to File while in Outline view): level box, promote/demote, move,
  expand/collapse, Show Level, the two check boxes, Close Outline View. Clicking a symbol
  selects the heading with its content; double-clicking expands or collapses it.
- Catalog: Outlining tab rows (421/453, 92.9%); translations in all nine catalogs; docs.

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

* Docs: drop the stale shortcut and dialog rows left by the rebase (#493)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 19:54:56 -04:00
2e5ecb916b File › Options: Word's panes with working settings (#485) (#492)
File › Options is now a pane list (General, Display, Proofing, Save,
Language, Accessibility, Advanced, Agents), one function per pane in
crates/ui-egui/src/options.rs. Every setting shown is wired:

- General: theme, interface size (#475), mini toolbar on selection, dark page, community
  button, user name and initials (comments use the initials).
- Display: tab characters, spaces, paragraph marks and hidden text shown
  with Show/Hide ¶ off; rulers; print the page colour; update fields
  before printing.
- Proofing: ignore words in capitals / with numbers / web and file
  addresses (proof::Options, passed through layout), check spelling and
  mark grammar as you type, custom dictionary add/remove (kept in ui.json).
- Save: AutoSave. Language: interface language. Accessibility: Read Aloud
  speed and citations. Advanced: typing replaces selection, Insert key
  overtype, overtype (text.overtype), recent documents count, measurement
  units (dialogs, Layout ribbon indents and rulers).

file.options reads and sets the engine-side settings headlessly and only
opens the page when given no setting. ui.json loads leniently: a bad value
falls back to its default instead of dropping every setting.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 19:17:50 -04:00
968c66a13e Fixes: tracked paste marks, plain paste blank lines, ^p in Replace, Delete/Split columns and cells, Font dialog keeps untouched formatting (#417, #422, #423, #431, #432, #466) (#479)
- Paste under Track Changes marks the paragraph marks it adds as inserted, so Reject All
  joins the paragraphs again (#417).
- Keep Text Only / Paste Special text paste treat paragraph separators as text, not Enter:
  an empty list item between them no longer ends the list (#422).
- Untracked Replace All splits paragraphs at CR / U+2029 in the replacement like a single
  Replace does (#423).
- Delete Columns deletes every grid column the selected cells cover (#431).
- Split Cells refines the grid inside the split cell only; other rows' cells widen over the
  new grid columns, keeping the table width (#432).
- Font dialog sends only the properties the user changed and shows small caps, all caps,
  hidden and spacing from the selection (format.state reports them) (#466).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 17:36:30 -04:00
50505d9e62 Editing fixes: Center range, undo grouping, Change Case runs and offsets, Bold undo at caret (#418, #419, #420, #421, #424) (#477)
- Paragraph formatting skips the paragraph a non-empty selection ends at the start of
  (Select Paragraph's end), via Document::para_paths_in (#418).
- A non-mutating command that moves the selection closes the typing group (#419).
- Change Case converts char by char and replaces only changed stretches inside one run,
  keeping each run's formatting (#421) and mapping the selection to the new offsets (#420).
- Undo/redo steps carry the pending caret formatting (#424).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 17:13:50 -04:00
4b5ed293d7 Compare, Restrict Editing and AutoCorrect Options dialogs (#412) (#476)
* Compare, Restrict Editing and AutoCorrect Options dialogs (#412)

- Compare / Combine Documents dialog: original (this document or a file) and
  revised document, label changes with, More >> comparison settings, word or
  character level, show changes in. review.compare honours case changes,
  white space, formatting (tracked formatting changes), tables (compared and
  kept, no longer dropped) and character level; the LCS no longer drops text
  past its size cap. Unsupported settings are accepted and reported.
- Restrict Editing pane: formatting restrictions (locked styles,
  w:formatting), editing restrictions, exceptions for everyone
  (w:permStart/permEnd), Start Enforcing Protection with an optional password
  (SHA-512, salt, spin count 100000; ECMA-376 §17.15.1.29) and Stop
  Protection. Enforced in Session::run: outside the exceptions nothing may
  change; undo can't lift protection; passwords never land in Repeat/macros.
  DOCX round trip of protection, hash attributes, locked styles and ranges.
- AutoCorrect Options dialog: replacement list (add/replace/delete, built-in
  entries too), capitalization options with exceptions, AutoFormat As You
  Type switches, Math AutoCorrect list. Saved per user in ui.json; damaged
  values read as defaults. Names of days are capitalised.

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

* Restrict Editing: verify Word's legacy protection hash; refuse unsupported forms (#412)

- Stop Protection checks Word's legacy w:documentProtection hash
  (w:cryptAlgorithmSid/w:hash/w:salt/w:cryptSpinCount) like Word: the password
  becomes the legacy 32-bit key (ECMA-376 Part 4 §14.8.1 initial code array and
  encryption matrix), its reversed bytes as an upper-case hex string are hashed
  with the salt prepended and the iteration number appended. A wrong password
  is rejected; the form no longer unlocks with any password.
- Unsupported or damaged hashes (MD2/MD4/MD5 and other SIDs, w:algIdExt,
  unknown algorithmName, spin counts above 10,000,000, bad base64) refuse with
  "This password type isn't supported" and the protection stays on. A
  protection without a hash still stops without a password.
- Known-answer tests: the specification's "Example" key 0x64CEED7E and an
  independently computed SHA-1 hash; pane message in all 9 catalogs.

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-11 17:10:02 -04:00
adcbf52611 Move paragraphs, promote/demote, Go Back and Alt+X like Word (#343) (#351)
- para.moveUp / para.moveDown (Alt+Shift+Up/Down, Ctrl+Shift+Up/Down on macOS): the
  paragraphs the selection touches swap with their neighbour in the same story or cell,
  keeping style and list membership; selection follows; one undo step; refused at the
  container edges, next to a table and across a section break.
- para.promote / para.demote (Alt+Shift+Left/Right, Ctrl+Shift+Left/Right on macOS):
  heading level up/down (Heading 1..9), promoted body text takes the level of the heading
  before it, list items change level like Shift+Tab/Tab.
- edit.goBack (Shift+F5): cycles the caret through the last four edit places.
- text.toggleUnicode (Alt+X; unbound on macOS): hex code <-> character, undoable. The
  canvas no longer types the key's letter after an Alt shortcut ran.
- Catalog rows, translations in every interface catalog, typing/UI parity docs, tests.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 11:49:02 -04:00
Valentin KleinandClaude Opus 5.5 5e84270e60 Hyperlinks cover only their own text (#413) (#449)
Linking a whole paragraph also linked its paragraph mark, and Enter
copied the link at the split into the new paragraph's mark, so everything
typed in the paragraphs Enter made (above or below) became the link, and
hovering anywhere near the end of the text showed the link's tooltip.

- doc: a paragraph mark never takes a hyperlink (format_range, split_off)
- engine: typing at either end of a link isn't linked; inside it is (Word)
- ui: hover tooltip and Ctrl+click only on the linked text itself

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:50:48 -04:00
Brandon ThomasandClaude Opus 5.5 eb31860917 Progress docs: target-app parity, gaps, alpha gate, readiness by audience (#277)
Progress docs per craftrules standards/progress-docs.md: target-app parity, gaps, roadmap with alpha gate, readiness by audience (full / mainstream / essentials) with Opus 5.5 hours, and feature, format, layout, hardware, UI and localization parity docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:11:53 -04:00
Valentin KleinandClaude Opus 5.5 4c4dc14a54 Reject All removes paragraph breaks inserted with Track Changes on (#229) (#244)
Enter under Track Changes already recorded the new paragraph mark as an
insertion, but accepting or rejecting only cleared that flag, so Reject All
left the paragraphs split. Rejecting an inserted mark (or accepting a deleted
one) now joins the paragraph with the next one again; Accept All, Reject All,
Accept/Reject at the caret or over a selection, Next/Previous Change and the
Reviewing Pane all see paragraph marks.

Backspace at a paragraph start and Delete at a paragraph end now record a
tracked deletion of the mark (or remove a mark the same author inserted)
instead of only moving the caret; tracked deletions across paragraphs mark
the paragraph marks too.

DOCX reads and writes paragraph-mark revisions (w:ins / w:del in the
mark's w:rPr), and a tracked mark is drawn in its author's colour when
formatting marks are shown.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:19:52 -04:00
Brandon ThomasandClaude Opus 5.5 869ac16110 Typing parity with Word: lists, AutoFormat and AutoCorrect as you type (#204)
* Typing parity with Word: lists, AutoFormat and AutoCorrect as you type

Typed side by side with Word (same keystrokes, results read back) and matched it:

- Enter after a list no longer gets stuck: the paragraph that leaves a list goes back to
  Normal, and later Enters make blank paragraphs (the `num: 0` marker counted as a list item,
  so every Enter re-ran "leave the list" and did nothing; the list indent also stuck to every
  following paragraph).
- Enter on an empty nested item moves it up a level before leaving the list.
- Second Backspace at the start of an un-numbered list paragraph returns it to Normal
  instead of joining it to the paragraph above.
- "1. " always starts numbering at 1; "a. " and "I. " start lettered/roman lists; "- " makes
  a dash bullet.
- Undo right after an AutoFormat/AutoCorrect change undoes only that change.
- AutoCorrect: sentence capitals (with abbreviation/initial exceptions), runs on Enter too,
  "word -- word" en dash, "wait..." ellipsis, 1/2 1/4 3/4, ordinal superscripts, web
  addresses become links.
- Border lines: Enter after ---, ===, ***, ___, ~~~, ### draws a bottom border.
- Layout: no line break right after a slash ("and/or", URLs), as in Word.
- ui_shot: UI_SHOT_FULL=1 prints whole replies for scripted checks.

Regression tests in crates/engine/src/tests_typing.rs and the layout tests.

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

* Typing parity: record Word's observed typing rules, two more tests

docs/typing-parity.md lists what Word does for each key sequence (lists, Backspace, Tab,
AutoCorrect, AutoFormat, slash line breaks) with the test that pins it, the known gaps, and how
to compare without taking over the shared machine. CLAUDE.md/AGENTS.md point at it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 04:52:10 -04:00