PixelWeaver v0.5.0 /server.src.pixelweaver.websocket
On this page

WebSocket message handler for PixelWeaver.

#server.src.pixelweaver.websocket

#server.src.pixelweaver.websocket

WebSocket message handler for PixelWeaver.

Routes incoming messages to the appropriate handler based on type, validates them through the protocol models, and coordinates responses/broadcasts.

#handle_message

python
async def handle_message(websocket: WebSocket, raw: dict[str, Any], state: ServerState, manager: ConnectionManager, on_dirty: DirtyCallback=None) -> None

Dispatch a parsed JSON message from a client.

Never raises -- all errors are sent back to the client as error messages.

#_handle_command

python
async def _handle_command(websocket: WebSocket, msg: CommandMessage, state: ServerState, manager: ConnectionManager, on_dirty: DirtyCallback=None) -> None

Validate, APPLY to authoritative state, store in history, ack, broadcast.

Every arriving command takes exactly ONE of two paths -- there is no silent fall-through and no escape hatch:

  1. Covered (apply_ws_command materialized it): the mutation is stored in

authoritative pixel/role/structure state, recorded in history (riding its undo record), acked, and a state_patch is broadcast so other clients repaint from authoritative state.

  1. Not covered: REJECTED with command_reject and NOT recorded in history.

An applier error (a covered command that fails to apply) is also a rejection and does NOT touch history.

#_handle_sync_request

python
async def _handle_sync_request(websocket: WebSocket, msg: Any, state: ServerState, manager: ConnectionManager) -> None

Restore the active project on the REQUESTING socket only.

Repurposed handshake: the client sends sync_request on every socket open (initial connect AND reconnect -- one path). Under the mutation lock (so the snapshot is never a torn read against a concurrent MCP mutation) we build an authoritative-replace full_state snapshot and send it -- as the same binary state_patch frame broadcasts use -- to the requester alone.

A fresh server with no active project sends NOTHING: the client's canvas simply stays empty, which is the correct fresh-server state and needs no new message type.

#_handle_undo

python
async def _handle_undo(websocket: WebSocket, msg: UndoMessage, state: ServerState, manager: ConnectionManager, on_dirty: DirtyCallback=None) -> None

Pop last command, REVERT its authoritative buffers, push onto redo.

When the client sends command_id (the id of the command it just undid locally), the server verifies it matches the id at the top of its command history BEFORE reverting. A mismatch means the histories diverged -- the undo is rejected loudly (command_reject) and nothing is reverted, so the client surfaces the divergence instead of silently reverting a stale edit.

#_handle_redo

python
async def _handle_redo(websocket: WebSocket, msg: RedoMessage, state: ServerState, manager: ConnectionManager, on_dirty: DirtyCallback=None) -> None

Pop last undone command, REAPPLY its authoritative buffers, restore it.

Like undo, when the client sends command_id the server verifies it matches the top of its redo stack before reapplying; a mismatch is rejected loudly instead of reapplying a diverged command.

#broadcast_patches

python
async def broadcast_patches(connections: ConnectionManager, project_name: str, patches: list[dict[str, Any]], command_id: str | None=None, exclude_ws: WebSocket | None=None) -> None

Broadcast data patches to all connected WebSocket clients.

After MCP modifies server state, call this with the resulting patches so every frontend can apply the changes. exclude_ws allows skipping the originating client (e.g. the one that sent a frontend command -- it already applied the mutation locally).

#broadcast_full_state

python
async def broadcast_full_state(state: ServerState, connections: ConnectionManager, project_name: str | None=None, command_id: str | None=None, exclude_ws: WebSocket | None=None, replace: bool=False) -> None

Build a full-state patch from the active project and broadcast it.

Convenience wrapper combining build_full_state_patch + broadcast_patches. Used by MCP tools after they mutate pixel data / layers / canvas.

replace threads the EXPLICIT authoritative-replace signal onto the emitted patch. It stays False for MCP mutations (merge) and is True only for a PROJECT SWITCH (the activate path), so switched-to clients rebuild their layer tree, drop stale canvases, and repoint the active canvas.

Search