> ## Documentation Index
> Fetch the complete documentation index at: https://developer.revise.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracked changes

> Suggestions your users can accept or reject, from people and agents alike.

## Modes

A document in `suggesting` 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.

```ts theme={null}
editor.view.setDocumentMode("suggesting");
```

Set the starting mode per document, or editor-wide:

```tsx theme={null}
<ReviseEditor
  defaultDocumentMode="suggesting"
  initialDocuments={[{ id: "a", docx: file, documentMode: "editing" }]}
/>
```

<Note>
  `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.
</Note>

A mode is a stance the user can leave. When a participant must *not* be able to
leave it — a reviewer who may only propose — use a [role](/editor-sdk/guides/roles)
instead: `role="suggester"` pins the mode, refuses direct edits, and withholds
accept and reject on every surface.

## Reviewing

The `review` 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.

```ts theme={null}
const state = editor.review.getState();
// { open, targetCount, openSuggestionIds, currentSuggestionIds, ... }

editor.review.open();
editor.review.next();
editor.review.acceptCurrent();
editor.review.rejectCurrent();
editor.review.acceptAll();
```

Subscribe to render your own review UI:

```ts theme={null}
useEffect(() => editor.review.subscribe(setReviewState), [editor]);
```

### Targeted operations

When you know which suggestions you care about — for instance, everything a
particular agent run produced — operate on the IDs directly:

```ts theme={null}
const ids = editor.review.getOpenSuggestionIds();

editor.review.navigateToSuggestion(ids[0]);
editor.review.previewSuggestions(ids, "accept"); // non-destructive preview
editor.review.acceptSuggestions(ids); // { resolved, missing, unresolved }
```

`previewCurrent()` and `previewAll()` do the same for the current stop and the
whole document. Pass `null` to clear a preview.

### Display

```ts theme={null}
editor.review.setShowRemovals(false);
editor.review.setSuggestionViewMode("final"); // "all-markup" | "final" | "original"
```

`"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. Pass `directMode` per call:

```ts theme={null}
await editor.tools.execute(
  "style_blocks",
  { selectors: "*", attrs: [{ name: "fontFamily", value: "Georgia" }] },
  { directMode: true },
);
```

For a delegated agent run, the mode is inherited by every tool call in the
loop, so one flag governs the whole task.

<Warning>
  Direct mode bypasses the tracked-change trail. Reserve it for changes the
  user has already approved in your own UI; anything speculative should stay a
  suggestion.
</Warning>

## 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 survive `exportDocx()` 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.

<Note>
  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.
</Note>

## 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:

| Word markup                                                      | Round trip                                                 |
| ---------------------------------------------------------------- | ---------------------------------------------------------- |
| `w:ins` / `w:del`, including nested insert-inside-delete         | Preserved, as layered suggestions                          |
| Run formatting changes (`w:rPrChange`)                           | Preserved, with the prior properties, so rejecting reverts |
| Paragraph property changes (`w:pPrChange`)                       | Preserved, with the prior properties                       |
| Paragraph-mark (pilcrow) revisions                               | Preserved, as pending splits and merges                    |
| Tracked deletion of a section break                              | Preserved, both directions                                 |
| Table rows, cells, and table properties                          | Preserved, as one resolvable change per table              |
| Author, date, and Word revision ids                              | Preserved, both directions                                 |
| Comments, threading, resolved state, anchors inside tracked runs | Preserved                                                  |

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:

```ts theme={null}
for (const change of editor.review.listChanges()) {
  // { id, kind, author, authorType, createdAt, blockIds,
  //   insertedText, deletedText, description, agentModel,
  //   moveSourceBlockIds, moveDestinationBlockIds }
}

editor.review.getChange(id); // one row, or null once it is resolved
```

`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:

```tsx theme={null}
<li onClick={() => editor.review.navigateToSuggestion(change.id)}>
  <b>{change.author}</b> {change.kind === "delete" ? "removed" : "added"}{" "}
  <q>{change.deletedText ?? change.insertedText}</q>
  <button onClick={() => editor.review.acceptSuggestions([change.id])}>
    Accept
  </button>
</li>
```

<Note>
  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.
</Note>
