Modes
A document insuggesting mode records every change as a tracked suggestion
instead of applying it. This applies to typing, toolbar commands, and agent
tool calls equally — which is what makes “let the model draft it, then review
every change” work without any special casing.
defaultDocumentMode seeds the mode; it does not control it. Changing the
prop later will not move a document that has already opened — use
view.setDocumentMode() for that.role="suggester" pins the mode, refuses direct edits, and withholds
accept and reject on every surface.
Reviewing
Thereview controller is the host-facing surface for tracked changes. It is
deliberately not an agent tool: it exposes interactive state and moves the
caret and viewport.
Targeted operations
When you know which suggestions you care about — for instance, everything a particular agent run produced — operate on the IDs directly:previewCurrent() and previewAll() do the same for the current stop and the
whole document. Pass null to clear a preview.
Display
"final" shows the document as it would read with everything accepted;
"original" as it read before. Useful for a read-only “clean copy” toggle
without mutating anything.
Direct mode
Sometimes an agent edit should just apply — a formatting sweep, a find-and-replace the user explicitly asked for. PassdirectMode per call:
Attribution
Suggestions carry their author, and agent-authored ones are distinguishable from human edits.review.getState().visibleAgentSuggestionIds narrows to the
agent’s own work, and nextAgentSuggestion() steps through only those — enough
to build a “review what the AI changed” flow that skips the user’s own typing.
Export
Tracked changes surviveexportDocx() as Word revision marks, so a reviewer
who opens the file in Word sees the same suggestions, and accepting them there
produces the same result as accepting them in Revise.
Suggestions are authored under the identity you pass as
currentUser.
Without it, every human suggestion exports as reviewer “Anonymous” and
agent suggestions as “Revise Agent”. Pass currentUser before anyone
edits — the name is stamped at edit time, not at export.Word round trip: what survives
The DOCX path is the high-fidelity one, and revision marks are part of what it preserves. Concretely, from a Word file, through the editor, and back:
One behaviour is worth knowing before you build a test corpus:
Tables are covered too: tracked row insertions and deletions (
w:trPr →
w:ins / w:del), cell revisions (w:cellIns, w:cellDel, w:cellMerge),
and tracked table property changes (w:tblPrChange) all import as resolvable
suggestions and export as the same markup, authors intact.
Linked moves round-trip natively: w:moveFrom / w:moveTo pairs import as
one atomic move suggestion — accepting either half keeps the text at its
destination, rejecting either half restores the original location — and
export re-emits the paired move markup. An orphaned or mismatched half falls
back to an ordinary insertion or deletion, so unrelated content is never
resolved together.
Building your own review panel
listChanges() returns every pending change with the metadata a panel needs,
so you can render the list yourself instead of driving the built-in ribbon:
kind is Word’s three — "insert", "delete", "format" — plus "move"
for a linked move pair. A replacement is a deletion and an insertion sharing
a location, and appears as both — the same way Word counts it. A move is the
opposite: ONE change for both halves, with moveSourceBlockIds and
moveDestinationBlockIds locating where the text left and where it landed
(deletedText and insertedText carry the text at each end), and a single
accept or reject settles both locations. author is a person’s name,
"Revise Agent", an external agent’s name, or "Anonymous" when no identity
was supplied, and imported Word redlines keep their original reviewer.
blockIds gives the blocks a change touches, which is what
navigateToSuggestion() scrolls to.
Wire a row to the controller with the ID:
It is a pull, not a subscription: the list is derived from the document, and
recomputing it on every keystroke would be wasteful for a panel that
re-renders far less often. Call it when you render — after
review.subscribe() tells you the counts moved, for instance. One call
walks the document once no matter how many changes are open.