State, selection, history and saving

Documentation index

Authoritative state

The model, not innerHTML, owns the document. Source sessions contain literal text. Rich sessions contain blocks and marked runs. Serialization produces the storage string. There is a maximum of 200,000 UTF-16 code units; this is not a byte, Unicode grapheme, or visible-character count.

Each session has a sessionId, generation, and revision. Loading increments generation/revision and invalidates old bookmarks; the instance's session ID remains the same. Edits, undo/redo, and completed image changes advance revision. Mere selection changes do not. Dirty state compares serialized content with the saved baseline; it is not simply “revision is greater than zero.”

Safe save protocol

  1. Obtain a validated snapshot.
  2. Persist that snapshot's value and format through your application.
  3. After successful persistence, acknowledge the snapshot's session/revision.
  4. If the document advanced meanwhile, leave it dirty and save again when appropriate.
var snapshot = await editor.GetSnapshotAsync();
await File.WriteAllTextAsync(fileName, snapshot.Value);
bool stillCurrent = await editor.MarkSavedAsync(snapshot);

The file write above is illustrative. A server save should also handle application-level document version/conflict rules. Editor revision numbers describe a local session, not a database concurrency token shared across users.

getSnapshot / GetSnapshotAsync validates policy and image state. getValue / GetEditorTextAsync retrieves serialized text but does not by itself reject policy diagnostics. Both can reject during active composition or while images are pending/failed. Calling markSaved with a stale revision returns false; it must not clear the newer document's dirty state.

WinForms cached properties and queue

EditorText returns the most recently assigned or confirmed string. Before initialization, its setter stores startup text. After initialization, it schedules an asynchronous load. Use SetEditorTextAsync if you need completion and error propagation. The bridge serializes queued property updates/method calls so the normal sequence of UI-thread operations is preserved.

Text returned from older loads is guarded against overwriting a newer assignment. A failed text assignment reverts to the last confirmed text and reports an error. Other option setters can record a desired value before browser-side validation completes; do not assume all properties automatically revert after a failed configuration. Query capabilities/snapshots when you need accepted state.

FlushAsync waits for the current operation queue and requests a committed value. It is not a file save or the policy validation step. It can fail if the queued operation failed or content cannot yet be committed. Later operations can recover after an earlier queued failure; the control is not permanently poisoned by one rejected command.

State events can arrive often and in succession. Avoid saving on every selection or command-state event. For autosave, debounce content changes, catch temporary composition/import failures, persist snapshots, and retry only according to application policy. The demo's optional browser draft storage is not a durable application backup service.

Selection coordinates

API/context Representation
JavaScript Source { anchor: number, head: number } UTF-16 offsets in source
JavaScript Visual { anchor: { blockId, offset }, head: { blockId, offset } }
WinForms both views EditorSelection(Anchor, Head) with flat UTF-16 offsets
Find matches and range replacement Flat { from, to } / SearchMatch(From, To)

Anchor is the fixed end and head is the active end; a backward selection has head before anchor. Rich flat offsets refer to the plain model text, not positions in serialized Markdown/HTML. Blocks contribute separators, and an image contributes an object-replacement position. A range must not split a surrogate pair or CRLF in a context where those are validated. Rich/source selection mapping around markup is approximate.

Never reuse offsets from one mode as if they belonged to another serialized representation. Capture a bookmark or obtain a fresh selection after a mode change.

Bookmarks for menus and dialogs

string bookmark = await editor.CaptureBookmarkAsync();
try
{
    // Show a host dialog here; it may transfer focus away from the editor.
    if (!await editor.RestoreBookmarkAsync(bookmark)) return;
    await editor.FormatSelectionAsync(TextMark.Bold);
}
finally
{
    await editor.ReleaseBookmarkAsync(bookmark);
}

A bookmark tracks a logical selection through supported edits and view mappings. Loading a document invalidates it. Restore returns false when it is no longer usable. Release tokens when finished. A bookmark is not a proof that selected text is unchanged; asynchronous content replacement also needs revision/content checks.

Applying delayed or AI-generated edits

For Source mode, an atomic transaction includes session ID, expected revision, nonoverlapping source edits, and a label:

const snapshot = editor.getSnapshot();
// After obtaining a proposed replacement for known offsets in this snapshot:
editor.applyTransaction({
  sessionId: snapshot.sessionId,
  revision: snapshot.revision,
  edits: [{ from: 0, to: 0, text: '<!-- reviewed -->\n' }],
  label: 'Insert review note'
});
var snapshot = await editor.GetSnapshotAsync();
await editor.ApplyTransactionAsync(new EditorTransaction(
    snapshot.SessionId, snapshot.Revision,
    [new TextEdit(0, 0, "<!-- reviewed -->\n")], "Insert review note"));

All transaction ranges refer to the same original document revision. Overlapping, invalid, stale, or disallowed edits are rejected atomically. This transaction API is Source-only; it is not a general rich-document patch protocol. ReplaceRangeAsync can take an expected revision for a smaller operation, subject to rich range restrictions.

An AI workflow can capture text and revision, obtain a proposal, show the proposal in host UI, then apply it through a guarded command. If the user edited meanwhile, ask them to review/recompute rather than silently replacing a new selection. No AI provider, prompt window, or network transmission is built into this delivery.

Undo, load and conversion

History stores up to 100 snapshots. Compatible adjacent typing groups within roughly 900 ms can form one undo item. Structural commands and replacements have labels. Undo can restore the view associated with the original edit. This history is in memory and is not a collaboration log.

setValue / LoadAsync resets history and bookmarks. Format conversion also resets history. Policy changes clear undo/redo because previous history may contain now-disallowed operations. Rebuilding the surface, on the other hand, preserves the model, history and selection; that is an important architectural boundary.

Dirty state can return to false after undo restores the saved content. A successful save acknowledgement changes the baseline only for the current revision. Closing an editor with unsaved or incomplete content remains a host decision.