/** * Real-time collaboration. Presence (who else is here + where their cursor * is) and live edits (a change in one client lands in every other), over a * pluggable transport. The transport is the only thing tied to infrastructure * - swap `broadcastChannelTransport` (same-browser tabs, zero backend) for a * WebSocket / WebRTC / CRDT adapter and the controller is unchanged. * * This is also the natural "multiple agents on one grid" substrate: an AI * agent is just another peer posting `edit` messages. */ export type CollabUser = { id: string; name: string; /** A CSS color for this user's cursor / avatar. */ color: string; }; export type CollabCell = { rowId: string; columnId: string; }; export type CollabPresence = CollabUser & { cell: CollabCell | null; /** Last time we heard from this peer (ms). Used to prune the gone. */ ts: number; }; export type CollabMessage = { kind: 'hello'; user: CollabUser; } | { kind: 'presence'; user: CollabUser; cell: CollabCell | null; } | { kind: 'edit'; user: CollabUser; rowId: string; columnId: string; value: unknown; } | { kind: 'bye'; userId: string; }; export type CollabTransport = { post(msg: CollabMessage): void; subscribe(handler: (msg: CollabMessage) => void): () => void; /** Release any underlying resource (e.g. a BroadcastChannel). Optional. */ dispose?(): void; }; export type Collaboration = { /** Broadcast where this user's cursor is (or null when it leaves). */ setCell(cell: CollabCell | null): void; /** Broadcast a cell edit to every peer. */ sendEdit(rowId: string, columnId: string, value: unknown): void; /** The peers currently present (excludes self). */ peers(): CollabPresence[]; dispose(): void; }; export type CollaborationOptions = { user: CollabUser; transport: CollabTransport; /** Fired (with self excluded) whenever the peer set or a cursor changes. */ onPeersChange?: (peers: CollabPresence[]) => void; /** Fired when another user edits a cell - apply it to your data. */ onRemoteEdit?: (edit: { rowId: string; columnId: string; value: unknown; user: CollabUser; }) => void; /** Drop peers we haven't heard from in this many ms. Default 15000. */ peerTimeoutMs?: number; }; /** BroadcastChannel transport - live across tabs of the same browser, no * backend. No-ops where BroadcastChannel is unavailable (SSR / old env). */ export declare function broadcastChannelTransport(name: string): CollabTransport; export declare function createCollaboration(options: CollaborationOptions): Collaboration;