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

In-memory authoritative state for PixelWeaver projects.

#server.src.pixelweaver.state

#server.src.pixelweaver.state

In-memory authoritative state for PixelWeaver projects.

The server is the single source of truth. All state mutations flow through ServerState, which is then persisted to disk by the auto-saver.

#PartRegistryEntry

One entry in the project-scoped part registry (mirrors PartDefinition).

Internal keys are snake_case; JSON emission (to_dict / patches) uses camelCase for JS consumers. The variant list is derived, never stored.

#VariantPreset

One variant preset (a named set of per-group palette swaps).

A faithful mirror of the client's serialized VariantPreset shape (variant-state.svelte.ts SerializedPreset): groupOverrides maps a layer-group id to a color map (original hex -> replacement hex). The stored shape is the client-canonical camelCase one -- like the manifest / level map, the raw dict is kept verbatim so it round-trips byte-exact. The active-preset selection is a transient client-only preview toggle and is NOT stored here.

#LevelTilePlacement

One tile placement / the active-tile brush (mirror of TilePlacement).

#LevelEntity

A placed entity on an entity layer (mirror of EntityPlacement).

#LevelCollisionPoint

One vertex of a collision shape's polygon (mirror of a client collision point).

#LevelCollisionShape

A collision rect/polygon (mirror of CollisionShape).

#LevelLayer

One map layer (mirror of SerializedLayer). The per-kind collections are optional -- a tile layer carries tiles, an entity layer entities, a collision layer collisionShapes (the client omits the others).

#LevelMap

The whole level-editor document (mirror of SerializedMapState).

NOTE: activeTile is deliberately absent. The selected brush is per-user tool state (like the client-local foreground color), NOT authoritative document state -- syncing one user's brush to everyone would be wrong. It lives only in the client map-state singleton and the client's own local project-save format; the server round-trip does not carry it. set_level_map strips it from any legacy save that still has it (see below).

#default_level_map

python
def default_level_map() -> dict[str, Any]

The empty level document (mirror of the client's initial map-state).

Absent level_map on load resolves to this -- the migration for projects created before the level editor became server-authoritative. activeTile is client-local (see LevelMap) and is intentionally not present here.

#FrameState

Single animation frame within a canvas.

Each frame holds its own pixel data per layer. The id is a stable UUID that matches the frontend Frame.id. duration_ms=None means "derive from global_fps".

#CanvasState

State for a single canvas within a project.

#set_iso_settings

python
def set_iso_settings(self, tile_width: int, tile_height: int, footprint_cols: int, footprint_rows: int) -> None

Validate and set this canvas's iso settings (all-or-nothing).

Hard errors (validate everything before setting anything): - tile_width/tile_height must be even integers >= 2. - footprint_cols/footprint_rows must be integers >= 1.

#current_frame

python
def current_frame(self) -> FrameState

Return the currently selected frame (bounds-checked).

#frame_at

python
def frame_at(self, index: int) -> FrameState

Return frame at the given index (bounds-checked).

#frame_count

python
def frame_count(self) -> int

Number of frames in this canvas.

#to_dict

python
def to_dict(self) -> dict[str, Any]

Serialize to a JSON-compatible dict (excludes heavy pixel data).

#to_full_dict

python
def to_full_dict(self) -> dict[str, Any]

Serialize including info about which layers have pixel data.

#ProjectState

State for a single project.

#set_variant_presets

python
def set_variant_presets(self, presets: list[dict[str, Any]] | None) -> None

Validate and set the variant preset list (None/absent = empty list).

Structurally validates each entry against VariantPreset (raises pydantic ValidationError on malformed data -- a hard error, never a silent fallback), enforces unique ids, then stores the RAW dicts unchanged so the client-canonical shape round-trips byte-exact (mirror of set_level_map). None (an absent save field) resolves to the empty list -- the migration for pre-variant-authoritative projects.

#set_current_roles

python
def set_current_roles(self, foreground_role: int, background_role: int) -> None

Validate and set the current foreground/background roles.

Roles must be integers in [0, 254] -- the 0xFF sentinel is never a current role. Hard error on invalid values (no silent defaults for present-and-invalid data); both values validate before either is set.

#set_level_map

python
def set_level_map(self, level_map: dict[str, Any] | None) -> None

Validate and set the level-editor document (None = empty document).

Structurally validates against LevelMap (raises pydantic ValidationError on malformed data -- a hard error, never a silent fallback), then stores the RAW dict unchanged so the client-canonical shape round-trips byte-exact (mirror of set_manifest storing the raw manifest). None (an absent save field) resolves to the empty document -- the migration for pre-level-editor projects.

#set_manifest

python
def set_manifest(self, manifest: dict[str, Any] | None) -> None

Validate and set the constraint manifest (None clears it).

Raises pydantic.ValidationError for invalid manifests -- a hard error, never a silent fallback.

#set_character_preview

python
def set_character_preview(self, doc: dict[str, Any] | None) -> None

Validate and set the character-preview document (None clears it).

Raises pydantic.ValidationError for invalid documents -- a hard error, never a silent fallback (mirror of set_manifest).

#set_part_registry

python
def set_part_registry(self, entries: list[dict[str, Any]]) -> None

Validate and set the part registry (mirror of set_manifest).

Validates each entry, then uniqueness of id/name/z_order across entries, then class resolution for every non-null palette_class_id against the current manifest. All hard errors -- never a silent fallback. Only assigns once every check passes.

#_part_registry_json

python
def _part_registry_json(self) -> list[dict[str, Any]]

Serialize the part registry with camelCase keys for JS consumers.

#_set_members

python
def _set_members(self, part_set: str, except_name: str) -> list[CanvasState]

Members of part_set OTHER than except_name.

#set_canvas_part_metadata

python
def set_canvas_part_metadata(self, canvas_name: str, part_set: str, part_name: str, variant_index: int, paired_with: str | None=None) -> None

Set a canvas's part-set metadata with set-wide invariant checks.

Hard errors (no silent coercion): - variant_index must be a non-negative int; part_set/part_name non-empty. - paired_with (when given) must name an existing, different canvas. - all members of a set share dimensions (width/height) and origin (origin_x/origin_y). - two members may not share the same (part_name, variant_index).

#clear_canvas_part_metadata

python
def clear_canvas_part_metadata(self, canvas_name: str) -> None

Clear a canvas's four part fields (does not touch the partner).

#pair_canvases

python
def pair_canvases(self, name_a: str, name_b: str) -> None

Pair two index-locked canvases in different sets reciprocally.

Hard errors: unknown names, name_a == name_b, either already paired, either variant_index None or unequal, either part_set None or equal.

#unpair_canvases

python
def unpair_canvases(self, name: str) -> None

Clear a canvas's pairing on both sides. Hard errors if not paired.

#to_dict

python
def to_dict(self) -> dict[str, Any]

Serialize to a JSON-compatible dict for state sync.

#ServerState

In-memory authoritative state for all projects.

#create_project

python
def create_project(self, name: str, width: int, height: int) -> ProjectState

Create a new project with a default canvas and layer.

#get_project

python
def get_project(self, name: str) -> ProjectState | None

Get a project by name.

#get_active_project

python
def get_active_project(self) -> ProjectState | None

Get the currently active project.

#set_active_project

python
def set_active_project(self, name: str) -> None

Validate and set the active project by name.

The single validate-and-set path shared by the REST activate endpoint and the MCP open_project tool. Raises KeyError if no project with that name exists. This is navigation only -- it does not mutate project content, so callers do not mark the auto-saver dirty.

The active project IS persisted, but as a lightweight pointer (not project content): the collab-server navigation choke points (the REST activate endpoint, the create endpoint, and the MCP state-sync handler) write an active_project.json marker via storage.save_active_project_marker, and the lifespan startup restores it. This method stays pure (no I/O) so unit tests and the MCP-process ServerState can call it without touching the data dir.

#delete_project

python
def delete_project(self, name: str) -> bool

Delete a project. Returns True if it existed.

#list_projects

python
def list_projects(self) -> list[str]

Return names of all projects.

#_serialize_layer

python
def _serialize_layer(layer: dict[str, Any]) -> dict[str, Any]

Serialize one layer node for a full-state patch, recursing into groups.

Matches the client's serialized layer shape exactly: a GROUP carries type: "group", a recursively-serialized children list, and an expanded flag; a LEAF carries the pixel/role metadata. Both kinds carry the common id/name/visible/opacity/blendMode/locked/offset fields.

#build_full_state_patch

python
def build_full_state_patch(project: ProjectState, *, replace: bool=False) -> dict[str, Any]

Build a full-state patch from the current project state.

Serializes all canvas data including per-frame pixel buffers as base64 strings. Used when MCP modifies state and needs to notify WebSocket clients. Keys are camelCase for JS consumers.

replace is the EXPLICIT authoritative-replace signal. It is False for MCP mutations of the same project (the client MERGES the patch) and True only for a PROJECT SWITCH (the activate path), where the snapshot is the whole truth: the client rebuilds each canvas's layer tree, drops canvases the snapshot omits, and repoints the active canvas. Never a heuristic -- mode selection set by the caller.

#validate_part_sets

python
def validate_part_sets(project: ProjectState) -> list[str]

Validate pairing completeness across all part-sets in a project.

Line-for-line port of the frontend validatePartSets (same message wording), operating on the server model where canvases are keyed by name and paired_with stores the partner's NAME.

Checks: - every paired_with target exists and pairs back (reciprocal-consistent) - paired canvases are index-locked (equal variant_index) - two sets connected by any pairing are index-complete

Search