Dollie Editor SDK guide
Show and Save the Edit Trail
Let people review manual and Copilot edits, undo a specific AI change, and return to an earlier saved revision
The edit trail answers three simple questions:
- What changed?
- Was the change made by a person or by the Copilot?
- Can I undo it?
The Editor already records these details for every page change. You choose which parts to show and whether to keep them after the page is saved.
What people see
The built-in timeline appears in the Editor toolbar. It shows:
- manual edits;
- Copilot and AI fill edits;
- edits that can still be redone;
- saved revisions from earlier sessions.
Clicking an edit from the current session moves the page to that point in its undo history. Clicking a saved revision calls your restore handler.
Copilot edits can also appear below the chat message that produced them. These markers offer Undo, Redo, or Revert, depending on the page's current state. A section that was removed is shown as unavailable instead of offering an action that cannot work.
Add saved revisions to the timeline
PageBuilderEditor already includes the timeline button. Pass the saved revisions returned by your page store:
<PageBuilderEditor
editor={editor}
revisions={revisions}
onRestoreRevision={async (revisionId) => {
if (!transport.restoreRevision) return;
await transport.restoreRevision(pageId, revisionId);
window.location.reload();
}}
/>
Restoring a revision should create a new current revision. It should not delete or rewrite the old history.
Show Copilot changes in chat
useEditorCopilot returns a trail handle. Pass it directly to AgentChat:
const { trail } = useEditorCopilot({
editor,
fill,
catalog,
sections,
});
<AgentChat markers={trail} />
The chat kit places each marker below the assistant message that produced the edit. This works when the apply result includes its message id; the built-in Editor result renderers do that automatically.
Save the trail with the page
The live undo stack stays in memory. Do not save every keystroke as a revision.
When the user saves, send trail.pendingTrail with the page definition. Call trail.markSaved() only after the server confirms the save:
const saved = await transport.save(pageId, {
definition: serializeEditorState(editor.state),
revision: document.revision,
trail: trail.pendingTrail,
});
trail.markSaved();
Each revision summary may contain:
type PageRevisionTrailEntry = {
id: string;
at: number;
source: 'user' | 'copilot' | 'fill';
label: string;
targetKey?: string;
afterMessageId?: string;
snapshot?: PageRevisionTrailSnapshot;
};
Manual entries contain a label and other small pieces of metadata. Copilot and fill entries also contain the before-and-after snapshot needed to revert that specific change.
Section snapshots also keep the section's position at apply time. The Editor uses the saved content and position to reconnect the marker to the current session's section id after a reload.
Your page store must return the saved trail from listRevisions. The built-in file store already does this.
Listen for edits in your own interface
Use subscribe when your product needs a notification, audit panel, or another custom surface:
useEffect(() => {
return trail.subscribe((event) => {
if (event.type === 'recorded') {
analytics.track('page edit recorded', {
label: event.entry.meta.label,
source: event.entry.meta.source,
});
}
});
}, [trail]);
A recorded event has this shape:
type EditorTrailEvent = {
type: 'recorded';
entry: {
meta: {
id: string;
at: number;
source: 'user' | 'copilot' | 'fill';
label: string;
targetKey?: string;
afterMessageId?: string;
};
position: number;
branch: 'applied' | 'redo';
current: boolean;
};
};
subscribe reports new edit entries. It does not replace your page store and it does not send anything to the Copilot.
Build another trail interface
useEditorTrail(editor) exposes the same state used by the built-in UI:
| Value | Use |
|---|---|
entries |
Current session edits, including the redo branch |
markers |
Copilot and fill edits with live revert actions |
currentPosition |
Current point in the session history |
jumpTo(position) |
Move through the undo or redo stack |
pendingTrail |
Entries that have not been included in a successful save |
markSaved() |
Move the save checkpoint forward |
subscribe(listener) |
Hear about newly recorded edits |
Use Marker, MarkerIcon, and MarkerContent to match the built-in visual language. Marker supports default, border, and separator variants and accepts a Base UI render prop when it needs to behave as a button or link.
The registry descriptions for these building blocks are:
- Marker: A small status or activity row for edits, saves, and other events.
- Edit timeline: A toolbar history panel that combines live page edits with saved revision checkpoints.
Keep markers out of the model prompt
Markers are interface history, not instructions to the model.
Do not turn them into synthetic chat messages. They can become stale after an undo, a later manual edit, or a revision restore.
The latest page_context remains the Copilot's source of truth for the page. Send that current context with every user message.