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

Phoenix Channel for Yjs collaborative editing via TipTap.

Implements the Yjs sync protocol over Phoenix Channels, replacing the need
for a separate Hocuspocus (Node.js) server. Each document gets its own
channel topic: `"editor:collab:<doc_id>"`.

## Protocol Messages

### Client → Server
- `"yjs:sync-step-1"` — client sends its state vector, server responds with diff
- `"yjs:sync-step-2"` — client sends its diff to server
- `"yjs:update"`       — client sends incremental update, server broadcasts + persists
- `"awareness:update"` — client sends cursor/selection awareness, server broadcasts

### Collaboration Messages (Client → Server)
- `"collab:thread:create"` — create a new comment thread anchored to a selection
- `"collab:thread:resolve"` — mark a thread as resolved
- `"collab:thread:reopen"` — reopen a resolved thread
- `"collab:comment:create"` — add a comment to a thread
- `"collab:comment:edit"` — edit an existing comment
- `"collab:comment:delete"` — delete a comment
- `"collab:comment:react"` — toggle a reaction on a comment
- `"collab:cursor:update"` — broadcast cursor position to peers
- `"collab:typing:start"` — broadcast typing indicator on
- `"collab:typing:stop"` — broadcast typing indicator off
- `"collab:version:snapshot"` — trigger a version snapshot

### Server → Client
- `"yjs:sync-step-1"` — server sends its state vector (on join)
- `"yjs:sync-step-2"` — server sends diff in response to client sync-step-1
- `"yjs:update"`       — server broadcasts updates from other clients
- `"awareness:update"` — server broadcasts awareness from other clients
- `"collab:thread:created"` / `"collab:thread:resolved"` / `"collab:thread:reopened"`
- `"collab:comment:created"` / `"collab:comment:edited"` / `"collab:comment:deleted"` / `"collab:comment:reacted"`
- `"collab:cursor:updated"` — peer cursor positions
- `"collab:typing:updated"` — peer typing state
- `"collab:version:saved"` — version snapshot confirmation

## Setup

In your endpoint/socket:

    channel "editor:collab:*", PhiaUi.Editor.CollabChannel

In your app.js (with Yjs):

    npm install yjs @tiptap/extension-collaboration @tiptap/extension-collaboration-cursor

## Persistence

Override `c:load_document/1` and `c:save_document/2` callbacks in your own
channel module to persist Y.Doc state to your database:

    defmodule MyApp.EditorChannel do
      use PhiaUi.Editor.CollabChannel

      @impl true
      def load_document(doc_id) do
        case MyApp.Repo.get(MyApp.Document, doc_id) do
          nil -> {:ok, nil}
          doc -> {:ok, doc.yjs_state}
        end
      end

      @impl true
      def save_document(doc_id, state) do
        MyApp.Repo.insert_or_update!(...)
        :ok
      end

      @impl true
      def load_threads(doc_id) do
        threads = MyApp.Repo.all(from t in MyApp.Thread, where: t.doc_id == ^doc_id)
        {:ok, threads}
      end
    end

# `delete_comment`
*optional* 

```elixir
@callback delete_comment(doc_id :: String.t(), comment_id :: String.t()) ::
  :ok | {:error, term()}
```

Delete a comment by ID.

# `delete_thread`
*optional* 

```elixir
@callback delete_thread(doc_id :: String.t(), thread_id :: String.t()) ::
  :ok | {:error, term()}
```

Delete a comment thread by ID.

# `load_document`

```elixir
@callback load_document(doc_id :: String.t()) :: {:ok, binary() | nil} | {:error, term()}
```

Load persisted Y.Doc state for a document.

Returns `{:ok, binary | nil}` where binary is the Yjs-encoded document state.
Return `{:ok, nil}` for new documents.

# `load_threads`
*optional* 

```elixir
@callback load_threads(doc_id :: String.t()) :: {:ok, list()} | {:error, term()}
```

Load all comment threads for a document.

# `load_versions`
*optional* 

```elixir
@callback load_versions(doc_id :: String.t()) :: {:ok, list()} | {:error, term()}
```

Load all version snapshots for a document.

# `save_comment`
*optional* 

```elixir
@callback save_comment(doc_id :: String.t(), comment :: map()) ::
  {:ok, map()} | {:error, term()}
```

Persist a new or updated comment.

# `save_document`

```elixir
@callback save_document(doc_id :: String.t(), state :: binary()) :: :ok | {:error, term()}
```

Persist Y.Doc state for a document.

Called after each `yjs:update` message. The `state` is the full Yjs-encoded
document state (binary).

# `save_thread`
*optional* 

```elixir
@callback save_thread(doc_id :: String.t(), thread :: map()) ::
  {:ok, map()} | {:error, term()}
```

Persist a new or updated comment thread.

# `save_version`
*optional* 

```elixir
@callback save_version(doc_id :: String.t(), version :: map()) ::
  {:ok, map()} | {:error, term()}
```

Persist a version snapshot.

---

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