CodeMirror
This is a real-time collaborative example of the CodeMirror 6 editor. It uses the Text, a custom CRDT type from Yorkie.
main.ts
1/* eslint-disable jsdoc/require-jsdoc */2import yorkie, { DocEventType, OperationInfo } from 'yorkie-js-sdk';3import { basicSetup, EditorView } from 'codemirror';4import { keymap } from '@codemirror/view';5import {6 markdown,7 markdownKeymap,8 markdownLanguage,9} from '@codemirror/lang-markdown';10import { Transaction } from '@codemirror/state';11import { network } from './network';12import { displayLog, displayPeers } from './utils';13import { YorkieDoc } from './type';14import './style.css';1516const editorParentElem = document.getElementById('editor')!;17const peersElem = document.getElementById('peers')!;18const documentElem = document.getElementById('document')!;19const documentTextElem = document.getElementById('document-text')!;20const networkStatusElem = document.getElementById('network-status')!;2122async function main() {23 // 01. create client with RPCAddr then activate it.24 const client = new yorkie.Client(import.meta.env.VITE_YORKIE_API_ADDR, {25 apiKey: import.meta.env.VITE_YORKIE_API_KEY,26 });27 await client.activate();2829 // subscribe peer change event30 client.subscribe((event) => {31 network.statusListener(networkStatusElem)(event);32 });3334 // 02-1. create a document then attach it into the client.35 const doc = new yorkie.Document<YorkieDoc>(36 `codemirror6-${new Date()37 .toISOString()38 .substring(0, 10)39 .replace(/-/g, '')}`,40 );41 doc.subscribe('presence', (event) => {42 if (event.type !== DocEventType.PresenceChanged) {43 displayPeers(peersElem, doc.getPresences(), client.getID()!);44 }45 });46 await client.attach(doc);47 doc.update((root) => {48 if (!root.content) {49 root.content = new yorkie.Text();50 }51 }, 'create content if not exists');5253 // 02-2. subscribe document event.54 const syncText = () => {55 const text = doc.getRoot().content;56 view.dispatch({57 changes: { from: 0, to: view.state.doc.length, insert: text.toString() },58 annotations: [Transaction.remote.of(true)],59 });60 };61 doc.subscribe((event) => {62 if (event.type === 'snapshot') {63 // The text is replaced to snapshot and must be re-synced.64 syncText();65 }66 displayLog(documentElem, documentTextElem, doc);67 });6869 doc.subscribe('$.content', (event) => {70 if (event.type === 'remote-change') {71 const { operations } = event.value;72 handleOperations(operations);73 }74 });7576 await client.sync();7778 // 03-1. define function that bind the document with the codemirror(broadcast local changes to peers)79 const updateListener = EditorView.updateListener.of((viewUpdate) => {80 if (viewUpdate.docChanged) {81 for (const tr of viewUpdate.transactions) {82 const events = ['select', 'input', 'delete', 'move', 'undo', 'redo'];83 if (!events.map((event) => tr.isUserEvent(event)).some(Boolean)) {84 continue;85 }86 if (tr.annotation(Transaction.remote)) {87 continue;88 }89 let adj = 0;90 tr.changes.iterChanges((fromA, toA, _, __, inserted) => {91 const insertText = inserted.toJSON().join('\n');92 doc.update((root) => {93 root.content.edit(fromA + adj, toA + adj, insertText);94 }, `update content byA ${client.getID()}`);95 adj += insertText.length - (toA - fromA);96 });97 }98 }99 });100101 // 03-2. create codemirror instance102 const view = new EditorView({103 doc: '',104 extensions: [105 basicSetup,106 markdown({ base: markdownLanguage }),107 keymap.of(markdownKeymap),108 updateListener,109 ],110 parent: editorParentElem,111 });112113 // 03-3. define event handler that apply remote changes to local114 function handleOperations(operations: Array<OperationInfo>) {115 operations.forEach((op) => {116 if (op.type === 'edit') {117 handleEditOp(op);118 }119 });120 }121 function handleEditOp(op: any) {122 const changes = [123 {124 from: Math.max(0, op.from),125 to: Math.max(0, op.to),126 insert: op.value!.content,127 },128 ];129130 view.dispatch({131 changes,132 annotations: [Transaction.remote.of(true)],133 });134 }135136 syncText();137 displayLog(documentElem, documentTextElem, doc);138}139140main();
- User 1
- User 2
- User 1
- User 2
Event Log