preservation error code and changes nothing.
What Inkkit keeps
- An unedited document comes back from
snapshot()as the loaded text, byte for byte, including line endings, a byte order mark, trailing spaces and a missing final line ending. - Blocks that an edit doesn’t reach keep their bytes, and so does the text between blocks, such as extra blank lines. Link and footnote definitions stay, whether or not anything uses them.
- An edited block keeps the spelling of what the edit didn’t change where it can: the other lines of a paragraph,
__bold__next to typed text, a list’s*or+markers, a setext underline, reference labels, and a code fence’s characters and info string. - Line endings follow the document. In a CRLF document, the lines an edit writes use CRLF.
- Undo back to the loaded text returns the loaded bytes, and
dirtyisfalseagain. - Presentation never changes the source. Folding a callout, showing comments, Mermaid previews, code highlighting and automatic language detection change neither the text nor the revision.
When a block is written afresh
Inkkit writes a block in canonical form when it can’t keep the authored spelling: a new block, or an edited block whose own source can’t express the change. Canonical form uses- for bullets, * for emphasis, ** for strong, fenced code blocks and --- for rules. You notice it in these cases:
- Indented code that you edit is saved as a fenced code block.
- Moved blocks. A block moved with
moveBlockor its shortcuts keeps its source, and an ordered item takes the number written at its new place. A block moved by cut and paste, or by drag and drop, is written in canonical form. - Repeated blocks. When the same block appears more than once, Inkkit can’t always tell which copy you edited or moved, and that copy can be written in canonical form.
- Neighboring lists. Markdown reads two adjacent lists of one kind as one list unless their markers differ. A new, moved or rewritten list takes a bullet (
-,*or+) or number delimiter (.or)) that the lists beside it don’t use. An item added to an authored list takes that list’s marker. - Thematic breaks. A
---rule that comes to open the document while another---line follows it is written as***, because it would otherwise read as front matter. A new rule inside a list item, quote, callout or footnote is written as___. - Spaces at the edge of a block or table cell, or beside a soft line break are written as
 , because Markdown would otherwise drop them. - A line break at the end of a paragraph isn’t saved until text follows it. See Line breaks.
How an edit is checked
In formatted mode, Inkkit checks every change to the document before it appears: typing, commands, paste, drop, table operations, find and replace, undo and redo. It writes the Markdown for the edited document and reads it back. If the result doesn’t read as the document the editor would show, Inkkit refuses the edit:- The document, selection, revision and undo history stay as they were.
- No
changedevent fires. - The refusal is an
InkKitErrorwith the codepreservation.
replaceSource take effect on the text exactly as written, and Inkkit reads the text when you switch back to formatted mode.
Where a refusal arrives depends on what made the edit:
A refusal that a call reports isn’t also sent to
events.error. Errors describes how every failure arrives.
Why an edit is refused
Each reason below reportspreservation, and each leaves the document, revision and history unchanged and the editor usable. A refused edit or load needs no recovery. A failed snapshot does: don’t save. Branch on the code, not the message, which is for logs.
Detailed refusal reference
Detailed refusal reference
Examples of edits that would read differently
Examples of edits that would read differently
- Deleting the block between two lists that use the same marker, which would merge them.
- Leaving an HTML comment at the start of a paragraph, or a paragraph that holds only an HTML comment. Markdown reads that line as an HTML block.
- Ending a literal block with a line break, or typing text into one that would change how other blocks read.
- Turning text into a code block when the code can’t be saved as the same text.
- Removing a code block’s language when the rest of its info string would then read as the language.
- Calling
editReferenceDefinitionwith an emptytitle.

