# Collaboration

Real-time multiplayer via **centralized OT** (markloom `rebase` / `makeBias`) and a **Cloudflare Worker** Durable Object room (same WebSocket pattern as the chat repo: `acceptWebSocket` + fan-out).

## Client (copy-paste)

```bash
npm i markloom
# run the worker (below) or point url at a deployed room host
```

```ts
import { Editor } from 'markloom/view'
import 'markloom/view/theme.css'

new Editor(el, doc, {
  collab: {
    url: 'ws://127.0.0.1:8787', // or wss://your-worker.workers.dev
    room: 'demo-room',
    name: 'Alice',
  },
})
```

That is enough: local edits upload as Steps; remote Steps apply through OT. No extension list.

**Presence (built-in, Feishu-style):** top-right avatar stack (self + peers) and colored remote carets with name labels. Colors auto-assign by `siteId` unless you set `color`. No extra config.

Optional: `color`, `name`, `seed` (empty room init; default = constructor `doc`).

Programmatic: `ed.collab` → `CollabClient` (`peersList`, `close()`).

## Worker

```bash
cd worker
bun install   # or npm i
bun run dev   # wrangler dev → ws://127.0.0.1:8787
```

- `GET /room/:id` + WebSocket upgrade → one Durable Object per room name  
- Snapshot persisted in DO storage; op log in memory (stale base → `resync`)

Deploy: `bun run deploy` (Cloudflare account + `wrangler login`).

## Protocol (summary)

| Direction | Types |
|-----------|--------|
| C→S | `join`, `op`, `presence`, `ping` |
| S→C | `welcome`, `op`, `presence`, `peer-join` / `peer-leave`, `resync`, `error` |

See `src/collab/protocol.ts`.

## Limits (honest)

- **TP2** (reordered remote undo chains) not proven; server serializes commits so common concurrent typing is covered by TP1 + central order.  
- Local **undo** interleaved with remote may need a later resync.  
- No auth: room id is the capability.  
- Presence is on the wire; rich multi-caret chrome is optional product UI.

## Lab

```bash
# terminal 1
cd worker && bun run dev
# terminal 2
cd .. && bun run lab
# open two windows: http://localhost:5181/?room=demo
```
