| import * as Y from 'yjs'; |
| import { Socket } from 'socket.io-client'; |
|
|
| export class YjsBinding { |
| public doc: Y.Doc; |
| public text: Y.Text; |
| private socket: Socket; |
| private fileId: string; |
| private isLocalUpdate = false; |
|
|
| constructor(socket: Socket, fileId: string) { |
| this.socket = socket; |
| this.fileId = fileId; |
| this.doc = new Y.Doc(); |
| this.text = this.doc.getText('content'); |
|
|
| |
| this.doc.on('update', (update: Uint8Array, origin: any) => { |
| if (origin !== 'remote') { |
| this.socket.emit('doc:update', { update: Array.from(update), fileId: this.fileId }); |
| } |
| }); |
|
|
| |
| this.socket.on('doc:sync', (data: { update: number[]; fileId: string }) => { |
| if (data.fileId !== this.fileId) return; |
| this.isLocalUpdate = true; |
| Y.applyUpdate(this.doc, new Uint8Array(data.update), 'remote'); |
| this.isLocalUpdate = false; |
| }); |
|
|
| this.socket.on('doc:state', (data: { state: number[]; fileId: string }) => { |
| if (data.fileId !== this.fileId) return; |
| if (data.state.length > 0) { |
| this.isLocalUpdate = true; |
| Y.applyUpdate(this.doc, new Uint8Array(data.state), 'remote'); |
| this.isLocalUpdate = false; |
| } |
| }); |
| } |
|
|
| requestState(): void { this.socket.emit('doc:request-state', { fileId: this.fileId }); } |
| getText(): string { return this.text.toString(); } |
| isRemoteUpdate(): boolean { return this.isLocalUpdate; } |
|
|
| applyMonacoChanges(changes: Array<{ rangeOffset: number; rangeLength: number; text: string }>): void { |
| this.doc.transact(() => { |
| const sorted = [...changes].sort((a, b) => b.rangeOffset - a.rangeOffset); |
| for (const change of sorted) { |
| if (change.rangeLength > 0) this.text.delete(change.rangeOffset, change.rangeLength); |
| if (change.text) this.text.insert(change.rangeOffset, change.text); |
| } |
| }); |
| } |
|
|
| observe(callback: (event: Y.YTextEvent) => void): void { this.text.observe(callback); } |
|
|
| destroy(): void { |
| this.doc.destroy(); |
| this.socket.off('doc:sync'); |
| this.socket.off('doc:state'); |
| } |
| } |
|
|
| export { Y }; |
|
|