# Block Coding — Database Save Format This document describes the JSON structure stored in the Supabase `rooms` table under the `graphical_coding` column for the block coding system (replacing the old node-graph format). --- ## Top-level shape ```json { "scripts": [ ...BlockScript ] } ``` The entire block coding state is one JSON blob — a flat array of scripts. Each script is self-contained (trigger + effects) with no cross-script references. --- ## `BlockScript` ```json { "id": "bl-1-1234567890", "trigger": { ...TriggerBlock }, "effects": [ ...EffectBlock ] } ``` | Field | Type | Description | |---|---|---| | `id` | `string` | Unique script identifier, format `bl--` | | `trigger` | `TriggerBlock` | The single trigger block at the top of the script | | `effects` | `EffectBlock[]` | Ordered list of effect blocks stacked below the trigger | --- ## `TriggerBlock` ```json { "id": "bl-2-1234567890", "event": "clicked", "sourceItemId": 3, "radius": 5, "exitRadius": 5, "cooldown": 500 } ``` | Field | Type | Description | |---|---|---| | `id` | `string` | Unique block identifier | | `event` | `"clicked" \| "proximity_enter" \| "proximity_exit"` | The interaction event to watch for | | `sourceItemId` | `number \| null` | Scene item ID to watch (`null` = no item selected yet) | | `radius` | `number` | Proximity enter radius in metres (only used when `event` is `"proximity_enter"`) | | `exitRadius` | `number` | Proximity exit radius in metres (only used when `event` is `"proximity_exit"`) | | `cooldown` | `number` | Minimum milliseconds between consecutive firings | --- ## `EffectBlock` All fields are always present regardless of `effectProp`. Fields that are irrelevant to the chosen property are stored but ignored at runtime. ```json { "id": "bl-3-1234567890", "effectProp": "color", "targetObjectType": "item", "targetItemId": 7, "targetColor": "#ff3366", "positionAxis": "x", "targetPositionValue": 0, "rotationAxis": "x", "targetRotationValue": 0, "targetScale": 1, "targetVisibility": true, "toggle": true } ``` | Field | Type | Description | |---|---|---| | `id` | `string` | Unique block identifier | | `effectProp` | `"color" \| "position" \| "rotation" \| "scale" \| "visibility"` | Which property this block modifies | | `targetObjectType` | `"item" \| "sky"` | Whether to target a scene item or the sky color | | `targetItemId` | `number \| null` | Scene item ID (only relevant when `targetObjectType` is `"item"`) | | `targetColor` | `string` | Target hex color (used when `effectProp` is `"color"`) | | `positionAxis` | `"x" \| "y" \| "z"` | Axis to move along (used when `effectProp` is `"position"`) | | `targetPositionValue` | `number` | Target position on the chosen axis in world units | | `rotationAxis` | `"x" \| "y" \| "z"` | Axis to rotate around (used when `effectProp` is `"rotation"`) | | `targetRotationValue` | `number` | Target rotation angle in degrees | | `targetScale` | `number` | Uniform scale multiplier (used when `effectProp` is `"scale"`) | | `targetVisibility` | `boolean` | `true` = visible, `false` = hidden (used when `effectProp` is `"visibility"`) | | `toggle` | `boolean` | When `true`, each trigger alternates between current state and target value instead of always applying the target. Not available for `"visibility"`. | --- ## Full example A script that changes a cube's color and moves it along X when a visitor clicks it, using toggle mode so each click alternates between states: ```json { "scripts": [ { "id": "bl-1-1700000000001", "trigger": { "id": "bl-2-1700000000002", "event": "clicked", "sourceItemId": 4, "radius": 5, "exitRadius": 5, "cooldown": 300 }, "effects": [ { "id": "bl-3-1700000000003", "effectProp": "color", "targetObjectType": "item", "targetItemId": 4, "targetColor": "#c05580", "positionAxis": "x", "targetPositionValue": 0, "rotationAxis": "x", "targetRotationValue": 0, "targetScale": 1, "targetVisibility": true, "toggle": true }, { "id": "bl-4-1700000000004", "effectProp": "position", "targetObjectType": "item", "targetItemId": 4, "targetColor": "#ffffff", "positionAxis": "x", "targetPositionValue": 3.0, "rotationAxis": "x", "targetRotationValue": 0, "targetScale": 1, "targetVisibility": true, "toggle": true } ] } ] } ``` --- ## Persistence notes - **Column:** `graphical_coding` (JSONB) in the Supabase `rooms` table — same column as the old node-graph format; the schema is distinguished by the presence of `scripts` (block coding) vs `nodes`/`connections` (legacy). - **Save:** the Zustand `blockCoding` store value is JSON-stringified and sent as `graphical_coding` in the `FormData` of `PATCH /api/save-room/[id]`, which writes it directly to the `graphical_coding` column. - **Load:** `roomData.graphical_coding` is read in `creator.tsx` and hydrated into `setBlockCoding(gc)` in the Zustand store. - **Local cache:** Zustand's `persist` middleware also writes the value to `localStorage` under the key `"scene-storage"`, so edits survive a page refresh before an explicit save. - **TypeScript types:** defined inline in `components/creator/graphical-coding/graphical-coding-panel.tsx` as `TriggerBlock`, `EffectBlock`, and `BlockScript`.