- Create TypeScript and Vite configuration for SolidJS - Implement Yjs document structure for screenplay collaboration - Build WebSocket connection manager with exponential backoff reconnection - Create CRDT document manager with undo/redo support - Set up WebSocket sync server with JWT authentication - Add SolidJS reactive bindings for Yjs shared types - Build collaborative editor component - Write unit tests for CRDT operations - Document implementation in analysis/fre600_websocket_foundation.md Architecture: Yjs chosen over Automerge for better ecosystem and Tauri compatibility. WebSocket for sync, WebRTC for video. Co-Authored-By: Paperclip <noreply@paperclip.ing>
95 lines
2.3 KiB
TypeScript
95 lines
2.3 KiB
TypeScript
/**
|
|
* 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<ScreenplayMetadata>
|
|
): 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<ScreenplayMetadata>('metadata');
|
|
const characters = doc.getMap<CharacterRef>('characters');
|
|
const scenes = doc.getMap<SceneMeta>('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.toJSON().length === 0) {
|
|
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<ScreenplayMetadata>('metadata'),
|
|
characters: doc.getMap<CharacterRef>('characters'),
|
|
scenes: doc.getMap<SceneMeta>('scenes'),
|
|
};
|
|
}
|