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

The single validated mutation path for project and canvas metadata: constraint manifest, part registry, part-set pairing, and isometric settings, with undo records.

#server.src.pixelweaver.metadata_ops

#server.src.pixelweaver.metadata_ops

Shared, schema-neutral project/canvas metadata mutations.

Companion to structural_ops (layers/frames): this module owns the ONE mutation path for project- and canvas-level metadata -- the constraint manifest, the part registry, canvas part-set membership + pairing, and the isometric document settings. Both the bespoke MCP handlers and the browser command applier route through these functions so the authoritative state stays identical regardless of which client mutated it (previously the browser path fell through to a history-only append and the metadata was silently lost on reload).

Every function here:

- takes an already-resolved ProjectState (and, where relevant, a resolved canvas) plus plain, schema-neutral values, - performs the mutation through the validated setters on the state model (set_manifest / set_part_registry / set_canvas_part_metadata / set_iso_settings / pair_canvases / ...), so an invalid mutation is a hard error, never a silent partial write, - returns a JSON-serializable meta-snapshot undo record (op == "meta_snapshot") describing how to revert (undo) and re-apply (redo) the change. The record rides the command-history entry exactly like the structural records and is applied by undo_history.

A meta-snapshot record captures, for each affected attribute, its before and after value. target is the sentinel "__project__" for project-level attributes or a canvas name for canvas-level attributes. Values are deep-copied JSON (dicts/lists/scalars) so undo/redo restores an independent object, never an alias of the live state.

#resolve_canvas

python
def resolve_canvas(project: ProjectState, canvas_id: str) -> CanvasState

Resolve a canvas by its client id (== server canvas name); hard error.

#set_manifest

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

Validate + set the constraint manifest. Return a meta-snapshot record.

#apply_manifest_transform

python
def apply_manifest_transform(project: ProjectState, manifest: dict[str, Any] | None, remaps: list[dict[str, Any]] | None) -> tuple[dict[str, Any], dict[str, int]]

Swap the constraint manifest AND remap painted role bytes (ONE path).

Shared by the MCP transform handler and the browser command applier. A faithful port of the client apply_manifest_transform: for every role layer whose class has a remap, rewrite each frame's role plane by roleMap[byte] (the transparent sentinel is never touched), then swap in the new manifest.

All validation runs BEFORE any mutation (planes are staged on copies), so a bad manifest, an out-of-range remap entry, an orphaned class, a stranded painted byte, or a corrupt plane is a hard ValueError that leaves state untouched. Callers shape the error (MCP -> structured dict; applier -> CommandApplyError).

Returns (record, stats) where record is a COMPOUND undo record (the manifest meta_snapshot followed by one role before/after record per changed plane) applied in order on redo and reversed on undo, and stats is {"planes_remapped", "pixels_remapped"}.

#define_part

python
def define_part(project: ProjectState, *, name: str, part_set: str, z_order: int, palette_class_id: str | None, part_id: str | None=None) -> dict[str, Any]

Append a new part-registry entry (validated). Return an undo record.

part_id is the CLIENT-generated id when present so server and client ids never diverge; a fresh uuid is minted only when the client omits it.

#update_part

python
def update_part(project: ProjectState, *, part_id: str, updates: dict[str, Any]) -> dict[str, Any]

Patch fields of an existing part entry (validated). Return undo record.

updates uses the internal snake_case keys (name/part_set/z_order/ palette_class_id); only present keys are changed.

#remove_part

python
def remove_part(project: ProjectState, part_id: str) -> dict[str, Any]

Remove a part entry by id (validated). Return undo record.

#move_part

python
def move_part(project: ProjectState, part_id: str, direction: str) -> dict[str, Any]

Swap a part's z_order with its adjacent neighbour (mirrors the client).

direction is "up" (towards a lower z_order) or "down" (higher). Parts are ordered ascending by z_order; the target and its neighbour swap z_orders in a single undoable step. No-op (still records) when there is no neighbour.

#assign_canvas_part

python
def assign_canvas_part(project: ProjectState, *, canvas_id: str, part_set: str, part_name: str, variant_index: int) -> dict[str, Any]

Set a canvas's part-set metadata (validated). Return undo record.

#clear_canvas_part

python
def clear_canvas_part(project: ProjectState, canvas_id: str) -> dict[str, Any]

Clear a canvas's part-set metadata. Return undo record.

#pair_canvases

python
def pair_canvases(project: ProjectState, canvas_id_a: str, canvas_id_b: str) -> dict[str, Any]

Pair two canvases reciprocally (validated). Return undo record.

#unpair_canvases

python
def unpair_canvases(project: ProjectState, canvas_id: str) -> dict[str, Any]

Clear a canvas's pairing on both sides (validated). Return undo record.

#set_iso_tile_size

python
def set_iso_tile_size(project: ProjectState, *, canvas_id: str, tile_width: int, tile_height: int) -> dict[str, Any]

Set a canvas's iso tile size (validated). Return undo record.

#set_iso_footprint

python
def set_iso_footprint(project: ProjectState, *, canvas_id: str, cols: int, rows: int) -> dict[str, Any]

Set a canvas's iso footprint (validated). Return undo record.

Search