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" }