nexus-scribe-collaboration

Real-time collaboration engine for multi-user transcript editing.

Overview

Enables concurrent editing with:

  • WebSocket-based real-time communication
  • Operational Transformation (OT) for conflict-free editing
  • User presence and cursor tracking
  • Real-time comment threading
  • Document state synchronization

Configuration

use nexus_scribe_collaboration::CollaborationConfig;

let config = CollaborationConfig {
    max_connections_per_meeting: 100,
    operation_buffer_size: 1000,
    heartbeat_interval_secs: 30,
    idle_timeout_secs: 300,
};

Collaboration Service

Main service managing all sessions:

use nexus_scribe_collaboration::CollaborationService;

let service = CollaborationService::new(config, db)?;

// Join a meeting
let (session, receiver) = service.join_meeting(
    meeting_id,
    user_id,
    "John Doe".to_string(),
).await?;

// Leave meeting
service.leave_meeting(meeting_id, user_id).await?;

Meeting Sessions

use nexus_scribe_collaboration::MeetingSession;

let session = MeetingSession::new(meeting_id, initial_content);

// Add user
let rx = session.add_connection(user_id, "Alice".to_string());

// Get document state
let doc = session.get_document().await;

// Active user count
let count = session.active_user_count();

Operational Transformation

Conflict-free concurrent editing:

use nexus_scribe_collaboration::Operation;

// Apply operation with automatic transformation
session.apply_operation(operation).await?;

Operations are transformed against the operation history to ensure convergence.

Operations

use nexus_scribe_collaboration::{Operation, OperationType};

let insert_op = Operation::new(
    user_id,
    OperationType::Insert {
        position: 10,
        text: "Hello".to_string(),
    },
    version,
);

let delete_op = Operation::new(
    user_id,
    OperationType::Delete {
        position: 5,
        length: 3,
    },
    version,
);

Real-time Messages

#[derive(Clone, Serialize, Deserialize)]
pub enum CollaborationMessage {
    Operation { operation: Operation, user_id: Uuid },
    UserJoined { user_id: Uuid, username: String },
    UserLeft { user_id: Uuid },
    CursorUpdate { user_id: Uuid, position: CursorPosition },
    CommentAdded { comment: Comment, user_id: Uuid },
}

Presence Tracking

use nexus_scribe_collaboration::{PresenceTracker, CursorPosition};

// Update cursor position
service.update_cursor(
    meeting_id,
    user_id,
    CursorPosition { line: 10, column: 5 },
).await?;

// Get all users in session
let info = service.get_session_info(meeting_id).await;
for user in info.active_users {
    println!("{}: cursor at {:?}", user.username, user.cursor);
}

Comments

use nexus_scribe_collaboration::Comment;

let comment = Comment {
    id: Uuid::new_v4(),
    user_id,
    segment_id,
    text: "This needs clarification".to_string(),
    timestamp: Utc::now().timestamp(),
    parent_id: None,  // For replies
};

service.add_comment(meeting_id, user_id, comment).await?;

Broadcasting

// Send to all users in session
session.broadcast(CollaborationMessage::UserJoined {
    user_id,
    username: "Bob".to_string(),
})?;

Document State

pub struct Document {
    pub content: String,
    pub version: u64,
}

Session Info

pub struct SessionInfo {
    pub meeting_id: Uuid,
    pub active_users: Vec<UserPresence>,
    pub user_count: usize,
}

pub struct UserPresence {
    pub user_id: Uuid,
    pub username: String,
    pub cursor: Option<CursorPosition>,
    pub last_active: i64,
}

WebSocket Integration

// In WebSocket handler
let (session, mut rx) = service.join_meeting(meeting_id, user_id, username).await?;

// Send initial document state
ws.send(session.get_document().await)?;

// Forward messages to client
tokio::spawn(async move {
    while let Ok(msg) = rx.recv().await {
        ws.send(serde_json::to_string(&msg)?).await?;
    }
});

// Handle incoming operations
while let Some(msg) = ws.recv().await {
    let operation: Operation = serde_json::from_str(&msg)?;
    service.broadcast_edit(meeting_id, operation).await?;
}

Usage

[dependencies]
nexus-scribe-collaboration = { path = "../nexus-scribe-collaboration" }