Skip to content

Commit 92d0eb8

Browse files
authored
Add history functionality to document management (#279)
1 parent fa00e0d commit 92d0eb8

File tree

1 file changed

+118
-34
lines changed

1 file changed

+118
-34
lines changed

docs/js-sdk.mdx

Lines changed: 118 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ await client.sync(doc); // Trigger synchronization manually using the sync funct
536536

537537
#### Managing Document Revisions
538538

539-
Yorkie provides revision management capabilities that allow you to save snapshots of your document at specific points in time, browse through document history, and restore previous versions. This is useful for implementing features like version control, undo/redo functionality, or audit trails in your collaborative applications.
539+
Yorkie provides revision management capabilities that allow you to save snapshots of your document at specific points in time, browse through document history, and restore previous versions. This is useful for implementing features like version control, or audit trails in your collaborative applications.
540540

541541
##### Creating a Revision
542542

@@ -717,49 +717,21 @@ Restoring a revision replaces the entire current document state with the revisio
717717
All clients currently attached to the document will receive the restored state through their sync mechanisms.
718718
</Alert>
719719

720-
Here's a complete example of using the revision API:
720+
**Example workflow:**
721721

722722
```javascript
723-
// Create a client and attach a document
724-
const client = new yorkie.Client({
725-
rpcAddr: '{{API_ADDR}}',
726-
apiKey: 'xxxxxxxxxxxxxxxxxxxx',
727-
});
728-
await client.activate();
729-
730-
const doc = new yorkie.Document('my-document');
731-
await client.attach(doc);
732-
733-
// Make some changes
734-
doc.update((root) => {
735-
root.title = 'My Document';
736-
root.content = 'Initial content';
737-
});
738-
739-
// Create a revision to save this state
723+
// Create a revision
740724
const v1 = await client.createRevision(doc, 'v1.0', 'Initial version');
741725

742-
// Make more changes
743-
doc.update((root) => {
744-
root.content = 'Updated content';
745-
});
746-
747-
// Create another revision
748-
const v2 = await client.createRevision(doc, 'v2.0', 'Updated content');
749-
750726
// List all revisions
751727
const revisions = await client.listRevisions(doc);
752-
console.log(`Found ${revisions.length} revisions`);
753728

754-
// Get details of a specific revision
755-
const revisionDetails = await client.getRevision(doc, v1.id);
756-
console.log('V1.0 snapshot:', revisionDetails.snapshot);
729+
// Get revision details with snapshot
730+
const revision = await client.getRevision(doc, v1.id);
757731

758-
// Restore to v1.0
732+
// Restore to a previous revision
759733
await client.restoreRevision(doc, v1.id);
760734
await client.sync();
761-
762-
console.log('Restored to v1.0');
763735
```
764736

765737
#### Detaching the Document
@@ -902,6 +874,118 @@ doc.update((root) => {
902874
});
903875
```
904876

877+
#### History (Undo/Redo)
878+
879+
Yorkie provides built-in undo/redo functionality for all document changes. This allows users to revert or reapply changes made to the document, making it ideal for collaborative editors and applications requiring change history.
880+
881+
##### Basic Usage
882+
883+
You can undo and redo changes using the `document.history` API:
884+
885+
```javascript
886+
doc.update((root) => {
887+
root.text = new yorkie.Text();
888+
root.text.edit(0, 0, 'Hello');
889+
});
890+
891+
// Undo the last change
892+
doc.history.undo();
893+
console.log(doc.getRoot().text.toString()); // ""
894+
895+
// Redo the undone change
896+
doc.history.redo();
897+
console.log(doc.getRoot().text.toString()); // "Hello"
898+
```
899+
900+
##### Checking Undo/Redo Availability
901+
902+
Before calling `undo()` or `redo()`, you can check if these operations are available:
903+
904+
```javascript
905+
doc.update((root) => {
906+
root.counter = new yorkie.Counter(yorkie.IntType, 0);
907+
root.counter.increase(5);
908+
});
909+
910+
console.log(doc.history.canUndo()); // true
911+
console.log(doc.history.canRedo()); // false
912+
913+
doc.history.undo();
914+
console.log(doc.history.canUndo()); // false
915+
console.log(doc.history.canRedo()); // true
916+
```
917+
918+
##### Redo Stack Behavior
919+
920+
When you make a new change after undoing, the redo stack is automatically cleared:
921+
922+
```javascript
923+
doc.update((root) => {
924+
root.value = 1;
925+
});
926+
927+
doc.update((root) => {
928+
root.value = 2;
929+
});
930+
931+
doc.history.undo();
932+
console.log(doc.history.canRedo()); // true
933+
934+
// Making a new change clears the redo stack
935+
doc.update((root) => {
936+
root.value = 3;
937+
});
938+
console.log(doc.history.canRedo()); // false
939+
```
940+
941+
##### Supported Operations
942+
943+
History supports all CRDT types and operations including:
944+
- **Text**: edit operations (setStyle support is under development)
945+
- **Object**: property set, delete operations
946+
- **Array**: push, insert, delete, move operations
947+
- **Counter**: increase operations
948+
- **Tree**: Undo/Redo support is under development
949+
950+
```javascript
951+
doc.update((root) => {
952+
root.todos = [];
953+
root.todos.push('Task 1');
954+
root.todos.push('Task 2');
955+
});
956+
957+
doc.history.undo(); // Undoes 'Task 2' push
958+
console.log(doc.getRoot().todos.toJS()); // ['Task 1']
959+
960+
doc.history.redo(); // Redoes 'Task 2' push
961+
console.log(doc.getRoot().todos.toJS()); // ['Task 1', 'Task 2']
962+
```
963+
964+
965+
966+
##### Limitations
967+
968+
- **Maximum Stack Depth**: The undo/redo stacks maintain a maximum of 50 changes each. Older changes are automatically removed when the limit is reached.
969+
- **Not Available During Update**: You cannot call `undo()` or `redo()` inside a `doc.update()` callback.
970+
971+
```javascript
972+
doc.update((root) => {
973+
doc.history.undo(); // ❌ Throws Error: "Undo is not allowed during an update"
974+
});
975+
```
976+
977+
- **Local Only**: History tracks only local changes. Remote changes from other clients are applied but are not added to the undo/redo stacks. This means undo/redo operations only affect your own editing history and seamlessly integrate with changes from other users without conflicts.
978+
979+
<Alert status="info">
980+
When you undo or redo, Yorkie intelligently applies only your changes while preserving modifications made by other collaborators. This ensures conflict-free collaboration where each user can independently manage their own editing history.
981+
</Alert>
982+
983+
##### Examples
984+
985+
For complete working examples of history implementation, see:
986+
- [Vanilla CodeMirror6 Example](https://github.com/yorkie-team/yorkie-js-sdk/tree/main/examples/vanilla-codemirror6) - Text editor with undo/redo
987+
- [Whiteboard Example](https://github.com/yorkie-team/yorkie-js-sdk/blob/main/packages/sdk/public/whiteboard.html) - Visual example with history UI
988+
905989
#### TypeScript Support
906990

907991
To use the Document more strictly, you can use [type variable](https://www.typescriptlang.org/docs/handbook/2/generics.html) in TypeScript when creating a Document.

0 commit comments

Comments
 (0)