Repository navigation
1.0.0 — public API frozen
The public API is frozen. From here the surface follows SemVer — no breaking change without a major bump — and it is tracked in PublicAPI.Shipped.txt (553 entries), so a change to it shows up as a build warning and a diff rather than shipping unnoticed.
What made this 1.0 is verification depth, not new features. The editor has been feature-complete for a while; this release is what a full comparison against AvaloniaRichEditor 1.0, four audit passes and a randomized edit-sequence fuzz turned up — 34 defects, several of them data-losing.
⚠️ Upgrading from 0.9.x
One breaking removal:
Formatters.RoundTripHarnessis gone. It was a development tool that only used the public formatter API, so it moved into the demo project. Removing public API is breaking, which is why it happens at the freeze rather than after it.
Two behaviour changes — check these if you host the control:
- A damaged document now throws instead of reading as an empty one.
LoadJson/LoadJsonAsync/LoadPackageAsyncraiseJsonException/InvalidDataException. Swallowing the error was the outcome that lost data: a host could not tell "this file was empty" from "this file is damaged", showed a blank editor, and the next save overwrote a recoverable file with nothing. If you call these, handle the exception — the open document is left alone when they throw. - RTF table output changed shape. A horizontal merge is now expressed geometrically (one cell whose
\cellxsits at the merged edge) because HWP dissolves the flag form that Word also accepts, and a table nested in a cell is now real\itapnesting instead of flattened text.
Fixed
Reachable from a paste — any pasted or opened file is untrusted input:
- A single
<td colspan="100000000">exhausted memory in the HTML importer.rowspanwas bounded by the rows that exist;colspanhad no ceiling at all. - A
.flow/JSON document declaring a huge table exhausted memory before a cell was read — the constructor was sized from the file's own numbers, and everything it allocated was discarded three lines later. - A JSON
nullinsideBlocks/Cells/InlinesthrewNullReferenceException. - Ordinary Word RTF imported with text missing. The pending run was flushed at an ignorable group's closing brace, by which point the skipped destination was active and threw it away — and Word writes such groups routinely (bookmarks, fields, a nested table's properties). A nested table's row definition also restarted the row mid-cell, discarding what the parent cell had accumulated.
Data loss in round trips:
- A document of indented list items exported to HTML came back as literal markup text. Indent the only list item in a document and the export opens
<ol><ol><li>…; the importer looked at direct<li>children only, found nothing, produced zero blocks, and the raw-text fallback dumped the whole file as text. - Several paragraphs in a table cell collapsed into one on every RTF round trip (and on every Word/HWP paste): intra-cell
\parwas read as a newline rather than a paragraph break. - An inline table alone in its paragraph — the ordinary "treat as character" shape — swallowed the paragraph before it when reloaded, in both HTML and RTF.
- Separators accumulated as content on each save/load cycle: a space after an inline table in HTML, a newline before a nested table in RTF, a
<br>after a block element in a cell.
Interoperability (verified in Word, HWP and a browser):
- Horizontal cell merges dissolved in HWP.
- Inline tables now survive an HTML and an RTF round trip, and sit inside the text line in browsers and Word instead of stretching to a full-width band.
- Cell background colour is written to and read from RTF (
\clcbpat) — it was dropped even by our own reader.
Editing:
- The toolbar silently took keyboard focus: the caret vanished and typing stopped, while the buttons still appeared to work because the commands ran against the remembered caret position. Nothing in the strip takes focus now, and pickers hand it back when they close.
- The caret was drawn below the text, and vertical arrow movement broke, at line spacing above 100% — the caret is placed on the line's baseline now, and movement steps over the line rather than over the caret.
- Turning on a list detached an inline table or image in the same paragraph, leaving the caret pointing into a subtree no longer in the document.
- A block image inside a table cell could not be selected, resized or deleted at all.
GetPlainText()andGetImageCount()reached only one level deep, so text inside nested and inline tables was invisible — including to assistive technology, which reads the former.
Added
- Nine behaviour flags became dependency properties, so they can be bound and styled:
AllowImages,AllowTables,AllowRichPaste,AllowFindReplace,AllowLocalFileImages,AllowRemoteImagesOnPaste,AutoLinkOnType,MaxRecommendedImages,ShowFormattingMenu. Same names, same defaults. RichEditor.FocusEditor()— returns keyboard focus to the editing surface without moving the caret. Focus lives on an inner canvas, soFocus()on the control does not reach it.- XML documentation now ships with the package, so the library's comments reach IntelliSense.
- A randomized edit-sequence fuzz over the document model (
DocumentFuzzTests): structural invariants after every step, plus a two-cycle round-trip comparison through all four formats. It found three of the defects above.
Verified
Build 0 warnings / 0 errors · 106 headless tests · Native AOT publish builds, runs and renders (self-contained, 15 MB native exe) · package contents checked (dll + XML docs + README, no stray files) · a separate consumer app consuming the NuGet package builds and runs (13/13 checks) · Word, HWP and browser interoperability checked by hand.
Known limitations
- HWP does not implement RTF nested tables. It drops
\nestcelland runs the nested cells' text together. Our output is byte-identical to what AvaloniaRichEditor 1.0 emits, and Word reads it correctly. - A table whose every row is merged identically reads back from RTF as one wide column — nothing in the file then reveals the underlying grid. It renders identically.
- A nested table's column widths come back at the default: they live in an ignorable group.
- HTML loses one space inside a merged cell on the second round trip. Deliberately not fixed at 1.0 — HTML whitespace handling is separately tuned for browser copy, Word paste and
<pre>, and that risk was not worth a cosmetic space on release day.
Requires Windows App SDK 2.2.1 or later. Apps that publish self-contained must reference the Microsoft.WindowsAppSDK meta-package (only it carries the redistributable runtime).
Full detail, including the reasoning behind each fix, is in CHANGELOG.md.