@cyberart-io/engine 0.0.6 → 0.0.7
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/README.md +56 -11
- package/dist/headless.d.ts +201 -5
- package/dist/headless.js +1 -1
- package/dist/index.d.ts +209 -4
- package/dist/index.js +1 -1
- package/docs/calculation-carts.md +134 -0
- package/docs/capability-manifest.md +5 -2
- package/docs/compositor.md +8 -3
- package/docs/runtime-group.md +3 -3
- package/docs/visual-layers.md +151 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@cyberart-io/engine`
|
|
2
2
|
|
|
3
|
-
An engine for building animated, interactive, generative programs for the web. You write a **cart** — a small object with `getDefaultState` (initial state), `update` (advance that state each tick), and `render` (draw it).
|
|
3
|
+
An engine for building animated, interactive, generative programs for the web. You write a **cart** — a small object with `getDefaultState` (initial state), `update` (advance that state each tick), and optional `render` (draw it). A render cart is a state machine in a loop that paints to the screen. A calculation cart runs the same state machine without a canvas, and talks to a sibling that does paint. The engine mounts it into a DOM element you own, runs the loop, and gives you pause, snapshot, events, and save/load.
|
|
4
4
|
|
|
5
5
|
## License
|
|
6
6
|
|
|
@@ -97,7 +97,9 @@ Full API for the event router, deterministic replay, and CI harness (so agents c
|
|
|
97
97
|
- [Executable modules](docs/executable-modules.md) — trusted versioned factories, host allowlists, isolation, per-module failures
|
|
98
98
|
- [Normalized geometry](docs/normalized-geometry.md) — coordinate spaces, contain/cover/crop layout, landmarks, hit regions, debug overlay
|
|
99
99
|
- [Runtime group](docs/runtime-group.md) — `createRuntimeGroup`, shared router attach, lockstep clock; `createHeadlessMultiCartHarness` from `@cyberart-io/engine/headless`
|
|
100
|
+
- [Calculation carts](docs/calculation-carts.md) — `kind: 'calculation'`, update + events without paint; sibling render carts still paint
|
|
100
101
|
- [Compositor](docs/compositor.md) — `createCompositor`, transparent stacking, `screen` blend, `writeComposedFrame`
|
|
102
|
+
- [Visual layers](docs/visual-layers.md) — `createVisualLayerController`, versioned overlay `v1`/`v2`, deterministic swaps, `hostState` JSON
|
|
101
103
|
- [Browser harness](docs/browser-harness.md) — `createBrowserHarness`, DOM clicks, viewport/DPR, composed screenshots
|
|
102
104
|
- [Replay inspector](docs/replay-inspector.md) — `createReplayInspector`, causation trees, redacted export/import, headless replay
|
|
103
105
|
- [MIDI](docs/midi.md) — host-owned `MidiManager`, note/CC/pitch in and out, `inject` / fake port, structured `requestAccess`
|
|
@@ -112,18 +114,20 @@ A cart is an `AnimationCart`. Required:
|
|
|
112
114
|
|---|---|
|
|
113
115
|
| `getDefaultState` | Build the initial `state`. Use `R` (`Random`) for anything that should follow the seed. |
|
|
114
116
|
| `update` | Advance `state` each tick. Return the next state. |
|
|
115
|
-
| `render` | Draw into the 2D context (and optional `ImageData`). |
|
|
116
117
|
| `metadata.id` / `name` / `frameRate` | Identity and loop rate. |
|
|
117
118
|
|
|
119
|
+
`render` draws into the 2D context (and optional `ImageData`). Omit it on calculation carts (`createRuntime({ kind: 'calculation' })` or a group participant with `kind: 'calculation'`). The runtime does not call `render` and does not create a canvas for that kind. Render carts (the default) still paint as before.
|
|
120
|
+
|
|
118
121
|
Useful optionals:
|
|
119
122
|
|
|
120
123
|
| Piece | Role |
|
|
121
124
|
|---|---|
|
|
125
|
+
| `render` | Draw into the 2D context. Omit on calculation carts; the runtime will not call a no-op either. |
|
|
122
126
|
| `teardown` | Dispose long-lived resources (Tone nodes, listeners) when the cart unloads. |
|
|
123
127
|
| `metadata.audio` | `'tone'` if the piece needs Web Audio. Omit for a silent cart. |
|
|
124
128
|
| `metadata.generative` | `true` when output is a function of the token hash. Saves then only load on the same seed. |
|
|
125
129
|
|
|
126
|
-
Callbacks also receive `DimensionContext` (`width` / `height` and derived size fields), `KeyboardManager`, `PointerManager`, and an optional `HostChannel`. You do not have to use them. Types live on the package: `AnimationCart`, `Random`, `DimensionContext`, `KeyboardManager`, `PointerManager`, `HostChannel`.
|
|
130
|
+
Callbacks also receive `DimensionContext` (`width` / `height` and derived size fields), `KeyboardManager`, `PointerManager`, and an optional `HostChannel`. You do not have to use them. Types live on the package: `AnimationCart`, `CartKind`, `Random`, `DimensionContext`, `KeyboardManager`, `PointerManager`, `HostChannel`.
|
|
127
131
|
|
|
128
132
|
`Random` is seed-stable: `R.dec(min, max)`, `R.int(min, max)`, `R.bool()`, `R.choose(list)`.
|
|
129
133
|
|
|
@@ -159,6 +163,7 @@ const cart = runtime.mount(artProject, {
|
|
|
159
163
|
| `assets` | off | Host `AssetResolver` plus engine cache/preload. Carts keep logical refs. Leave unset when the piece has no media. |
|
|
160
164
|
| `audioBroker` | off | Shared `createAudioBroker` instance. Optional. Omit when the cart only uses `metadata.audio: 'tone'`. |
|
|
161
165
|
| `audioParticipantId` | none | Group participant id to authorize / teardown on this runtime. |
|
|
166
|
+
| `kind` | `'render'` | `'calculation'` skips canvas construction and paint. `getDefaultState`, `update`, and host-channel events still run. |
|
|
162
167
|
|
|
163
168
|
`CartHandle` (what `mount` returns):
|
|
164
169
|
|
|
@@ -182,9 +187,9 @@ const cart = runtime.mount(artProject, {
|
|
|
182
187
|
| `tokenData` | Live hash and token id. |
|
|
183
188
|
| `getCartState()` | Live object for debug UI. Not JSON-safe — use export/import for saves. |
|
|
184
189
|
|
|
185
|
-
`runtime.destroy()` tears down the runtime. `runtime.unlockAudio()` is for an early click before `mount`. `runtime.onError` receives frame errors (`phase`, `consecutive`, `stopped`).
|
|
190
|
+
`runtime.destroy()` tears down the runtime. `runtime.unlockAudio()` is for an early click before `mount`. `runtime.onError` receives frame errors (`phase`, `consecutive`, `stopped`). `runtime.kind` is `'render'` (default) or `'calculation'`.
|
|
186
191
|
|
|
187
|
-
One cart per runtime. A second `mount` unloads the first. Several pieces on a page means several `createRuntime()` calls, or one `createRuntimeGroup()` that attaches each mailbox to a shared router and locksteps a deterministic clock.
|
|
192
|
+
One cart per runtime. A second `mount` unloads the first. Several pieces on a page means several `createRuntime()` calls, or one `createRuntimeGroup()` that attaches each mailbox to a shared router and locksteps a deterministic clock. Calculation participants (`kind: 'calculation'`) share that group without a canvas; see [calculation carts](docs/calculation-carts.md).
|
|
188
193
|
|
|
189
194
|
## Audio
|
|
190
195
|
|
|
@@ -418,17 +423,57 @@ import { createRuntimeGroup } from '@cyberart-io/engine';
|
|
|
418
423
|
const group = createRuntimeGroup({
|
|
419
424
|
origin: 0,
|
|
420
425
|
participants: [
|
|
421
|
-
{ id: '
|
|
422
|
-
{ id: '
|
|
426
|
+
{ id: 'world', cart: worldCart, kind: 'calculation', emit: ['host.intent.*'], subscribe: ['host.state.*'] },
|
|
427
|
+
{ id: 'overlay', cart: overlayCart, subscribe: ['host.intent.*'] },
|
|
423
428
|
],
|
|
424
429
|
});
|
|
425
|
-
group.publish({ type: 'host.state.
|
|
430
|
+
group.publish({ type: 'host.state.scene-changed', kind: 'state', payload: { sceneId: 'alpha' } });
|
|
426
431
|
await group.step(2);
|
|
427
432
|
const { trace } = await group.inspect();
|
|
428
433
|
group.destroy();
|
|
429
434
|
```
|
|
430
435
|
|
|
431
|
-
Headless / CI: `createHeadlessMultiCartHarness` from `@cyberart-io/engine/headless` is the same handle after `installHeadlessCanvas()`. Full options: [runtime group](docs/runtime-group.md). Causation trees, redaction, and tape replay: [replay inspector](docs/replay-inspector.md).
|
|
436
|
+
Headless / CI: `createHeadlessMultiCartHarness` from `@cyberart-io/engine/headless` is the same handle after `installHeadlessCanvas()`. Full options: [runtime group](docs/runtime-group.md). Calculation carts (no canvas / paint): [calculation carts](docs/calculation-carts.md). Causation trees, redaction, and tape replay: [replay inspector](docs/replay-inspector.md).
|
|
437
|
+
|
|
438
|
+
## Visual layers
|
|
439
|
+
|
|
440
|
+
Durable overlay / mask / sprite versions (`v1`, `v2`) with deterministic show, hide, replace, and crossfade. An accepted `host.state.accepted` (`sceneId: 'alpha'`) runs a presentation cue; the compositor reveals a **preloaded** alternate source in one `setLayer`. Failed assets keep the prior valid frame and emit `visual.layer.failed`. Persist `controller.snapshot()` in envelope `hostState` — do not put pixels in the envelope.
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
import {
|
|
444
|
+
ASSET_READY_EVENT,
|
|
445
|
+
HOST_STATE_ACCEPTED_EVENT,
|
|
446
|
+
createVisualLayerController,
|
|
447
|
+
} from '@cyberart-io/engine';
|
|
448
|
+
|
|
449
|
+
const layers = createVisualLayerController({
|
|
450
|
+
compositor,
|
|
451
|
+
sceneId: 'alpha',
|
|
452
|
+
layers: [
|
|
453
|
+
{
|
|
454
|
+
id: 'overlay',
|
|
455
|
+
kind: 'layer',
|
|
456
|
+
initialVersion: 'v1',
|
|
457
|
+
versions: [
|
|
458
|
+
{ id: 'v1', assetId: 'overlay.v1' },
|
|
459
|
+
{ id: 'v2', assetId: 'overlay.v2' },
|
|
460
|
+
],
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
onAccepted: [{ layerId: 'overlay', toVersion: 'v2', kind: 'replace', durationFrames: 2 }],
|
|
464
|
+
});
|
|
465
|
+
layers.registerSource('overlay.v1', imageV1);
|
|
466
|
+
layers.registerSource('overlay.v2', imageV2);
|
|
467
|
+
layers.handleHostEvent({ type: ASSET_READY_EVENT, kind: 'state', payload: { id: 'overlay.v2' } });
|
|
468
|
+
layers.handleHostEvent({
|
|
469
|
+
type: HOST_STATE_ACCEPTED_EVENT,
|
|
470
|
+
kind: 'state',
|
|
471
|
+
payload: { sceneId: 'alpha' },
|
|
472
|
+
});
|
|
473
|
+
layers.step(2);
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Headless capture: `captureVisualLayers(layers)` from `@cyberart-io/engine` or `@cyberart-io/engine/headless`. Full API: [visual layers](docs/visual-layers.md).
|
|
432
477
|
|
|
433
478
|
## Normalized geometry
|
|
434
479
|
|
|
@@ -594,7 +639,7 @@ The engine prefers, in order:
|
|
|
594
639
|
2. The container’s first `<canvas>`
|
|
595
640
|
3. A canvas it creates (no `id="canvas"`)
|
|
596
641
|
|
|
597
|
-
A canvas the host adopted is left in place on destroy; a canvas the engine created is removed. Both `cart.destroy()` and `runtime.destroy()` are idempotent.
|
|
642
|
+
A canvas the host adopted is left in place on destroy; a canvas the engine created is removed. Both `cart.destroy()` and `runtime.destroy()` are idempotent. `createRuntime({ kind: 'calculation' })` never creates or adopts a canvas (`cart.canvas` is `undefined`).
|
|
598
643
|
|
|
599
644
|
## Limits
|
|
600
645
|
|
|
@@ -605,7 +650,7 @@ A canvas the host adopted is left in place on destroy; a canvas the engine creat
|
|
|
605
650
|
|
|
606
651
|
## Publishing this package (maintainers)
|
|
607
652
|
|
|
608
|
-
Not part of writing a cart. Engine source lives in `packages/engine/src/` (not mixed into the site). The npm tarball is built from `packages/engine/src/index.ts` and `packages/engine/src/headless.ts` and contains minified `dist/index.js` + `dist/headless.js`, rolled-up `.d.ts` for both, `LICENSE`, `README.md`, `docs/` (including compositor, browser harness, replay inspector, MIDI, executable modules, snapshots, and
|
|
653
|
+
Not part of writing a cart. Engine source lives in `packages/engine/src/` (not mixed into the site). The npm tarball is built from `packages/engine/src/index.ts` and `packages/engine/src/headless.ts` and contains minified `dist/index.js` + `dist/headless.js`, rolled-up `.d.ts` for both, `LICENSE`, `README.md`, `docs/` (including compositor, visual layers, browser harness, replay inspector, MIDI, executable modules, snapshots, audio, and calculation carts), and `package.json`. Site code that still imports `src/ui/lib/...` hits thin re-export shims so those paths keep working.
|
|
609
654
|
|
|
610
655
|
```bash
|
|
611
656
|
pnpm run pack:engine
|
package/dist/headless.d.ts
CHANGED
|
@@ -254,11 +254,16 @@ type AnimationTiming = {
|
|
|
254
254
|
deltaSinceLastUpdate: number;
|
|
255
255
|
deltaSinceLastRender: number;
|
|
256
256
|
};
|
|
257
|
+
type CartKind = 'render' | 'calculation';
|
|
257
258
|
type AnimationCart<T = unknown, TFeatureState = undefined> = {
|
|
258
259
|
getDefaultFeatureState?: (R: Random, dimensionContext: DimensionContext, rawParams: number[], keyboardManager: KeyboardManager, customState?: Partial<T>, pointerManager?: PointerManager, gameManager?: unknown, hostChannel?: HostChannel) => TFeatureState;
|
|
259
260
|
getDefaultState: (R: Random, dimensionContext: DimensionContext, rawParams: number[], keyboardManager: KeyboardManager, customState?: Partial<T>, pointerManager?: PointerManager, gameManager?: unknown, featureState?: Readonly<TFeatureState>, hostChannel?: HostChannel) => T;
|
|
260
261
|
update: (R: Random, framesElapsed: number, rawParams: number[], dimensionContext: DimensionContext, state: T, keyboardManager: KeyboardManager, pointerManager?: PointerManager, gameManager?: unknown, timing?: AnimationTiming, featureState?: Readonly<TFeatureState>, hostChannel?: HostChannel) => T;
|
|
261
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Draw into the 2D context. Optional on calculation carts (`kind:
|
|
264
|
+
* 'calculation'`); the runtime does not call it and does not create a canvas.
|
|
265
|
+
*/
|
|
266
|
+
render?: (R: Random, framesElapsed: number, rawParams: number[], dimensionContext: DimensionContext, state: T, drawingContext: CanvasRenderingContext2D, imageData: ImageData, pointerManager?: PointerManager, gameManager?: unknown, timing?: AnimationTiming, featureState?: Readonly<TFeatureState>, hostChannel?: HostChannel) => void;
|
|
262
267
|
adjust?: Record<string, {
|
|
263
268
|
type: 'switch';
|
|
264
269
|
immediate?: boolean;
|
|
@@ -669,6 +674,11 @@ type CreateRuntimeOptions = {
|
|
|
669
674
|
* this id does not close Tone for remaining carts.
|
|
670
675
|
*/
|
|
671
676
|
audioParticipantId?: string;
|
|
677
|
+
/**
|
|
678
|
+
* `'calculation'` skips canvas construction and paint. `getDefaultState`,
|
|
679
|
+
* `update`, and host-channel events still run. Default `'render'`.
|
|
680
|
+
*/
|
|
681
|
+
kind?: CartKind;
|
|
672
682
|
};
|
|
673
683
|
type MountOptions<T = unknown> = {
|
|
674
684
|
/** Boot overrides passed as `customState` into `getDefaultState`. Not a live-state replay. */
|
|
@@ -747,6 +757,8 @@ type CyberArtRuntime = {
|
|
|
747
757
|
readonly assets: AssetPreloader | undefined;
|
|
748
758
|
/** Shared broker when `createRuntime({ audioBroker })` was set. */
|
|
749
759
|
readonly audioBroker: AudioBroker | undefined;
|
|
760
|
+
/** `'render'` (default) or `'calculation'` (no canvas / paint). */
|
|
761
|
+
readonly kind: CartKind;
|
|
750
762
|
onError?: (error: unknown, info: FrameErrorInfo) => void;
|
|
751
763
|
};
|
|
752
764
|
|
|
@@ -838,7 +850,7 @@ type EventRouter = {
|
|
|
838
850
|
* locksteps a deterministic clock. Carts never receive the router object.
|
|
839
851
|
*/
|
|
840
852
|
|
|
841
|
-
type RuntimeGroupKind =
|
|
853
|
+
type RuntimeGroupKind = CartKind;
|
|
842
854
|
/**
|
|
843
855
|
* Optional capability-shaped attach hints. Explicit participant `emit` /
|
|
844
856
|
* `subscribe` / `authoritative` win. Do not import the capability manifest
|
|
@@ -856,7 +868,7 @@ type RuntimeGroupFrameError = {
|
|
|
856
868
|
type RuntimeGroupParticipantConfig<T = unknown> = {
|
|
857
869
|
id: string;
|
|
858
870
|
cart: AnimationCart<T>;
|
|
859
|
-
/** Rendered surface vs calculation cart (
|
|
871
|
+
/** Rendered surface vs calculation cart (update + events, no canvas / paint). */
|
|
860
872
|
kind?: RuntimeGroupKind;
|
|
861
873
|
seed?: CreateRuntimeOptions['seed'];
|
|
862
874
|
container?: HTMLElement;
|
|
@@ -944,6 +956,20 @@ type CompositorBlendMode = (typeof COMPOSITOR_BLEND_MODES)[number];
|
|
|
944
956
|
declare const COMPOSITOR_CLEAR_POLICIES: readonly ["transparent", "opaque"];
|
|
945
957
|
type CompositorClearPolicy = (typeof COMPOSITOR_CLEAR_POLICIES)[number];
|
|
946
958
|
type CompositorPointerEvents = 'auto' | 'none';
|
|
959
|
+
declare const COMPOSITOR_SOURCE_KINDS: readonly ["participant", "image", "canvas"];
|
|
960
|
+
type CompositorSourceKind = (typeof COMPOSITOR_SOURCE_KINDS)[number];
|
|
961
|
+
type CompositorParticipantSource = {
|
|
962
|
+
kind: 'participant';
|
|
963
|
+
};
|
|
964
|
+
type CompositorImageSource = {
|
|
965
|
+
kind: 'image';
|
|
966
|
+
image: ImageData;
|
|
967
|
+
};
|
|
968
|
+
type CompositorCanvasSource = {
|
|
969
|
+
kind: 'canvas';
|
|
970
|
+
canvas: HTMLCanvasElement;
|
|
971
|
+
};
|
|
972
|
+
type CompositorLayerSource = CompositorParticipantSource | CompositorImageSource | CompositorCanvasSource;
|
|
947
973
|
type CompositorClip = {
|
|
948
974
|
x: number;
|
|
949
975
|
y: number;
|
|
@@ -959,6 +985,11 @@ type CompositorLayerConfig = {
|
|
|
959
985
|
clip?: CompositorClip;
|
|
960
986
|
pointerEvents?: CompositorPointerEvents;
|
|
961
987
|
clearPolicy?: CompositorClearPolicy;
|
|
988
|
+
/**
|
|
989
|
+
* Pixel source. Default `participant` reads the runtime-group canvas.
|
|
990
|
+
* `image` / `canvas` swap atomically via `setLayer` / `addLayer`.
|
|
991
|
+
*/
|
|
992
|
+
source?: CompositorLayerSource;
|
|
962
993
|
};
|
|
963
994
|
type CompositorLayerInspect = {
|
|
964
995
|
id: string;
|
|
@@ -969,6 +1000,7 @@ type CompositorLayerInspect = {
|
|
|
969
1000
|
clip: CompositorClip | null;
|
|
970
1001
|
pointerEvents: CompositorPointerEvents;
|
|
971
1002
|
clearPolicy: CompositorClearPolicy;
|
|
1003
|
+
sourceKind: CompositorSourceKind;
|
|
972
1004
|
};
|
|
973
1005
|
type ComposedFrame = {
|
|
974
1006
|
imageData: ImageData;
|
|
@@ -1001,6 +1033,7 @@ type Compositor = {
|
|
|
1001
1033
|
*/
|
|
1002
1034
|
resize(width: number, height: number, dpr?: number): void;
|
|
1003
1035
|
setLayer(id: string, patch: Partial<Omit<CompositorLayerConfig, 'id'>>): void;
|
|
1036
|
+
addLayer(config: CompositorLayerConfig): void;
|
|
1004
1037
|
layer(id: string): CompositorLayerInspect;
|
|
1005
1038
|
layers(): CompositorLayerInspect[];
|
|
1006
1039
|
pointerTarget(x: number, y: number): string | undefined;
|
|
@@ -1411,6 +1444,14 @@ type CueView = {
|
|
|
1411
1444
|
progress: number;
|
|
1412
1445
|
repeatIndex: number;
|
|
1413
1446
|
};
|
|
1447
|
+
type PlayCueResult = {
|
|
1448
|
+
ok: true;
|
|
1449
|
+
cue: CueView;
|
|
1450
|
+
} | {
|
|
1451
|
+
ok: false;
|
|
1452
|
+
reason: 'duplicate' | 'invalid';
|
|
1453
|
+
detail: string;
|
|
1454
|
+
};
|
|
1414
1455
|
|
|
1415
1456
|
/**
|
|
1416
1457
|
* Copyright (c) 2026 Aaron Boyarsky
|
|
@@ -1494,6 +1535,160 @@ declare function createHeadlessAudioAdapter(options?: {
|
|
|
1494
1535
|
originFrame?: number;
|
|
1495
1536
|
}): HeadlessAudioAdapter;
|
|
1496
1537
|
|
|
1538
|
+
declare const VISUAL_LAYER_SNAPSHOT_SCHEMA_VERSION: 1;
|
|
1539
|
+
declare const VISUAL_LAYER_KINDS: readonly ["layer", "mask", "sprite"];
|
|
1540
|
+
type VisualLayerKind = (typeof VISUAL_LAYER_KINDS)[number];
|
|
1541
|
+
declare const VISUAL_LAYER_TRANSITIONS: readonly ["show", "hide", "replace", "crossfade"];
|
|
1542
|
+
type VisualLayerTransitionKind = (typeof VISUAL_LAYER_TRANSITIONS)[number];
|
|
1543
|
+
declare const VISUAL_LAYER_EVENTS: readonly ["visual.layer.revealed", "visual.layer.hidden", "visual.layer.transition-started", "visual.layer.transition-completed", "visual.layer.failed"];
|
|
1544
|
+
type VisualLayerEventType = (typeof VISUAL_LAYER_EVENTS)[number];
|
|
1545
|
+
declare const VISUAL_LAYER_FAILURE_CODES: readonly ["asset-failed", "missing-source", "unknown-layer", "unknown-version", "invalid-snapshot"];
|
|
1546
|
+
type VisualLayerFailureCode = (typeof VISUAL_LAYER_FAILURE_CODES)[number];
|
|
1547
|
+
type VisualLayerAssetStatus = 'pending' | 'ready' | 'failed';
|
|
1548
|
+
type VisualLayerFallbackPolicy = 'keep-prior' | 'hide' | {
|
|
1549
|
+
version: string;
|
|
1550
|
+
};
|
|
1551
|
+
type VisualLayerVersionDeclaration = {
|
|
1552
|
+
id: string;
|
|
1553
|
+
assetId: string;
|
|
1554
|
+
provenance?: AssetProvenance;
|
|
1555
|
+
};
|
|
1556
|
+
type VisualLayerDeclaration = {
|
|
1557
|
+
id: string;
|
|
1558
|
+
kind: VisualLayerKind;
|
|
1559
|
+
versions: readonly VisualLayerVersionDeclaration[];
|
|
1560
|
+
initialVersion?: string;
|
|
1561
|
+
compositorLayerId?: string;
|
|
1562
|
+
incomingLayerId?: string;
|
|
1563
|
+
order?: number;
|
|
1564
|
+
blend?: CompositorBlendMode;
|
|
1565
|
+
clip?: CompositorClip;
|
|
1566
|
+
pointerEvents?: CompositorPointerEvents;
|
|
1567
|
+
clearPolicy?: CompositorClearPolicy;
|
|
1568
|
+
fallback?: VisualLayerFallbackPolicy;
|
|
1569
|
+
};
|
|
1570
|
+
type VisualLayerAcceptedBinding = {
|
|
1571
|
+
layerId: string;
|
|
1572
|
+
toVersion: string;
|
|
1573
|
+
kind?: VisualLayerTransitionKind;
|
|
1574
|
+
durationFrames?: number;
|
|
1575
|
+
delayFrames?: number;
|
|
1576
|
+
easing?: CueEasing;
|
|
1577
|
+
idempotencyKey?: string;
|
|
1578
|
+
};
|
|
1579
|
+
type VisualLayerCueSpec = CueSpec & {
|
|
1580
|
+
layerId: string;
|
|
1581
|
+
kind: VisualLayerTransitionKind;
|
|
1582
|
+
toVersion?: string;
|
|
1583
|
+
fromVersion?: string;
|
|
1584
|
+
};
|
|
1585
|
+
type VisualLayerTransitionInspect = {
|
|
1586
|
+
kind: VisualLayerTransitionKind;
|
|
1587
|
+
progress: number;
|
|
1588
|
+
fromVersion: string | null;
|
|
1589
|
+
toVersion: string | null;
|
|
1590
|
+
cueKey: string;
|
|
1591
|
+
startFrame: number;
|
|
1592
|
+
durationFrames: number;
|
|
1593
|
+
delayFrames: number;
|
|
1594
|
+
easing: CueEasing;
|
|
1595
|
+
};
|
|
1596
|
+
type VisualLayerInspect = {
|
|
1597
|
+
id: string;
|
|
1598
|
+
kind: VisualLayerKind;
|
|
1599
|
+
visible: boolean;
|
|
1600
|
+
activeVersion: string | null;
|
|
1601
|
+
pendingVersion: string | null;
|
|
1602
|
+
committedVersion: string | null;
|
|
1603
|
+
opacity: number;
|
|
1604
|
+
order: number;
|
|
1605
|
+
provenance: AssetProvenance | null;
|
|
1606
|
+
overrideVersion: string | null;
|
|
1607
|
+
transition: VisualLayerTransitionInspect | null;
|
|
1608
|
+
};
|
|
1609
|
+
type VisualLayerEvent = {
|
|
1610
|
+
type: VisualLayerEventType;
|
|
1611
|
+
atFrame: number;
|
|
1612
|
+
layerId: string;
|
|
1613
|
+
version?: string;
|
|
1614
|
+
kind?: VisualLayerTransitionKind;
|
|
1615
|
+
progress: number;
|
|
1616
|
+
code?: VisualLayerFailureCode;
|
|
1617
|
+
};
|
|
1618
|
+
type VisualLayerDiagnostic = {
|
|
1619
|
+
code: VisualLayerFailureCode;
|
|
1620
|
+
layerId: string;
|
|
1621
|
+
version?: string;
|
|
1622
|
+
assetId?: string;
|
|
1623
|
+
message: string;
|
|
1624
|
+
atFrame: number;
|
|
1625
|
+
failure?: AssetFailure;
|
|
1626
|
+
};
|
|
1627
|
+
type VisualLayerSnapshotRow = {
|
|
1628
|
+
id: string;
|
|
1629
|
+
kind: VisualLayerKind;
|
|
1630
|
+
visible: boolean;
|
|
1631
|
+
committedVersion: string | null;
|
|
1632
|
+
pendingVersion: string | null;
|
|
1633
|
+
activeVersion: string | null;
|
|
1634
|
+
opacity: number;
|
|
1635
|
+
order: number;
|
|
1636
|
+
overrideVersion: string | null;
|
|
1637
|
+
provenance: AssetProvenance | null;
|
|
1638
|
+
fallback: VisualLayerFallbackPolicy;
|
|
1639
|
+
transition: VisualLayerTransitionInspect | null;
|
|
1640
|
+
};
|
|
1641
|
+
type VisualLayerControllerSnapshot = {
|
|
1642
|
+
schemaVersion: typeof VISUAL_LAYER_SNAPSHOT_SCHEMA_VERSION;
|
|
1643
|
+
frame: number;
|
|
1644
|
+
sceneId: string | null;
|
|
1645
|
+
reducedMotion: boolean;
|
|
1646
|
+
layers: VisualLayerSnapshotRow[];
|
|
1647
|
+
assets: Record<string, VisualLayerAssetStatus>;
|
|
1648
|
+
events: VisualLayerEvent[];
|
|
1649
|
+
diagnostics: VisualLayerDiagnostic[];
|
|
1650
|
+
};
|
|
1651
|
+
type PlayVisualLayerResult = PlayCueResult;
|
|
1652
|
+
type RestoreVisualLayerResult = {
|
|
1653
|
+
ok: true;
|
|
1654
|
+
snapshot: VisualLayerControllerSnapshot;
|
|
1655
|
+
} | {
|
|
1656
|
+
ok: false;
|
|
1657
|
+
errors: VisualLayerDiagnostic[];
|
|
1658
|
+
};
|
|
1659
|
+
type CreateVisualLayerControllerOptions = {
|
|
1660
|
+
compositor: Compositor;
|
|
1661
|
+
layers: readonly VisualLayerDeclaration[];
|
|
1662
|
+
preloader?: AssetPreloader;
|
|
1663
|
+
originFrame?: number;
|
|
1664
|
+
reducedMotion?: boolean;
|
|
1665
|
+
sceneId?: string;
|
|
1666
|
+
fallback?: VisualLayerFallbackPolicy;
|
|
1667
|
+
onAccepted?: readonly VisualLayerAcceptedBinding[];
|
|
1668
|
+
dispatch?: (event: HostEvent) => void;
|
|
1669
|
+
};
|
|
1670
|
+
type VisualLayerController = {
|
|
1671
|
+
registerSource(assetId: string, source: ImageData | HTMLCanvasElement): void;
|
|
1672
|
+
handleHostEvent(event: HostEvent): void;
|
|
1673
|
+
override(layerId: string, version: string | null): void;
|
|
1674
|
+
play(spec: VisualLayerCueSpec): PlayVisualLayerResult;
|
|
1675
|
+
step(frames?: number): VisualLayerEvent[];
|
|
1676
|
+
snapshot(): VisualLayerControllerSnapshot;
|
|
1677
|
+
restore(input: unknown): RestoreVisualLayerResult;
|
|
1678
|
+
inspect(): VisualLayerInspect[];
|
|
1679
|
+
captureComposedFrame(): ComposedFrame;
|
|
1680
|
+
destroy(): void;
|
|
1681
|
+
readonly frame: number;
|
|
1682
|
+
readonly sceneId: string | null;
|
|
1683
|
+
readonly compositor: Compositor;
|
|
1684
|
+
};
|
|
1685
|
+
type VisualLayerCapture = {
|
|
1686
|
+
frame: ComposedFrame;
|
|
1687
|
+
layers: VisualLayerInspect[];
|
|
1688
|
+
};
|
|
1689
|
+
declare function captureVisualLayers(controller: VisualLayerController): VisualLayerCapture;
|
|
1690
|
+
declare function createVisualLayerController(options: CreateVisualLayerControllerOptions): VisualLayerController;
|
|
1691
|
+
|
|
1497
1692
|
/**
|
|
1498
1693
|
* Copyright (c) 2026 Aaron Boyarsky
|
|
1499
1694
|
* SPDX-License-Identifier: LicenseRef-CyberArt-Engine
|
|
@@ -1501,7 +1696,8 @@ declare function createHeadlessAudioAdapter(options?: {
|
|
|
1501
1696
|
*
|
|
1502
1697
|
* Node/jsdom test helpers. Import from `@cyberart-io/engine/headless`.
|
|
1503
1698
|
* Production carts and browser hosts must import `@cyberart-io/engine` instead
|
|
1504
|
-
* so Vite never walks `node:fs/promises`.
|
|
1699
|
+
* so Vite never walks `node:fs/promises`. `createHeadlessMultiCartHarness`
|
|
1700
|
+
* is the same group API as `createRuntimeGroup`, including calculation carts.
|
|
1505
1701
|
*/
|
|
1506
1702
|
|
|
1507
1703
|
type WriteComposedFrameResult = ComposedFrame & {
|
|
@@ -1513,4 +1709,4 @@ type WriteComposedFrameResult = ComposedFrame & {
|
|
|
1513
1709
|
*/
|
|
1514
1710
|
declare function writeComposedFrame(compositor: Compositor, path?: string): Promise<WriteComposedFrameResult>;
|
|
1515
1711
|
|
|
1516
|
-
export { type BoundReplaySession, type CausationTreeNode, type CreateHeadlessHarnessOptions, type CreateHeadlessMultiCartHarnessOptions, type CreateReplayInspectorOptions, DEFAULT_HEADLESS_HEIGHT, DEFAULT_HEADLESS_WIDTH, type GlyphAtlas, HEADLESS_PNG_DATA_URL, type HeadlessAudioAdapter, type HeadlessCanvas2DSettings, type HeadlessFrameError, type HeadlessHarness, type HeadlessImageFixture, type HeadlessInspect, type HeadlessMultiCartHarness, HeadlessUnsupportedOperationError, type InspectorRecord, type InstallHeadlessCanvasOptions, type ReplayCompareResult, type ReplayInspector, type ReplayInspectorExport, type ReplayInspectorReport, type ReplayParticipantSummary, type ReplayTapeAction, type ReplayTraceFilter, UPDATE_GOLDEN_ENV, type VisualArtifactPaths, type VisualArtifacts, type VisualCompareOptions, type VisualCompareResult, type WriteComposedFrameResult, assertPixelsEqual, assertPngDataUrlsEqual, attachHeadlessCanvas2D, compareImageData, compareReplayTraces, createDefaultGlyphAtlas, createHeadlessAudioAdapter, createHeadlessHarness, createHeadlessMultiCartHarness, createImageFixture, createReplayInspector, decodePng, encodePng, encodePngDataUrl, getHeadlessSurface, imageDataFromPngDataUrl, installHeadlessCanvas, makeImageData, replayExportedTrace, setDefaultGlyphAtlas, shouldUpdateGolden, writeComposedFrame, writeVisualArtifacts };
|
|
1712
|
+
export { type BoundReplaySession, type CausationTreeNode, type CreateHeadlessHarnessOptions, type CreateHeadlessMultiCartHarnessOptions, type CreateReplayInspectorOptions, DEFAULT_HEADLESS_HEIGHT, DEFAULT_HEADLESS_WIDTH, type GlyphAtlas, HEADLESS_PNG_DATA_URL, type HeadlessAudioAdapter, type HeadlessCanvas2DSettings, type HeadlessFrameError, type HeadlessHarness, type HeadlessImageFixture, type HeadlessInspect, type HeadlessMultiCartHarness, HeadlessUnsupportedOperationError, type InspectorRecord, type InstallHeadlessCanvasOptions, type ReplayCompareResult, type ReplayInspector, type ReplayInspectorExport, type ReplayInspectorReport, type ReplayParticipantSummary, type ReplayTapeAction, type ReplayTraceFilter, UPDATE_GOLDEN_ENV, type VisualArtifactPaths, type VisualArtifacts, type VisualCompareOptions, type VisualCompareResult, type VisualLayerCapture, type VisualLayerController, type VisualLayerInspect, type WriteComposedFrameResult, assertPixelsEqual, assertPngDataUrlsEqual, attachHeadlessCanvas2D, captureVisualLayers, compareImageData, compareReplayTraces, createDefaultGlyphAtlas, createHeadlessAudioAdapter, createHeadlessHarness, createHeadlessMultiCartHarness, createImageFixture, createReplayInspector, createVisualLayerController, decodePng, encodePng, encodePngDataUrl, getHeadlessSurface, imageDataFromPngDataUrl, installHeadlessCanvas, makeImageData, replayExportedTrace, setDefaultGlyphAtlas, shouldUpdateGolden, writeComposedFrame, writeVisualArtifacts };
|