/** * Yjs document structure for screenplay collaboration * Defines the shared types used in the CRDT document */ import { Doc, Text, Map as YMap, Array as YArray } from 'yjs'; /** * Screenplay metadata stored in Yjs Map */ export interface ScreenplayMetadata { title: string; author: string; projectId: string; createdAt: string; updatedAt: string; version: number; } /** * Character reference for the screenplay */ export interface CharacterRef { id: string; name: string; shortName: string; description?: string; } /** * Scene metadata */ export interface SceneMeta { id: string; slugline: string; startTime: number; // Position in document duration: number; } /** * Create a new Yjs document with the screenplay structure */ export function createScreenplayDoc( projectId: string, metadata: Partial ): Doc { const doc = new Doc(); // Apply updates from remote clients doc.on('update', (update: Uint8Array, origin: unknown) => { // Origin can be 'local', 'remote', or a WebSocket provider const isLocal = origin === 'local'; if (!isLocal) { console.log(`Received update from ${origin}`); } }); // Initialize shared types const text = doc.getText('main'); const meta = doc.getMap('metadata'); const characters = doc.getMap('characters'); const scenes = doc.getMap('scenes'); // Set default metadata const defaultMeta: ScreenplayMetadata = { title: metadata.title || 'Untitled Screenplay', author: metadata.author || '', projectId, createdAt: metadata.createdAt || new Date().toISOString(), updatedAt: metadata.updatedAt || new Date().toISOString(), version: metadata.version || 1, }; // Initialize metadata if empty if (!meta.get('projectId')) { Object.entries(defaultMeta).forEach(([key, value]) => { meta.set(key as keyof ScreenplayMetadata, value); }); } return doc; } /** * Get or create a shared type from the document */ export function getOrCreateSharedTypes(doc: Doc) { return { text: doc.getText('main'), metadata: doc.getMap('metadata'), characters: doc.getMap('characters'), scenes: doc.getMap('scenes'), }; }