Files
xrwise-datastructure/data-structure-graphical-coding.md
T
2026-06-29 18:17:43 +02:00

5.4 KiB

Graphical Coding — Database Save Format

This document describes the JSON structure stored in the Supabase rooms table under the graphical_coding column.


Top-level shape

{
  "nodes": [ ...GraphNode ],
  "connections": [ ...Connection ]
}

The entire graph is serialized as a single JSON blob — no normalization, no references across rows.


GraphNode

{
  "id": "gc-1-1234567890",
  "kind": "source | trigger | effect",
  "label": "My Node",
  "position": { "x": 100, "y": 200 },
  "inputs": [ ...PortDef ],
  "outputs": [ ...PortDef ],
  "config": { ...kind-specific config }
}
Field Type Description
id string Unique node identifier, format gc-<index>-<timestamp>
kind "source" | "trigger" | "effect" Node category
label string User-visible name
position { x: number, y: number } Canvas position in pixels
inputs PortDef[] Input ports
outputs PortDef[] Output ports
config object Kind-specific configuration (see below)

PortDef

{ "id": "gc-1-1234567890", "name": "fired", "dataType": "boolean" }
Field Type Description
id string Port identifier (shares format with node IDs)
name string Display name, e.g. "fired", "value", "trigger"
dataType string "number", "string", "boolean", "object", or "any"

Connection

{
  "id": "conn-1234567890",
  "fromNode": "gc-1-111",
  "fromPort": "gc-1-111-o0",
  "toNode": "gc-3-333",
  "toPort": "gc-3-333-i0"
}
Field Type Description
id string Unique connection identifier
fromNode string Source node ID
fromPort string Source port ID (output port)
toNode string Target node ID
toPort string Target port ID (input port)

Node config by kind

source — REST API data feed

{
  "endpoint": "https://api.example.com/sensor",
  "condition": ">",
  "threshold": 42
}
Field Type Description
endpoint string REST API URL polled for data
condition ">" | "<" | "==" | "!=" | ">=" | "<=" Comparison operator applied to the API response value
threshold number Value compared against the API response

trigger — User interaction in the scene

{
  "sourceItemId": 7,
  "triggerEvent": "clicked",
  "cooldown": 500
}
Field Type Description
sourceItemId number | null Scene item ID to watch (null = any item)
triggerEvent "clicked" | "proximity_enter" | "proximity_exit" Interaction type
cooldown number Minimum milliseconds between trigger firings

effect — Scene modification

{
  "inputMode": "trigger",
  "targetObjectType": "item",
  "targetItemId": 3,
  "effectProperty": "color",
  "targetColor": "#ff0000"
}
Field Type Description
inputMode "trigger" | "stream" Whether the effect fires on a trigger pulse or follows a continuous stream value
targetObjectType "item" | "sky" What to modify
targetItemId number | null Scene item ID (only relevant when targetObjectType is "item")
effectProperty "color" | "position" | "rotation" | "scale" | "visibility" Which property to change

Additional fields depend on effectProperty:

color

{ "targetColor": "#ffffff" }

position

{ "positionAxis": "x", "targetPositionValue": 2.5 }

rotation

{ "rotationAxis": "y", "targetRotationValue": 90 }

targetRotationValue is in degrees.

scale

{ "targetScale": 2.0 }

visibility

{ "targetVisibility": false }

Full example

A graph with one trigger and one effect, connected:

{
  "nodes": [
    {
      "id": "gc-1",
      "kind": "trigger",
      "label": "On Click",
      "position": { "x": 120, "y": 200 },
      "inputs": [],
      "outputs": [
        { "id": "gc-1-o0", "name": "fired", "dataType": "boolean" }
      ],
      "config": {
        "sourceItemId": null,
        "triggerEvent": "clicked",
        "cooldown": 500
      }
    },
    {
      "id": "gc-2",
      "kind": "effect",
      "label": "Skybox Color",
      "position": { "x": 480, "y": 200 },
      "inputs": [
        { "id": "gc-2-i0", "name": "trigger", "dataType": "boolean" }
      ],
      "outputs": [],
      "config": {
        "inputMode": "trigger",
        "targetObjectType": "sky",
        "targetItemId": null,
        "effectProperty": "color",
        "targetColor": "#3a0f8c"
      }
    }
  ],
  "connections": [
    {
      "id": "conn-9",
      "fromNode": "gc-1",
      "fromPort": "gc-1-o0",
      "toNode": "gc-2",
      "toPort": "gc-2-i0"
    }
  ]
}

Persistence notes

  • Column: graphical_coding (JSONB) in the Supabase rooms table. Not yet reflected in supabase-types.ts.
  • Save: triggered manually via the "Save Project" button; the whole object is JSON-stringified and sent as a FormData field to PATCH /api/save-room/[id].
  • Load: the value is read from roomData.graphical_coding in creator.tsx and hydrated into the Zustand SceneStore.
  • Local cache: Zustand's persist middleware also writes the graph to localStorage under the key "scene-storage", so edits survive a page refresh before an explicit save.