@cyberart-io/engine 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/headless.d.ts +22 -2
- package/dist/headless.js +1 -1
- package/dist/index.d.ts +25 -3
- package/dist/index.js +1 -1
- package/docs/normalized-geometry.md +1 -1
- package/docs/production-scenario.md +31 -2
- package/package.json +1 -1
|
@@ -75,7 +75,7 @@ Resize-stable under **contain**: a normalized point stays on the same content lo
|
|
|
75
75
|
| `normalizedToCanvas` / `canvasToNormalized` | Content box ↔ drawing buffer |
|
|
76
76
|
| `normalizedToCss` / `cssToNormalized` | Content box ↔ layout pixels |
|
|
77
77
|
| `normalizedToAsset` / `assetToNormalized` | Content box ↔ intrinsic pixels |
|
|
78
|
-
| `pointerToRegion(pointer, layout, doc)` | First matching region (document order) |
|
|
78
|
+
| `pointerToRegion(pointer, layout, doc, options?)` | First matching region (document order). Pass `precedence: { [regionId]: number }` to pick the highest priority on overlap; ties use region id. |
|
|
79
79
|
| `regionToViewport(region, layout)` | CSS rect (polygon AABB) |
|
|
80
80
|
| `createLandmarkRegistry()` | `register` / `get` / `list`; duplicate ids → `{ ok: false, error: 'duplicate-id' }` |
|
|
81
81
|
| `validateGeometry(doc)` | Overlap, out-of-bounds, duplicate id, invalid shape; diagnostics include region ids |
|
|
@@ -12,6 +12,8 @@ pnpm exec vitest run packages/engine/src/canvas/cyb-101-host-audio-observation.r
|
|
|
12
12
|
pnpm exec vitest run packages/engine/src/canvas/cyb-102-audio-sidecar-restore.repro.spec.ts
|
|
13
13
|
pnpm exec vitest run packages/engine/src/canvas/cyb-103-reduced-motion-propagate.repro.spec.ts
|
|
14
14
|
pnpm exec vitest run packages/engine/src/canvas/cyb-104-dynamic-host-geometry.repro.spec.ts
|
|
15
|
+
pnpm exec vitest run packages/engine/src/canvas/cyb-109-overlap-precedence.repro.spec.ts
|
|
16
|
+
pnpm exec vitest run packages/engine/src/canvas/cyb-108-transactional-reload.repro.spec.ts
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
## When to use
|
|
@@ -29,9 +31,9 @@ Generic fixture ids only: `whistle`, `ripple`, `room`, `npc`.
|
|
|
29
31
|
|
|
30
32
|
1. **Definition.** `schemaVersion` `1`, stable `id`, `seed`, `required.participants`, `required.layers`. JSON only. Callbacks and incomplete steps (a `click` without `selector` or `x`/`y`, a `key` without `key`) fail closed at define time. Invalid sequence or binding manifests throw when the runner is created, never silently dropped.
|
|
31
33
|
2. **Required composition.** After mount, every required participant and compositor layer must be present. Omitting the cart under test is a failure, not a skipped assertion. Missing participant localizes to `routing`; missing layer localizes to `pixels`.
|
|
32
|
-
3. **Input.** Pointer and keyboard go through the browser harness (DOM coordinates and a11y ids), then the host reducer, then the group router. After an accepted reduce, and on reload, the runner syncs the active input surface from `host.projectInputSurface(state)` when provided: visible/focusable controls and the `GeometryDocument` update atomically. Controls the projection omits are removed from the DOM (not clickable, focusable, or hit-testable). Construction-time `geometry` / region ids remain the fallback when the host does not project.
|
|
34
|
+
3. **Input.** Pointer and keyboard go through the browser harness (DOM coordinates and a11y ids), then the host reducer, then the group router. After an accepted reduce, and on reload, the runner syncs the active input surface from `host.projectInputSurface(state)` when provided: visible/focusable controls and the `GeometryDocument` update atomically. Controls the projection omits are removed from the DOM (not clickable, focusable, or hit-testable). Construction-time `geometry` / region ids remain the fallback when the host does not project. Overlapping regions are allowed when each control declares a distinct finite `priority` (higher wins hit-tests in browser and headless); equal or missing priority on an overlapping pair still fails closed at boundary `input` (`ambiguous-geometry`). Duplicate ids, stale controls, and invalid geometry still fail closed (`invalid-geometry` / `stale-geometry`). Coordinate clicks record `observation.inputHitTest` with the selected control and rejected overlapping ids. Evidence records `observation.activeControlIds`. The seam is host-generic: it does not encode product room names.
|
|
33
35
|
4. **Observation.** Evidence records reducer accept/reject, routed envelopes, selected bindings, cue sequences, composed pixels, captions, audio invocation, focus, and cleanup. Caption and audio evidence come from the presentation-sequence player **or** an optional host-owned audio observer (`hostAudio` on runner options)—not only from a test sequence.
|
|
34
|
-
5. **Save / reload / replay.** `save()` writes a snapshot envelope. `hostState.audio` is the versioned headless-adapter sidecar (`schemaVersion` `1`; see [Audio](audio.md)). `reload`
|
|
36
|
+
5. **Save / reload / replay.** `save()` writes a snapshot envelope. `hostState.audio` is the versioned headless-adapter sidecar (`schemaVersion` `1`; see [Audio](audio.md)). `reload` preflights the outer sidecar envelope, optional `host.validateRestoreState`, the projected input surface, and every **present** module sidecar before any live mutation. Apply is one transaction across host/input, visual, semantic, bindings, sequences, and **audio last** so a sequence reapply cannot double-invoke cues. Any invalid schema, unknown id, stale revision, or malformed field rolls back to the pre-reload checkpoint — live state stays byte-for-byte unchanged. Sidecar failures localize at the precise subsystem boundary (`invalid-visual-sidecar` → `pixels`, `invalid-semantic-sidecar` → `caption`, `invalid-binding-sidecar` → `binding`, `invalid-sequence-sidecar` → `cue`, `invalid-audio-sidecar` → `audio`, `invalid-host-sidecar` → `save-replay`). `applyPresentation()` is not run on reload. The runner does call `syncInputSurface()` after a successful restore so restored `hostState` rebuilds the active controls and geometry without playing sequences. Unlock status is observation-only and is not restored. Omitting a sidecar key (legacy envelopes) skips that controller and remains compatible. `observation.audioRestoreOk` is `false` when `hostState.audio` is absent or restore fails. `replay` re-runs the inspector tape on a fresh composition and compares traces.
|
|
35
37
|
6. **Matrix.** Desktop/mobile viewport, DPR, reduced-motion, mute, failed-asset ids, and audio fallback are fields on the same definition. Not every cell needs a unique golden. Construction-time `matrix.reducedMotion` still seeds browser, visual, sequence, and audio policy.
|
|
36
38
|
7. **Evidence bundle.** `ProductionScenarioEvidenceBundle` carries pixel/semantic/a11y/trace/snapshot/asset hashes, revisions, traces, screenshot metadata, and `firstBrokenBoundary` localization: input → reducer → routing → binding → cue → pixels/caption/audio → save-replay. `traces.audio` is the sequence/headless adapter cue log. `traces.hostAudio` is host-owned observer evidence (invocation, completion, failure, skip, fallback, captions, envelope identity). `observation.audioInvoked` / `audioFailed` / `captions` merge both sources. `observation.audioRestoreOk` reports whether reload restored the audio sidecar. `observation.activeControlIds` is the projected input surface. A runtime `reducedMotion` step records `observation.reducedMotion` and `observation.reducedMotionPropagation` (from/to, per-participant applied/reason, active sequence policy). The optional `selectionTrace` field holds a [selection trace](selection-trace.md) export (`attachSelectionTrace(bundle, exported)`); the runner never populates it itself.
|
|
37
39
|
8. **CI = local.** `run(steps)` executes the definition’s steps (or a shared step list). Do not fork scenario JSON for CI.
|
|
@@ -153,6 +155,33 @@ const host = {
|
|
|
153
155
|
- `inspect().inputSurface` and `observation.activeControlIds` are the active semantic targets. Browser and headless share the scenario object.
|
|
154
156
|
- Reload calls `syncInputSurface()` only. It does not call `applyPresentation()`, so sequences are not replayed.
|
|
155
157
|
- Invalid documents, overlapping/duplicate ids, or a control without a matching region fail closed at boundary `input`.
|
|
158
|
+
- Overlapping regions are permitted when controls declare distinct `priority` values. Higher priority wins coordinate hit-tests; `observation.inputHitTest` records the winner and rejected overlapping controls. Equal or missing priority on an overlapping pair is `ambiguous-geometry`.
|
|
159
|
+
|
|
160
|
+
## Overlap precedence
|
|
161
|
+
|
|
162
|
+
When two or more projected regions intersect, attach a JSON-safe numeric `priority` to each `ProductionScenarioControl`. Higher priority wins pointer hit-tests in browser and headless; DOM stacking follows the same order.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
projectInputSurface() {
|
|
166
|
+
return {
|
|
167
|
+
controls: [
|
|
168
|
+
{ id: 'whistle', priority: 1 },
|
|
169
|
+
{ id: 'npc', priority: 2 },
|
|
170
|
+
],
|
|
171
|
+
geometry: {
|
|
172
|
+
version: 1,
|
|
173
|
+
landmarks: [],
|
|
174
|
+
regions: [
|
|
175
|
+
{ id: 'whistle', rect: { x: 0.1, y: 0.1, width: 0.4, height: 0.4 } },
|
|
176
|
+
{ id: 'npc', rect: { x: 0.45, y: 0.33, width: 0.4, height: 0.4 } },
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- Distinct priorities on every overlapping pair → surface accepted; coordinate clicks set `observation.inputHitTest`.
|
|
184
|
+
- Equal priority, missing priority, duplicate ids, stale controls, or invalid geometry → fail closed at boundary `input`.
|
|
156
185
|
|
|
157
186
|
## Runtime reduced motion
|
|
158
187
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyberart-io/engine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "CyberArt host engine: mount carts, events, capability manifests, geometry, runtime groups, and a Node/jsdom headless entry.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|