adjust doc
This commit is contained in:
@@ -12,7 +12,7 @@ This document describes the JSON structure stored in the Supabase `rooms` table
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
The entire block coding state is one JSON blob — a flat array of scripts. Each script is self-contained (trigger/source + effects) with no cross-script references.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ The entire block coding state is one JSON blob — a flat array of scripts. Each
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "bl-1-1234567890",
|
"id": "bl-1-1234567890",
|
||||||
"trigger": { ...TriggerBlock },
|
"trigger": { ...SceneTriggerBlock | SourceBlock },
|
||||||
"effects": [ ...EffectBlock ]
|
"effects": [ ...EffectBlock ]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -29,15 +29,21 @@ The entire block coding state is one JSON blob — a flat array of scripts. Each
|
|||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `id` | `string` | Unique script identifier, format `bl-<index>-<timestamp>` |
|
| `id` | `string` | Unique script identifier, format `bl-<index>-<timestamp>` |
|
||||||
| `trigger` | `TriggerBlock` | The single trigger block at the top of the script |
|
| `trigger` | `SceneTriggerBlock \| SourceBlock` | The single head block at the top of the script. Discriminated by `kind` |
|
||||||
| `effects` | `EffectBlock[]` | Ordered list of effect blocks stacked below the trigger |
|
| `effects` | `EffectBlock[]` | Ordered list of effect blocks stacked below the head block |
|
||||||
|
|
||||||
|
A script's head block can be one of two kinds:
|
||||||
|
|
||||||
|
- **`SceneTriggerBlock`** (`kind: "scene"`) — fires from a scene interaction (click, proximity).
|
||||||
|
- **`SourceBlock`** (`kind: "source"`) — fires from, or streams a value from, a REST API endpoint.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## `TriggerBlock`
|
## `SceneTriggerBlock`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"kind": "scene",
|
||||||
"id": "bl-2-1234567890",
|
"id": "bl-2-1234567890",
|
||||||
"event": "clicked",
|
"event": "clicked",
|
||||||
"sourceItemId": 3,
|
"sourceItemId": 3,
|
||||||
@@ -49,6 +55,7 @@ The entire block coding state is one JSON blob — a flat array of scripts. Each
|
|||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| `kind` | `"scene"` | Discriminant identifying this as a scene-event trigger |
|
||||||
| `id` | `string` | Unique block identifier |
|
| `id` | `string` | Unique block identifier |
|
||||||
| `event` | `"clicked" \| "proximity_enter" \| "proximity_exit"` | The interaction event to watch for |
|
| `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) |
|
| `sourceItemId` | `number \| null` | Scene item ID to watch (`null` = no item selected yet) |
|
||||||
@@ -58,6 +65,34 @@ The entire block coding state is one JSON blob — a flat array of scripts. Each
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## `SourceBlock`
|
||||||
|
|
||||||
|
An alternative head block that connects to a REST API endpoint instead of a scene event. Depending on `mode`, it either fires the script like a trigger, or streams its fetched value into the script's `EffectBlock`s.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": "source",
|
||||||
|
"id": "bl-2-1234567890",
|
||||||
|
"url": "https://api.example.com/sensor",
|
||||||
|
"mode": "trigger",
|
||||||
|
"condition": "received",
|
||||||
|
"threshold": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `kind` | `"source"` | Discriminant identifying this as a REST API source |
|
||||||
|
| `id` | `string` | Unique block identifier |
|
||||||
|
| `url` | `string` | REST API endpoint to call |
|
||||||
|
| `mode` | `"trigger" \| "value"` | `"trigger"` fires the script's effects on a condition; `"value"` streams the fetched value into the effects instead |
|
||||||
|
| `condition` | `"received" \| ">" \| "<" \| "==" \| "!=" \| ">=" \| "<="` | Comparison applied to the fetched value when `mode` is `"trigger"`. `"received"` fires on any response, ignoring `threshold` |
|
||||||
|
| `threshold` | `number` | Value compared against the response when `condition` is not `"received"` |
|
||||||
|
|
||||||
|
When `mode` is `"value"`, `condition`/`threshold` are stored but unused — every `EffectBlock` in the script is expected to use the fetched value in place of its own static target value (see [Incoming values](#incoming-values-source-mode--value) below).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## `EffectBlock`
|
## `EffectBlock`
|
||||||
|
|
||||||
All fields are always present regardless of `effectProp`. Fields that are irrelevant to the chosen property are stored but ignored at runtime.
|
All fields are always present regardless of `effectProp`. Fields that are irrelevant to the chosen property are stored but ignored at runtime.
|
||||||
@@ -92,11 +127,29 @@ All fields are always present regardless of `effectProp`. Fields that are irrele
|
|||||||
| `targetRotationValue` | `number` | Target rotation angle in degrees |
|
| `targetRotationValue` | `number` | Target rotation angle in degrees |
|
||||||
| `targetScale` | `number` | Uniform scale multiplier (used when `effectProp` is `"scale"`) |
|
| `targetScale` | `number` | Uniform scale multiplier (used when `effectProp` is `"scale"`) |
|
||||||
| `targetVisibility` | `boolean` | `true` = visible, `false` = hidden (used when `effectProp` is `"visibility"`) |
|
| `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"`. |
|
| `toggle` | `boolean` | When `true`, each firing alternates between current state and target value instead of always applying the target. Not available for `"visibility"`, and not shown/used when the parent script's `trigger.kind` is `"source"` with `mode: "value"` |
|
||||||
|
|
||||||
|
`EffectBlock` has no field referencing a source — the binding is implicit via the parent script's head block. There is no per-effect opt-in; if a script's head is a `SourceBlock` in `"value"` mode, **every** effect in that script is driven by the incoming value.
|
||||||
|
|
||||||
|
### Incoming values (Source, mode `"value"`)
|
||||||
|
|
||||||
|
The expected shape of the REST response value depends on `effectProp`:
|
||||||
|
|
||||||
|
| `effectProp` | Expected response format |
|
||||||
|
|---|---|
|
||||||
|
| `color` | Hex color string, e.g. `"#ff0000"` or `"ff0000"` |
|
||||||
|
| `position` | Number, in meters, e.g. `2.5` |
|
||||||
|
| `rotation` | Number, in degrees, e.g. `90` |
|
||||||
|
| `scale` | Number, as a multiplier, e.g. `1.5` |
|
||||||
|
| `visibility` | Boolean or `0`/`1`, e.g. `true`, `false`, `1`, `0` |
|
||||||
|
|
||||||
|
This mapping is UI-only today (shown as a hint in the panel) — no runtime in `xrwise-viewer` currently polls `SourceBlock.url` or applies these values; see [Persistence notes](#persistence-notes).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Full example
|
## Full examples
|
||||||
|
|
||||||
|
### Scene trigger
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
@@ -106,6 +159,7 @@ A script that changes a cube's color and moves it along X when a visitor clicks
|
|||||||
{
|
{
|
||||||
"id": "bl-1-1700000000001",
|
"id": "bl-1-1700000000001",
|
||||||
"trigger": {
|
"trigger": {
|
||||||
|
"kind": "scene",
|
||||||
"id": "bl-2-1700000000002",
|
"id": "bl-2-1700000000002",
|
||||||
"event": "clicked",
|
"event": "clicked",
|
||||||
"sourceItemId": 4,
|
"sourceItemId": 4,
|
||||||
@@ -148,6 +202,82 @@ A script that changes a cube's color and moves it along X when a visitor clicks
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### REST API source — trigger mode
|
||||||
|
|
||||||
|
Fires the script's effects whenever the endpoint reports a value greater than `20`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": [
|
||||||
|
{
|
||||||
|
"id": "bl-5-1700000000005",
|
||||||
|
"trigger": {
|
||||||
|
"kind": "source",
|
||||||
|
"id": "bl-6-1700000000006",
|
||||||
|
"url": "https://api.example.com/temperature",
|
||||||
|
"mode": "trigger",
|
||||||
|
"condition": ">",
|
||||||
|
"threshold": 20
|
||||||
|
},
|
||||||
|
"effects": [
|
||||||
|
{
|
||||||
|
"id": "bl-7-1700000000007",
|
||||||
|
"effectProp": "visibility",
|
||||||
|
"targetObjectType": "item",
|
||||||
|
"targetItemId": 9,
|
||||||
|
"targetColor": "#ffffff",
|
||||||
|
"positionAxis": "x",
|
||||||
|
"targetPositionValue": 0,
|
||||||
|
"rotationAxis": "x",
|
||||||
|
"targetRotationValue": 0,
|
||||||
|
"targetScale": 1,
|
||||||
|
"targetVisibility": true,
|
||||||
|
"toggle": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### REST API source — value mode
|
||||||
|
|
||||||
|
Streams the endpoint's fetched value directly into the scale of item `12` (the static `targetScale` below is stored but ignored — the panel shows an "⚡ incoming value" badge in its place):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": [
|
||||||
|
{
|
||||||
|
"id": "bl-8-1700000000008",
|
||||||
|
"trigger": {
|
||||||
|
"kind": "source",
|
||||||
|
"id": "bl-9-1700000000009",
|
||||||
|
"url": "https://api.example.com/loudness",
|
||||||
|
"mode": "value",
|
||||||
|
"condition": "received",
|
||||||
|
"threshold": 0
|
||||||
|
},
|
||||||
|
"effects": [
|
||||||
|
{
|
||||||
|
"id": "bl-10-1700000000010",
|
||||||
|
"effectProp": "scale",
|
||||||
|
"targetObjectType": "item",
|
||||||
|
"targetItemId": 12,
|
||||||
|
"targetColor": "#ffffff",
|
||||||
|
"positionAxis": "x",
|
||||||
|
"targetPositionValue": 0,
|
||||||
|
"rotationAxis": "x",
|
||||||
|
"targetRotationValue": 0,
|
||||||
|
"targetScale": 1,
|
||||||
|
"targetVisibility": true,
|
||||||
|
"toggle": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Persistence notes
|
## Persistence notes
|
||||||
@@ -156,4 +286,6 @@ A script that changes a cube's color and moves it along X when a visitor clicks
|
|||||||
- **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.
|
- **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.
|
- **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.
|
- **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`.
|
- **Backward compatibility:** scripts saved before the `SourceBlock`/`kind` fields existed are missing `trigger.kind` (and may be missing other new fields). `normalizeScript()` in the panel fills in defaults for any missing/invalid field (defaulting `trigger.kind` to `"scene"`), so old saves keep loading without a migration step.
|
||||||
|
- **TypeScript types:** defined inline in `components/creator/graphical-coding/graphical-coding-panel.tsx` as `SceneTriggerBlock`, `SourceBlock`, `ScriptTrigger` (their union), `EffectBlock`, and `BlockScript`.
|
||||||
|
- **Runtime status:** as of this writing, only the creator's editing UI and data model support `SourceBlock`. The `xrwise-viewer` scene runtime (`lib/block-runtime.ts`) does not yet poll REST endpoints, evaluate `condition`/`threshold`, or apply incoming values to effects — that wiring is a follow-up.
|
||||||
|
|||||||
Reference in New Issue
Block a user