> ## Documentation Index
> Fetch the complete documentation index at: https://revise.io/developer/docs/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 covers typing, toolbar commands, and agent tool
calls. An agent can draft in this mode, and the user reviews each change.

```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", source: file, documentMode: "editing" }]}
/>
```

<Note>
  `defaultDocumentMode` sets the starting mode only. Changing the prop later
  does not affect a document that is already open. Use `view.setDocumentMode()`
  for that.
</Note>

The user can switch modes. To lock a participant into suggesting, use a
[role](/developer/docs/developer/docs/editor-sdk/guides/roles) instead. `role="suggester"` pins the mode,
refuses direct edits, and hides accept and reject everywhere.

## Reviewing

The `review` controller is the host API for tracked changes. It is 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

To act on specific suggestions, such as everything one agent run produced,
pass their IDs:

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

editor.review.navigateToSuggestion(ids[0]);
editor.review.previewSuggestions(ids, "accept"); // preview only, no changes
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 with every suggestion accepted. `"original"`
shows it with none accepted. Neither changes the document, so either works as
a read-only "clean copy" toggle.

## Direct mode

To apply an agent edit without tracking it, such as a formatting sweep or a
find-and-replace the user asked for, pass `directMode` per call:

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

In a delegated agent run, every tool call in the loop inherits the mode. One
flag covers the whole task.

<Warning>
  Direct mode skips tracked changes. Use it only for changes the user already
  approved in your UI.
</Warning>

## Attribution

Each suggestion records its author, and agent suggestions are marked as such.
`review.getState().visibleAgentSuggestionIds` lists only the agent's
suggestions, and `nextAgentSuggestion()` steps through only those. Use them to
review the agent's changes without the user's own edits.

Changes made through `tools.execute()` and agent runs are agent changes,
signed **"Revise Agent"**. To sign them as your product's
assistant, name it:

```tsx theme={null}
<ReviseEditor agent={{ name: "Acme Assistant" }} />
```

People's own edits use `currentUser`.

## Export

`exportDocx()` writes tracked changes as Word revision marks. A reviewer who
opens the file in Word sees the same suggestions, and accepting them there
gives the same result as accepting them in Revise.

<Note>
  Suggestions use the identity you pass as `currentUser`. Without it, human
  suggestions export with the reviewer name **"Anonymous"** and agent
  suggestions as **"Revise Agent"**. The name is recorded at edit time, not at
  export, so pass `currentUser` before anyone edits.
</Note>

## Word round trip

DOCX import and export preserve revision marks. 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 |

Tracked row insertions and deletions (`w:trPr` → `w:ins` / `w:del`), cell
revisions (`w:cellIns`, `w:cellDel`, `w:cellMerge`), and table property changes
(`w:tblPrChange`) import as resolvable suggestions. They export as the same
markup, with authors intact.

Linked moves (`w:moveFrom` / `w:moveTo` pairs) import as one move suggestion.
Accepting either half keeps the text at its destination. Rejecting either half
restores the original location. Export writes the paired move markup back. An
orphaned or mismatched half imports as an ordinary insertion or deletion, so
unrelated content is never resolved together.

## Custom review panel

`listChanges()` returns every pending change with the metadata a panel needs.
Use it to render the list yourself instead of using 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
```

| Field | Value |
| - | - |
| `kind` | `"insert"`, `"delete"`, or `"format"` (as in Word), or `"move"` for a linked move pair |
| `author` | A person's name, `"Revise Agent"`, an external agent's name, or `"Anonymous"` when no identity was supplied. Imported Word redlines keep their original reviewer. |
| `blockIds` | The blocks the change touches. `navigateToSuggestion()` scrolls to these. |
| `moveSourceBlockIds`, `moveDestinationBlockIds` | Where moved text left and where it landed. `deletedText` and `insertedText` hold the text at each end. |

A replacement made in one operation, such as the agent's `replace` tool, is
one change with both `deletedText` and `insertedText` set. Its `kind` is the
one it was recorded with, usually `"delete"`, so check for both texts to show it
as a replacement. One accept or reject settles both halves. A move is also one
change for both halves, and one accept or reject settles both locations.

Wire a row to the controller with the ID:

```tsx theme={null}
const verb =
  change.kind === "move"
    ? "moved"
    : change.kind === "format"
      ? "reformatted"
      : change.deletedText && change.insertedText
        ? "replaced"
        : change.kind === "delete"
          ? "removed"
          : "added";

<li onClick={() => editor.review.navigateToSuggestion(change.id)}>
  <b>{change.author}</b> {verb}{" "}
  <q>{change.deletedText ?? change.insertedText}</q>
  <button onClick={() => editor.review.acceptSuggestions([change.id])}>
    Accept
  </button>
</li>
```

<Note>
  `listChanges()` is not a subscription. It computes the list from the
  document on each call, in one pass. Call it when you render, for example
  after `review.subscribe()` reports that the counts changed.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.