@cyberart-io/engine 0.0.1 → 0.0.3

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.
@@ -0,0 +1,82 @@
1
+ # Presentation cue / timeline
2
+
3
+ Deterministic cue/effect primitive. Carts call `step(frames)` with the same clock they use for CYB-23; there are no `setTimeout` / rAF timers. Gold checkmarks, ripples, room transitions, weather pulses, and NPC beats should share this lifecycle instead of ad-hoc frame counters.
4
+
5
+ Back to the [package README](../README.md). Related: [deterministic mode](deterministic-mode.md), [events](events.md) (routed contracts vs local `cue.*` names).
6
+
7
+ ## One-command reproduce (this repo)
8
+
9
+ ```bash
10
+ pnpm exec vitest run packages/engine/src/canvas/cyb-58-presentation-cue.repro.spec.ts
11
+ ```
12
+
13
+ ## `createPresentationTimeline(options?)`
14
+
15
+ ```ts
16
+ import {
17
+ createPresentationTimeline,
18
+ CUE_STARTED_EVENT,
19
+ CUE_COMPLETED_EVENT,
20
+ CUE_CANCELLED_EVENT,
21
+ CUE_REPLACED_EVENT,
22
+ } from '@cyberart-io/engine';
23
+
24
+ const timeline = createPresentationTimeline({
25
+ originFrame: 0,
26
+ reducedMotion: false, // host flag, not a CSS media query
27
+ });
28
+
29
+ timeline.play({
30
+ name: 'checkmark',
31
+ idempotencyKey: 'gold',
32
+ durationFrames: 90,
33
+ delayFrames: 0,
34
+ easing: 'ease-out', // or 'linear'
35
+ repeat: { count: 0 },
36
+ reducedMotion: 'complete', // 'skip' | 'complete' | { durationFrames: n }
37
+ onDuplicate: 'replace', // 'ignore' | 'replace' | 'reject'
38
+ });
39
+
40
+ timeline.step(90);
41
+ ```
42
+
43
+ | Member | Meaning |
44
+ |---|---|
45
+ | `play(spec)` | Schedule or start a cue. `{ ok: true, cue }` or `{ ok: false, reason: 'duplicate' \| 'invalid' }`. |
46
+ | `step(frames?)` | Advance the frame index (default 1). Returns lifecycle events emitted during those frames. |
47
+ | `cancel(key)` | Cancel an active/scheduled cue (`cue.cancelled`). |
48
+ | `reset()` | Cancel remaining cues, drop dedupe, rewind to `originFrame`, clear the event log. |
49
+ | `snapshot()` | JSON-serializable `{ frame, reducedMotion, cues, events }`. |
50
+ | `get(key)` | Copy of the live cue, or `undefined`. |
51
+
52
+ `startFrame` defaults to the play frame. If `play` happens after `startFrame + delayFrames`, the timeline catches up on that call: mid-cue progress is applied, and a cue whose duration is already over completes immediately (`cue.started` then `cue.completed`). Progress is `0…1` after easing. `step` ignores non-finite counts (no infinite loop). Two identical `play` / `step` tapes produce identical snapshots.
53
+
54
+ ## Duplicate policy
55
+
56
+ Keyed by `idempotencyKey` among scheduled and active cues.
57
+
58
+ | `onDuplicate` | Effect |
59
+ |---|---|
60
+ | `replace` (default) | Emit `cue.replaced` for the old cue, start the new spec. |
61
+ | `ignore` | Keep the existing cue. |
62
+ | `reject` | `{ ok: false, reason: 'duplicate' }`. Unrelated cues keep running. |
63
+
64
+ ## Reduced motion
65
+
66
+ Pass `reducedMotion: true` on the timeline (host-owned). Per-cue `reducedMotion`:
67
+
68
+ - `complete` / `skip` — duration 0; `cue.started` then `cue.completed` at the play frame
69
+ - `{ durationFrames: n }` — use `n` instead of the authored duration
70
+
71
+ ## Lifecycle events (stable names)
72
+
73
+ | `type` | When |
74
+ |---|---|
75
+ | `cue.started` | Cue becomes active |
76
+ | `cue.completed` | Duration elapsed (or reduced-motion complete) |
77
+ | `cue.cancelled` | `cancel` or `reset` |
78
+ | `cue.replaced` | Duplicate with `replace` |
79
+
80
+ Each event: `{ type, atFrame, name, idempotencyKey, progress }`. Repeat `{ count: n }` restarts immediately after complete (`repeatIndex` increments). `{ forever: true }` with `durationFrames: 0` completes once (no infinite loop).
81
+
82
+ Do not `await` wall-clock time inside cart `update` / `render`. Drive the timeline from `cart.step` / `getClock().framesElapsed`.
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@cyberart-io/engine",
3
- "version": "0.0.1",
4
- "description": "Embeddable CyberArt host engine: mount a cart, pause, snapshot, and save/load state.",
3
+ "version": "0.0.3",
4
+ "description": "CyberArt host engine: mount a cart, assets, events, deterministic cues, and a Node/jsdom headless entry.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "files": [
8
8
  "dist/index.js",
9
- "dist/index.d.ts"
9
+ "dist/index.d.ts",
10
+ "dist/headless.js",
11
+ "dist/headless.d.ts",
12
+ "docs"
10
13
  ],
11
14
  "main": "./dist/index.js",
12
15
  "module": "./dist/index.js",
@@ -15,6 +18,15 @@
15
18
  ".": {
16
19
  "types": "./dist/index.d.ts",
17
20
  "import": "./dist/index.js"
21
+ },
22
+ "./headless": {
23
+ "types": "./dist/headless.d.ts",
24
+ "import": "./dist/headless.js"
25
+ }
26
+ },
27
+ "typesVersions": {
28
+ "*": {
29
+ "headless": ["./dist/headless.d.ts"]
18
30
  }
19
31
  },
20
32
  "sideEffects": false,