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 } from 'yorkie-js-sdk';3import type { TextOperationInfo, EditOpInfo } from 'yorkie-js-sdk';4import { basicSetup, EditorView } from 'codemirror';5import { keymap } from '@codemirror/view';6import {7 markdown,8 markdownKeymap,9 markdownLanguage,10} from '@codemirror/lang-markdown';11import { Transaction } from '@codemirror/state';12import { network } from './network';13import { displayLog, displayPeers } from './utils';14import { YorkieDoc } from './type';15import './style.css';1617const editorParentElem = document.getElementById('editor')!;18const peersElem = document.getElementById('peers')!;19const documentElem = document.getElementById('document')!;20const documentTextElem = document.getElementById('document-text')!;21const networkStatusElem = document.getElementById('network-status')!;2223async function main() {24 // 01. create client with RPCAddr then activate it.25 const client = new yorkie.Client(import.meta.env.VITE_YORKIE_API_ADDR, {26 apiKey: import.meta.env.VITE_YORKIE_API_KEY,27 });28 await client.activate();2930 // 02-1. create a document then attach it into the client.31 const doc = new yorkie.Document<YorkieDoc>(32 `codemirror6-${new Date()33 .toISOString()34 .substring(0, 10)35 .replace(/-/g, '')}`,36 {37 enableDevtools: true,38 },39 );40 doc.subscribe('connection', (event) => {41 network.statusListener(networkStatusElem)(event);42 });43 doc.subscribe('presence', (event) => {44 if (event.type !== DocEventType.PresenceChanged) {45 displayPeers(peersElem, doc.getPresences(), client.getID()!);46 }47 });48 await client.attach(doc);49 doc.update((root) => {50 if (!root.content) {51 root.content = new yorkie.Text();52 }53 }, 'create content if not exists');5455 // 02-2. subscribe document event.56 const syncText = () => {57 const text = doc.getRoot().content;58 view.dispatch({59 changes: { from: 0, to: view.state.doc.length, insert: text.toString() },60 annotations: [Transaction.remote.of(true)],61 });62 };63 doc.subscribe((event) => {64 if (event.type === 'snapshot') {65 // The text is replaced to snapshot and must be re-synced.66 syncText();67 }68 displayLog(documentElem, documentTextElem, doc);69 });7071 doc.subscribe('$.content', (event) => {72 if (event.type === 'remote-change') {73 const { operations } = event.value;74 handleOperations(operations);75 }76 });7778 await client.sync();7980 // 03-1. define function that bind the document with the codemirror(broadcast local changes to peers)81 const updateListener = EditorView.updateListener.of((viewUpdate) => {82 if (viewUpdate.docChanged) {83 for (const tr of viewUpdate.transactions) {84 const events = ['select', 'input', 'delete', 'move', 'undo', 'redo'];85 if (!events.map((event) => tr.isUserEvent(event)).some(Boolean)) {86 continue;87 }88 if (tr.annotation(Transaction.remote)) {89 continue;90 }91 let adj = 0;92 tr.changes.iterChanges((fromA, toA, _, __, inserted) => {93 const insertText = inserted.toJSON().join('\n');94 doc.update((root) => {95 root.content.edit(fromA + adj, toA + adj, insertText);96 }, `update content byA ${client.getID()}`);97 adj += insertText.length - (toA - fromA);98 });99 }100 }101 });102103 // 03-2. create codemirror instance104 const view = new EditorView({105 doc: '',106 extensions: [107 basicSetup,108 markdown({ base: markdownLanguage }),109 keymap.of(markdownKeymap),110 updateListener,111 ],112 parent: editorParentElem,113 });114115 // 03-3. define event handler that apply remote changes to local116 function handleOperations(operations: Array<TextOperationInfo>) {117 for (const op of operations) {118 if (op.type === 'edit') {119 handleEditOp(op);120 }121 }122 }123 function handleEditOp(op: EditOpInfo) {124 const changes = [125 {126 from: Math.max(0, op.from),127 to: Math.max(0, op.to),128 insert: op.value!.content,129 },130 ];131132 view.dispatch({133 changes,134 annotations: [Transaction.remote.of(true)],135 });136 }137138 syncText();139 displayLog(documentElem, documentTextElem, doc);140}141142main();
- User 1
- User 2
- User 1
- User 2
Event Log