# `PhiaUi.Editor.CollabServer`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/editor/collab_server.ex#L1)

GenServer managing a single collaborative document via Operational Transform.

Each document gets its own `CollabServer` process that maintains the
authoritative document state, version counter, and operation history.
Clients submit operations along with their last-known version; the server
transforms against any intervening operations before applying.

## Architecture

```
Client A ──apply_op──> CollabServer ──broadcast──> PubSub ──> Client B
Client B ──apply_op──> CollabServer ──broadcast──> PubSub ──> Client A
```

## State

  - `:doc_id` — unique document identifier
  - `:content` — current document text (string)
  - `:version` — monotonically increasing integer (starts at 0)
  - `:history` — list of `{version, delta, client_id}` tuples (newest first)
  - `:clients` — `MapSet` of connected client identifiers

## Usage

    # Start a server for a document
    {:ok, pid} = PhiaUi.Editor.CollabServer.start_link(doc_id: "doc-123")

    # Client submits an operation
    {:ok, transformed_op, new_version} =
      PhiaUi.Editor.CollabServer.apply_op(pid, [%{retain: 5}, %{insert: "!"}], 0)

    # Read current state
    {"Hello!", 1} = PhiaUi.Editor.CollabServer.get_document(pid)

## PubSub Broadcasting

After each successful operation, the server broadcasts on
`"collab:doc:<doc_id>"` via `Phoenix.PubSub` (configured in
`:phia_ui, :collab_pubsub`). The broadcast payload is:

    %{
      event: :op_applied,
      doc_id: doc_id,
      op: transformed_delta,
      version: new_version,
      client_id: client_id
    }

## History Pruning

History is capped at 1000 entries. Older entries are discarded to
bound memory usage. Clients that fall behind by more than 1000 versions
must re-fetch the full document.

# `t`

```elixir
@type t() :: %PhiaUi.Editor.CollabServer{
  clients: MapSet.t(),
  content: String.t(),
  doc_id: String.t(),
  history: [{non_neg_integer(), list(), String.t() | nil}],
  version: non_neg_integer()
}
```

# `apply_op`

```elixir
@spec apply_op(GenServer.server(), list(), non_neg_integer(), String.t() | nil) ::
  {:ok, list(), non_neg_integer()} | {:error, term()}
```

Submit a delta operation from a client.

The `client_version` is the version the client's delta was composed against.
The server transforms the delta against all operations that occurred between
`client_version` and the current server version, then applies the result.

Returns `{:ok, transformed_delta, new_version}` on success, or
`{:error, reason}` if the operation cannot be applied.

## Parameters

  - `server` — pid or registered name
  - `delta` — list of OT operations
  - `client_version` — the version the client is based on
  - `client_id` — optional identifier for the submitting client

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `get_clients`

```elixir
@spec get_clients(GenServer.server()) :: MapSet.t()
```

Get the set of connected client identifiers.

# `get_document`

```elixir
@spec get_document(GenServer.server()) :: {String.t(), non_neg_integer()}
```

Get the current document content and version.

Returns `{content, version}`.

# `get_ops_since`

```elixir
@spec get_ops_since(GenServer.server(), non_neg_integer()) ::
  {:ok, [{non_neg_integer(), list(), String.t() | nil}]}
  | {:error, :version_too_old}
```

Get operations from the history since a given version.

Returns a list of `{version, delta, client_id}` tuples in ascending
version order. If the requested version is older than the oldest entry
in history, returns `{:error, :version_too_old}`.

# `get_version`

```elixir
@spec get_version(GenServer.server()) :: non_neg_integer()
```

Get the current document version.

# `join`

```elixir
@spec join(GenServer.server(), String.t()) :: :ok
```

Register a client as connected to this document.

Used for tracking active collaborators. Purely informational.

# `leave`

```elixir
@spec leave(GenServer.server(), String.t()) :: :ok
```

Unregister a client from this document.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Start a `CollabServer` process for a document.

## Options

  - `:doc_id` (required) — unique document identifier
  - `:initial_content` — starting document text (default `""`)
  - `:name` — process name (defaults to `via_tuple(doc_id)` using the
    `PhiaUi.Collab.RoomRegistry` if available, otherwise the pid)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
